diff options
author | Renato Botelho <renato@netgate.com> | 2015-08-26 15:12:02 -0300 |
---|---|---|
committer | Renato Botelho <renato@netgate.com> | 2015-08-26 15:12:02 -0300 |
commit | 03b19a93f4d8d870507ee96121cee4acd748dd2a (patch) | |
tree | 71a34e9e7e73d13de21cb4ad831799fb10c30df4 /src/usr/local/www | |
parent | 7f410a121522c5d0e2660256ae50c1fde1df3645 (diff) | |
parent | 30ce58ac1ea27b758d5112cb5a3b190c9760f010 (diff) | |
download | pfsense-03b19a93f4d8d870507ee96121cee4acd748dd2a.zip pfsense-03b19a93f4d8d870507ee96121cee4acd748dd2a.tar.gz |
Merge branch 'master' into bootstrap
Diffstat (limited to 'src/usr/local/www')
354 files changed, 105521 insertions, 0 deletions
diff --git a/src/usr/local/www/apple-touch-icon.png b/src/usr/local/www/apple-touch-icon.png Binary files differnew file mode 100755 index 0000000..7a4b975 --- /dev/null +++ b/src/usr/local/www/apple-touch-icon.png diff --git a/src/usr/local/www/bandwidth_by_ip.php b/src/usr/local/www/bandwidth_by_ip.php new file mode 100755 index 0000000..9f8cc6c --- /dev/null +++ b/src/usr/local/www/bandwidth_by_ip.php @@ -0,0 +1,152 @@ +<?php +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + * + */ + +/* + pfSense_BUILDER_BINARIES: /usr/local/bin/rate + pfSense_MODULE: trafficgraph +*/ + +require_once('guiconfig.inc'); +require_once('interfaces.inc'); +require_once('pfsense-utils.inc'); +require_once('util.inc'); + +$listedIPs = ""; + +//get interface IP and break up into an array +$interface = $_GET['if']; +$real_interface = get_real_interface($interface); +if (!does_interface_exist($real_interface)) { + echo gettext("Wrong Interface"); + return; +} + +$intip = find_interface_ip($real_interface); +//get interface subnet +$netmask = find_interface_subnet($real_interface); +$intsubnet = gen_subnet($intip, $netmask) . "/$netmask"; + +// see if they want local, remote or all IPs returned +$filter = $_GET['filter']; + +if ($filter == "") { + $filter = "local"; +} + +if ($filter == "local") { + $ratesubnet = "-c " . $intsubnet; +} else { + // Tell the rate utility to consider the whole internet (0.0.0.0/0) + // and to consider local "l" traffic - i.e. traffic within the whole internet + // then we can filter the resulting output as we wish below. + $ratesubnet = "-lc 0.0.0.0/0"; +} + +//get the sort method +$sort = $_GET['sort']; +if ($sort == "out") { + $sort_method = "-T"; +} else { + $sort_method = "-R"; +} + +// get the desired format for displaying the host name or IP +$hostipformat = $_GET['hostipformat']; +$iplookup = array(); +// If hostname, description or FQDN is requested then load the locally-known IP address - host/description mappings into an array keyed by IP address. +if ($hostipformat != "") { + if (is_array($config['dhcpd'])) { + // Build an array of static-mapped DHCP entries keyed by IP address. + foreach ($config['dhcpd'] as $ifdata) { + if (is_array($ifdata['staticmap'])) { + foreach ($ifdata['staticmap'] as $hostent) { + if (($hostent['ipaddr'] != "") && ($hostent['hostname'] != "")) { + if ($hostipformat == "descr" && $hostent['descr'] != "") { + $iplookup[$hostent['ipaddr']] = $hostent['descr']; + } else { + $iplookup[$hostent['ipaddr']] = $hostent['hostname']; + if ($hostipformat == "fqdn") { + $iplookup[$hostent['ipaddr']] .= "." . $config['system']['domain']; + } + } + } + } + } + } + } + // Add any DNS host override data keyed by IP address. + foreach (array('dnsmasq', 'unbound') as $dns_type) { + if (isset($config[$dns_type]['enable'])) { + if (is_array($config[$dns_type]['hosts'])) { + foreach ($config[$dns_type]['hosts'] as $hostent) { + if (($hostent['ip'] != "") && ($hostent['host'] != "")) { + if ($hostipformat == "descr" && $hostent['descr'] != "") { + $iplookup[$hostent['ip']] = $hostent['descr']; + } else { + $iplookup[$hostent['ip']] = $hostent['host']; + if ($hostipformat == "fqdn") { + $iplookup[$hostent['ip']] .= "." . $hostent['domain']; + } + } + } + } + } + } + } +} + +$_grb = exec("/usr/local/bin/rate -i {$real_interface} -nlq 1 -Aba 20 {$sort_method} {$ratesubnet} | tr \"|\" \" \" | awk '{ printf \"%s:%s:%s:%s:%s\\n\", $1, $2, $4, $6, $8 }'", $listedIPs); + +$someinfo = false; +for ($x=2; $x<12; $x++) { + + $bandwidthinfo = $listedIPs[$x]; + + // echo $bandwidthinfo; + $emptyinfocounter = 1; + if ($bandwidthinfo != "") { + $infoarray = explode (":", $bandwidthinfo); + if (($filter == "all") || + (($filter == "local") && (ip_in_subnet($infoarray[0], $intsubnet))) || + (($filter == "remote") && (!ip_in_subnet($infoarray[0], $intsubnet)))) { + if ($hostipformat == "") { + // pass back just the raw IP address + $addrdata = $infoarray[0]; + } else { + // $hostipformat is one of "hostname", "descr" or "fqdn" - we want a text representation if we can get it. + if ($iplookup[$infoarray[0]] != "") { + // We have a local entry, so use it. + $addrdata = $iplookup[$infoarray[0]]; + } else { + // Try to reverse lookup the IP address. + $addrdata = gethostbyaddr($infoarray[0]); + if ($addrdata != $infoarray[0]) { + // Reverse lookup returned something other than the IP address (FQDN, we hope!) + if ($hostipformat != "fqdn") { + // The user does not want the whole FQDN, so only pass back the first part of the name. + $name_array = explode(".", $addrdata); + $addrdata = $name_array[0]; + } + } + } + } + //print host information; + echo $addrdata . ";" . $infoarray[1] . ";" . $infoarray[2] . "|"; + + //mark that we collected information + $someinfo = true; + } + } +} +unset($bandwidthinfo, $_grb); +unset($listedIPs); + +//no bandwidth usage found +if ($someinfo == false) + echo gettext("no info"); + +?>
\ No newline at end of file diff --git a/src/usr/local/www/bootstrap/css/bootstrap-theme.min.css b/src/usr/local/www/bootstrap/css/bootstrap-theme.min.css new file mode 100644 index 0000000..cefa3d1 --- /dev/null +++ b/src/usr/local/www/bootstrap/css/bootstrap-theme.min.css @@ -0,0 +1,5 @@ +/*! + * Bootstrap v3.3.4 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default:disabled,.btn-default[disabled]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary:disabled,.btn-primary[disabled]{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success:disabled,.btn-success[disabled]{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info:disabled,.btn-info[disabled]{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning:disabled,.btn-warning[disabled]{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger:disabled,.btn-danger[disabled]{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}
\ No newline at end of file diff --git a/src/usr/local/www/bootstrap/css/bootstrap.min.css b/src/usr/local/www/bootstrap/css/bootstrap.min.css new file mode 100644 index 0000000..cd1c616 --- /dev/null +++ b/src/usr/local/www/bootstrap/css/bootstrap.min.css @@ -0,0 +1,5 @@ +/*! + * Bootstrap v3.3.4 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date],input[type=time],input[type=datetime-local],input[type=month]{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px \9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.form-group-sm .form-control{height:30px;line-height:30px}select[multiple].form-group-sm .form-control,textarea.form-group-sm .form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:5px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.form-group-lg .form-control{height:46px;line-height:46px}select[multiple].form-group-lg .form-control,textarea.form-group-lg .form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:10px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.active,.btn-default.focus,.btn-default:active,.btn-default:focus,.btn-default:hover,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.active,.btn-primary.focus,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.active,.btn-success.focus,.btn-success:active,.btn-success:focus,.btn-success:hover,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.active,.btn-info.focus,.btn-info:active,.btn-info:focus,.btn-info:hover,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.active,.btn-warning.focus,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.active,.btn-danger.focus,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px)and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px 15px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding:48px 0}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-weight:400;line-height:1.4;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-weight:400;line-height:1.42857143;text-align:left;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;perspective:1000}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;margin-top:-10px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px)and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px)and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px)and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px)and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px)and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px)and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px)and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px)and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px)and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px)and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
\ No newline at end of file diff --git a/src/usr/local/www/bootstrap/css/pfSense.css b/src/usr/local/www/bootstrap/css/pfSense.css new file mode 100644 index 0000000..1384543 --- /dev/null +++ b/src/usr/local/www/bootstrap/css/pfSense.css @@ -0,0 +1,150 @@ +@import url("/bootstrap/css/bootstrap.min.css"); +@import url("/bootstrap/css/bootstrap-theme.min.css"); +@import url("/bootstrap/glyphicons/glyphicons-halflings.css"); + +html { + position: relative; + min-height: 100%; +} + +body { + padding-top: 0px; + margin-bottom: 80px; +} + +.navbar-brand { + padding-top: 0; +} + +body.no-menu #jumbotron { + padding: 100px 0; + background-image: linear-gradient(to bottom,#3c3c3c 0,#222 100%) +} + +body.no-menu { + background: url("/logo-black.png") no-repeat center 30px; + padding-top: 70px; +} + +body.no-menu #jumbotron { + margin-top: 75px; +} + +body#index .icons { + float: right; +} + +.ui-sortable-handle { + cursor: move; +} + +tr.disabled td, +tr.disabled th { + opacity: .5; +} + +.contains-table table { + border: 1px solid #ddd; +} + +.nav-pills { + margin-bottom: 20px; +} + +/** Content structure */ +.table-responsive { + clear: both; + margin-bottom: 0px; +} + +.form-horizontal { + margin-bottom: 20px; + overflow: hidden; +} + +.action-buttons { + text-align: right; + margin-bottom: 20px; +} + +/** Form validation */ +.input-errors ul { + margin-top: 20px; +} + +/** Page header with title and breadcrumb */ +.header { + position: relative; +} + +.header .page-header { + margin-top: 0; +} + +.header .context-links { + position: absolute; + right: 0; + top: 15px; +} + +.header .context-links li { + float: left; + display: inline; + margin-left: 10px; +} + +/** Form tweaks */ +form .btn + .btn { + margin-left: 5px; +} + +.input-group-inbetween { + border-left: 0; +} + +.user-duplication .controls { + margin-top: 10px; +} + +.checkbox.multi label { + display: block; +} + +.checkbox.multi .btn { + margin-top: 5px; +} + +.col-sm-10 .form-control { + width: calc(50% - 15px); /* substract 15px to match a .col-sm-5; which satisfies my OCD */ +} + +@media (max-width: 991px) { + .col-sm-10 .form-control { + width: 100%; + } +} + +/** Page footer */ +.footer { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 60px; + background-color: #f5f5f5; + padding-top: 21px; + text-align: center; +} + +/** Homepage / dashboard */ +#widgetSequence { + position: fixed; + left: 0; + bottom: 0; + padding: 20px 0; + width: 100%; + z-index: 100; + text-align: center; + background: rgba(255, 255, 255, 0.6); + border-top: 1px solid #ddd; +}
\ No newline at end of file diff --git a/src/usr/local/www/bootstrap/glyphicons/glyphicons-halflings.css b/src/usr/local/www/bootstrap/glyphicons/glyphicons-halflings.css new file mode 100644 index 0000000..3794571 --- /dev/null +++ b/src/usr/local/www/bootstrap/glyphicons/glyphicons-halflings.css @@ -0,0 +1,582 @@ +/* +manual-cp-man strikes again + +http://getbootstrap.com/2.3.2/assets/css/bootstrap.css +*/ +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + margin-top: 1px; + *margin-right: .3em; + line-height: 14px; + vertical-align: text-top; + background-image: url("/bootstrap/glyphicons/glyphicons-halflings.png"); + background-position: 14px 14px; + background-repeat: no-repeat; +} + +.icon-glass { + background-position: 0 0; +} + +.icon-music { + background-position: -24px 0; +} + +.icon-search { + background-position: -48px 0; +} + +.icon-envelope { + background-position: -72px 0; +} + +.icon-heart { + background-position: -96px 0; +} + +.icon-star { + background-position: -120px 0; +} + +.icon-star-empty { + background-position: -144px 0; +} + +.icon-user { + background-position: -168px 0; +} + +.icon-film { + background-position: -192px 0; +} + +.icon-th-large { + background-position: -216px 0; +} + +.icon-th { + background-position: -240px 0; +} + +.icon-th-list { + background-position: -264px 0; +} + +.icon-ok { + background-position: -288px 0; +} + +.icon-remove { + background-position: -312px 0; +} + +.icon-zoom-in { + background-position: -336px 0; +} + +.icon-zoom-out { + background-position: -360px 0; +} + +.icon-off { + background-position: -384px 0; +} + +.icon-signal { + background-position: -408px 0; +} + +.icon-cog { + background-position: -432px 0; +} + +.icon-trash { + background-position: -456px 0; +} + +.icon-home { + background-position: 0 -24px; +} + +.icon-file { + background-position: -24px -24px; +} + +.icon-time { + background-position: -48px -24px; +} + +.icon-road { + background-position: -72px -24px; +} + +.icon-download-alt { + background-position: -96px -24px; +} + +.icon-download { + background-position: -120px -24px; +} + +.icon-upload { + background-position: -144px -24px; +} + +.icon-inbox { + background-position: -168px -24px; +} + +.icon-play-circle { + background-position: -192px -24px; +} + +.icon-repeat { + background-position: -216px -24px; +} + +.icon-refresh { + background-position: -240px -24px; +} + +.icon-list-alt { + background-position: -264px -24px; +} + +.icon-lock { + background-position: -287px -24px; +} + +.icon-flag { + background-position: -312px -24px; +} + +.icon-headphones { + background-position: -336px -24px; +} + +.icon-volume-off { + background-position: -360px -24px; +} + +.icon-volume-down { + background-position: -384px -24px; +} + +.icon-volume-up { + background-position: -408px -24px; +} + +.icon-qrcode { + background-position: -432px -24px; +} + +.icon-barcode { + background-position: -456px -24px; +} + +.icon-tag { + background-position: 0 -48px; +} + +.icon-tags { + background-position: -25px -48px; +} + +.icon-book { + background-position: -48px -48px; +} + +.icon-bookmark { + background-position: -72px -48px; +} + +.icon-print { + background-position: -96px -48px; +} + +.icon-camera { + background-position: -120px -48px; +} + +.icon-font { + background-position: -144px -48px; +} + +.icon-bold { + background-position: -167px -48px; +} + +.icon-italic { + background-position: -192px -48px; +} + +.icon-text-height { + background-position: -216px -48px; +} + +.icon-text-width { + background-position: -240px -48px; +} + +.icon-align-left { + background-position: -264px -48px; +} + +.icon-align-center { + background-position: -288px -48px; +} + +.icon-align-right { + background-position: -312px -48px; +} + +.icon-align-justify { + background-position: -336px -48px; +} + +.icon-list { + background-position: -360px -48px; +} + +.icon-indent-left { + background-position: -384px -48px; +} + +.icon-indent-right { + background-position: -408px -48px; +} + +.icon-facetime-video { + background-position: -432px -48px; +} + +.icon-picture { + background-position: -456px -48px; +} + +.icon-pencil { + background-position: 0 -72px; +} + +.icon-map-marker { + background-position: -24px -72px; +} + +.icon-adjust { + background-position: -48px -72px; +} + +.icon-tint { + background-position: -72px -72px; +} + +.icon-edit { + background-position: -96px -72px; +} + +.icon-share { + background-position: -120px -72px; +} + +.icon-check { + background-position: -144px -72px; +} + +.icon-move { + background-position: -168px -72px; +} + +.icon-step-backward { + background-position: -192px -72px; +} + +.icon-fast-backward { + background-position: -216px -72px; +} + +.icon-backward { + background-position: -240px -72px; +} + +.icon-play { + background-position: -264px -72px; +} + +.icon-pause { + background-position: -288px -72px; +} + +.icon-stop { + background-position: -312px -72px; +} + +.icon-forward { + background-position: -336px -72px; +} + +.icon-fast-forward { + background-position: -360px -72px; +} + +.icon-step-forward { + background-position: -384px -72px; +} + +.icon-eject { + background-position: -408px -72px; +} + +.icon-chevron-left { + background-position: -432px -72px; +} + +.icon-chevron-right { + background-position: -456px -72px; +} + +.icon-plus-sign { + background-position: 0 -96px; +} + +.icon-minus-sign { + background-position: -24px -96px; +} + +.icon-remove-sign { + background-position: -48px -96px; +} + +.icon-ok-sign { + background-position: -72px -96px; +} + +.icon-question-sign { + background-position: -96px -96px; +} + +.icon-info-sign { + background-position: -120px -96px; +} + +.icon-screenshot { + background-position: -144px -96px; +} + +.icon-remove-circle { + background-position: -168px -96px; +} + +.icon-ok-circle { + background-position: -192px -96px; +} + +.icon-ban-circle { + background-position: -216px -96px; +} + +.icon-arrow-left { + background-position: -240px -96px; +} + +.icon-arrow-right { + background-position: -264px -96px; +} + +.icon-arrow-up { + background-position: -289px -96px; +} + +.icon-arrow-down { + background-position: -312px -96px; +} + +.icon-share-alt { + background-position: -336px -96px; +} + +.icon-resize-full { + background-position: -360px -96px; +} + +.icon-resize-small { + background-position: -384px -96px; +} + +.icon-plus { + background-position: -408px -96px; +} + +.icon-minus { + background-position: -433px -96px; +} + +.icon-asterisk { + background-position: -456px -96px; +} + +.icon-exclamation-sign { + background-position: 0 -120px; +} + +.icon-gift { + background-position: -24px -120px; +} + +.icon-leaf { + background-position: -48px -120px; +} + +.icon-fire { + background-position: -72px -120px; +} + +.icon-eye-open { + background-position: -96px -120px; +} + +.icon-eye-close { + background-position: -120px -120px; +} + +.icon-warning-sign { + background-position: -144px -120px; +} + +.icon-plane { + background-position: -168px -120px; +} + +.icon-calendar { + background-position: -192px -120px; +} + +.icon-random { + width: 16px; + background-position: -216px -120px; +} + +.icon-comment { + background-position: -240px -120px; +} + +.icon-magnet { + background-position: -264px -120px; +} + +.icon-chevron-up { + background-position: -288px -120px; +} + +.icon-chevron-down { + background-position: -313px -119px; +} + +.icon-retweet { + background-position: -336px -120px; +} + +.icon-shopping-cart { + background-position: -360px -120px; +} + +.icon-folder-close { + width: 16px; + background-position: -384px -120px; +} + +.icon-folder-open { + width: 16px; + background-position: -408px -120px; +} + +.icon-resize-vertical { + background-position: -432px -119px; +} + +.icon-resize-horizontal { + background-position: -456px -118px; +} + +.icon-hdd { + background-position: 0 -144px; +} + +.icon-bullhorn { + background-position: -24px -144px; +} + +.icon-bell { + background-position: -48px -144px; +} + +.icon-certificate { + background-position: -72px -144px; +} + +.icon-thumbs-up { + background-position: -96px -144px; +} + +.icon-thumbs-down { + background-position: -120px -144px; +} + +.icon-hand-right { + background-position: -144px -144px; +} + +.icon-hand-left { + background-position: -168px -144px; +} + +.icon-hand-up { + background-position: -192px -144px; +} + +.icon-hand-down { + background-position: -216px -144px; +} + +.icon-circle-arrow-right { + background-position: -240px -144px; +} + +.icon-circle-arrow-left { + background-position: -264px -144px; +} + +.icon-circle-arrow-up { + background-position: -288px -144px; +} + +.icon-circle-arrow-down { + background-position: -312px -144px; +} + +.icon-globe { + background-position: -336px -144px; +} + +.icon-wrench { + background-position: -360px -144px; +} + +.icon-tasks { + background-position: -384px -144px; +} + +.icon-filter { + background-position: -408px -144px; +} + +.icon-briefcase { + background-position: -432px -144px; +} + +.icon-fullscreen { + background-position: -456px -144px; +} + diff --git a/src/usr/local/www/bootstrap/glyphicons/glyphicons-halflings.png b/src/usr/local/www/bootstrap/glyphicons/glyphicons-halflings.png Binary files differnew file mode 100644 index 0000000..a996999 --- /dev/null +++ b/src/usr/local/www/bootstrap/glyphicons/glyphicons-halflings.png diff --git a/src/usr/local/www/bootstrap/js/bootstrap.min.js b/src/usr/local/www/bootstrap/js/bootstrap.min.js new file mode 100644 index 0000000..c8f82e5 --- /dev/null +++ b/src/usr/local/www/bootstrap/js/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.4 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.4",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.4",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.4",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.4",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.4",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('<div class="dropdown-backdrop"/>').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(b){if(/(38|40|27|32)/.test(b.which)&&!/input|textarea/i.test(b.target.tagName)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g&&27!=b.which||g&&27==b.which)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(b.target);38==b.which&&j>0&&j--,40==b.which&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",b).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f,g.prototype.keydown).on("keydown.bs.dropdown.data-api",'[role="menu"]',g.prototype.keydown).on("keydown.bs.dropdown.data-api",'[role="listbox"]',g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.3.4",c.TRANSITION_DURATION=300,c.BACKDROP_TRANSITION_DURATION=150,c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var d=this,e=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){d.$element.one("mouseup.dismiss.bs.modal",function(b){a(b.target).is(d.$element)&&(d.ignoreBackdropClick=!0)})}),this.backdrop(function(){var e=a.support.transition&&d.$element.hasClass("fade");d.$element.parent().length||d.$element.appendTo(d.$body),d.$element.show().scrollTop(0),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in").attr("aria-hidden",!1),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$dialog.one("bsTransitionEnd",function(){d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION):d.$element.trigger("focus").trigger(f)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").attr("aria-hidden",!0).off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(c.TRANSITION_DURATION):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},c.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$body.removeClass("modal-open"),a.resetAdjustments(),a.resetScrollbar(),a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var d=this,e=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var f=a.support.transition&&e;if(this.$backdrop=a('<div class="modal-backdrop '+e+'" />').appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.adjustDialog()},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth<a,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.init("tooltip",a,b)};c.VERSION="3.3.4",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport),this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c&&c.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-m<p.top?"bottom":"right"==h&&k.right+l>p.width?"left":"left"==h&&k.left-l<p.left?"right":h,f.removeClass(n).addClass(h)}var q=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(q,h);var r=function(){var a=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==a&&e.leave(e)};a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",r).emulateTransitionEnd(c.TRANSITION_DURATION):r()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top=b.top+g,b.left=b.left+h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=/top|bottom/.test(c),m=l?2*k.left-e+i:2*k.top-f+j,n=l?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(m,d[0][n],l)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c?"left":"top",50*(1-a/b)+"%").css(c?"top":"left","")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(b){function d(){"in"!=e.hoverState&&f.detach(),e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),b&&b()}var e=this,f=a(this.$tip),g=a.Event("hide.bs."+this.type);return this.$element.trigger(g),g.isDefaultPrevented()?void 0:(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",d).emulateTransitionEnd(c.TRANSITION_DURATION):d(),this.hoverState=null,this)},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName,e=c.getBoundingClientRect();null==e.width&&(e=a.extend({},e,{width:e.right-e.left,height:e.bottom-e.top}));var f=d?{top:0,left:0}:b.offset(),g={scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop()},h=d?{width:a(window).width(),height:a(window).height()}:null;return a.extend({},e,g,h,f)},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type)})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.4",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.4",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<e[0])return this.activeTarget=null,this.clear();for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(void 0===e[a+1]||b<e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.4",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){ +var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.4",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=a(document.body).height();"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
\ No newline at end of file diff --git a/src/usr/local/www/carp_status.php b/src/usr/local/www/carp_status.php new file mode 100644 index 0000000..6ba07fa --- /dev/null +++ b/src/usr/local/www/carp_status.php @@ -0,0 +1,244 @@ +<?php +/* $Id$ */ +/* + carp_status.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +##|+PRIV +##|*IDENT=page-status-carp +##|*NAME=Status: CARP page +##|*DESCR=Allow access to the 'Status: CARP' page. +##|*MATCH=carp_status.php* +##|-PRIV + +/* + pfSense_MODULE: carp +*/ + +require_once("guiconfig.inc"); +require_once("globals.inc"); + +function gentitle_pkg($pgname) { + global $config; + return $config['system']['hostname'] . "." . $config['system']['domain'] . " - " . $pgname; +} + +unset($interface_arr_cache); +unset($carp_interface_count_cache); +unset($interface_ip_arr_cache); + +$status = get_carp_status(); +$status = intval($status); +if ($_POST['carp_maintenancemode'] <> "") { + interfaces_carp_set_maintenancemode(!isset($config["virtualip_carp_maintenancemode"])); +} +if ($_POST['disablecarp'] <> "") { + if ($status > 0) { + set_single_sysctl('net.inet.carp.allow', '0'); + if (is_array($config['virtualip']['vip'])) { + $viparr = &$config['virtualip']['vip']; + $found_dhcpdv6 = false; + foreach ($viparr as $vip) { + $carp_iface = "{$vip['interface']}_vip{$vip['vhid']}"; + switch ($vip['mode']) { + case "carp": + interface_vip_bring_down($vip); + interface_ipalias_cleanup($carp_iface); + + /* + * Reconfigure radvd when necessary + * XXX: Is it the best way to do it? + */ + if (isset($config['dhcpdv6']) && is_array($config['dhcpdv6'])) { + foreach ($config['dhcpdv6'] as $dhcpv6if => $dhcpv6ifconf) { + if ($dhcpv6ifconf['rainterface'] != $carp_iface) { + continue; + } + + services_radvd_configure(); + break; + } + } + + sleep(1); + break; + } + } + } + $savemsg = sprintf(gettext("%s IPs have been disabled. Please note that disabling does not survive a reboot and some configuration changes will re-enable."), $carp_counter); + $status = 0; + } else { + $savemsg = gettext("CARP has been enabled."); + if (is_array($config['virtualip']['vip'])) { + $viparr = &$config['virtualip']['vip']; + foreach ($viparr as $vip) { + switch ($vip['mode']) { + case "carp": + interface_carp_configure($vip); + sleep(1); + break; + case 'ipalias': + if (strpos($vip['interface'], '_vip')) { + interface_ipalias_configure($vip); + } + break; + } + } + } + interfaces_sync_setup(); + set_single_sysctl('net.inet.carp.allow', '1'); + $status = 1; + } +} + +$carp_detected_problems = get_single_sysctl("net.inet.carp.demotion"); + +if (!empty($_POST['resetdemotion'])) { + set_single_sysctl("net.inet.carp.demotion", "-{$carp_detected_problems}"); + sleep(1); + $carp_detected_problems = get_single_sysctl("net.inet.carp.demotion"); +} + +$pgtitle = array(gettext("Status"), gettext("CARP")); +$shortcut_section = "carp"; +include("head.inc"); +?> +<form action="carp_status.php" method="post"> +<?php if ($savemsg) print_info_box($savemsg); ?> + +<?PHP if ($carp_detected_problems > 0) { + print_info_box( + gettext("CARP has detected a problem and this unit has been demoted to BACKUP status.") . "<br/>" . + gettext("Check the link status on all interfaces with configured CARP VIPs.") . "<br/>" . + gettext("Search the") . + " <a href=\"/diag_logs.php?filtertext=carp%3A+demoted+by\">" . + gettext("system log") . + "</a> " . + gettext("for CARP demotion-related events.") . "<br/>" . + "<input type=\"submit\" name=\"resetdemotion\" id=\"resetdemotion\" value=\"" . + gettext("Reset CARP Demotion Status") . + "\" />" + ); + +} ?> + +<?php +$carpcount = 0; +if(is_array($config['virtualip']['vip'])) { + foreach($config['virtualip']['vip'] as $carp) { + if ($carp['mode'] == "carp") { + $carpcount++; + break; + } + } +} +if ($carpcount > 0): + $carp_enabled = ($status > 0); +?> + <input type="submit" name="disablecarp" value="<?=($carp_enabled ? gettext("Temporarily Disable CARP") : gettext("Enable CARP"))?>" /> + <input type="submit" name="carp_maintenancemode" value="<?=($config["virtualip_carp_maintenancemode"] ? gettext("Leave Persistent CARP Maintenance Mode") : gettext("Enter Persistent CARP Maintenance Mode"))?>" /> +<?php elseif ($carpcount == 0): ?> + <div class="alert alert-info" role="alert"> + <p> + <?=gettext("Could not locate any defined CARP interfaces.")?><br/> + <a href="system_hasync.php" class="alert-link"><?=gettext("You can configure high availability sync settings here")?></a>. + </p> + </div> +<?php else: ?> +<table> + <tr> + <td><?=gettext("CARP Interface")?></td> + <td><?=gettext("Virtual IP")?></td> + <td><?=gettext("Status")?></td> + </tr> +<?php + foreach($config['virtualip']['vip'] as $carp) { + if ($carp['mode'] != "carp") + continue; + $ipaddress = $carp['subnet']; + $vhid = $carp['vhid']; + $status = get_carp_interface_status("{$carp['interface']}_vip{$carp['vhid']}"); + if($carp_enabled == false) { + $icon = 'remove-sign'; + $status = "DISABLED"; + } else { + if($status == "MASTER") { + $icon = 'ok-sign'; + } else if($status == "BACKUP") { + $icon = 'ok-circle'; + } else if($status == "INIT") { + $icon = 'question-sign'; + } + } +?> + <tr> + <td> + <td><?=convert_friendly_interface_to_friendly_descr($carp['interface'])?>@<?=$vhid?></td> + <td><?=$ipaddress?></td> + <td><i class="icon icon-<?=$icon?>">$status</i></td> + </tr> +<?php }?> +</table> +</form> +<?php endif?> + +<h4><?=gettext("pfSync nodes")?></h4> +<ul> +<?php + foreach (explode("\n", exec_command("/sbin/pfctl -vvss | /usr/bin/grep creator | /usr/bin/cut -d\" \" -f7 | /usr/bin/sort -u")) as $node) + echo '<li>'. $node .'</li>'; +?> +</ul> + +<?php include("foot.inc")?> diff --git a/src/usr/local/www/classes/Form.class.php b/src/usr/local/www/classes/Form.class.php new file mode 100644 index 0000000..b489a72 --- /dev/null +++ b/src/usr/local/www/classes/Form.class.php @@ -0,0 +1,126 @@ +<?php +/* + Form.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +require_once('classes/Form/Element.class.php'); +require_once('classes/Form/Input.class.php'); +foreach (glob('classes/Form/*.class.php') as $file) + require_once($file); + +class Form extends Form_Element +{ + const LABEL_WIDTH = 2; + const MAX_INPUT_WIDTH = 10; + protected $_tagName = 'form'; + protected $_attributes = array( + 'class' => array('form-horizontal' => true), + 'method' => 'post', + // Empty is interpreted by all browsers to submit to the current URI + 'action' => '', + ); + protected $_sections = array(); + protected $_global = array(); + + public function __construct($submit = null) + { + if (!isset($submit)) + $submit = 'Save'; + + if (gettype($submit) == 'string') + $submit = new Form_Button( + 'save', + $submit + ); + + if (false !== $submit) + $this->addGlobal($submit); + } + + public function add(Form_Section $section) + { + array_push($this->_sections, $section); + $section->_setParent($this); + + return $section; + } + + public function setAction($url) + { + $this->_attributes['action'] = $url; + + return $this; + } + + public function addGlobal(Form_Input $input) + { + array_push($this->_global, $input); + + return $input; + } + + public function setMultipartEncoding() + { + $this->_attributes['enctype'] = 'multipart/form-data'; + + return $this; + } + + protected function _setParent() + { + throw new Exception('Form does not have a parent'); + } + + public function __toString() + { + $element = parent::__toString(); + $html = implode('', $this->_sections); + $buttons = ''; + + foreach ($this->_global as $global) + { + if ($global instanceof Form_Button) + $buttons .= $global; + else + $html .= $global; + } + + if (!empty($buttons)) + { + $group = new Form_Element; + $group->addClass('col-sm-'. Form::MAX_INPUT_WIDTH, 'col-sm-offset-'. Form::LABEL_WIDTH); + + $html .= $group . $buttons .'</div>'; + } + + return <<<EOT + {$element} + {$html} + </form> +EOT; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Form/Button.class.php b/src/usr/local/www/classes/Form/Button.class.php new file mode 100644 index 0000000..32876a7 --- /dev/null +++ b/src/usr/local/www/classes/Form/Button.class.php @@ -0,0 +1,68 @@ +<?php +/* + Button.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +class Form_Button extends Form_Input +{ + protected $_tagSelfClosing = false; + protected $_attributes = array( + 'class' => array( + 'btn' => true, + ), + 'type' => 'submit', + ); + + public function __construct($name, $title, $link = null) + { + // If we have a link; we're actually an <a class='btn'> + if (isset($link)) + { + $this->_attributes['href'] = $link; + $this->_tagName = 'a'; + $this->addClass('btn-default'); + unset($this->_attributes['type']); + } + else + { + $this->_tagSelfClosing = true; + $this->_attributes['value'] = $title; + $this->addClass('btn-primary'); + } + + parent::__construct($name, $title, null); + } + + protected function _getInput() + { + $input = parent::_getInput(); + + if (!isset($this->_attributes['href'])) + return $input; + + return $input . htmlspecialchars($this->_title) .'</a>'; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Form/Checkbox.class.php b/src/usr/local/www/classes/Form/Checkbox.class.php new file mode 100644 index 0000000..e0734de --- /dev/null +++ b/src/usr/local/www/classes/Form/Checkbox.class.php @@ -0,0 +1,65 @@ +<?php +/* + Checkbox.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +class Form_Checkbox extends Form_Input +{ + protected $_attributes = array( + 'class' => array(), + ); + protected $_description; + + public function __construct($name, $title, $description, $checked, $value = 'yes') + { + parent::__construct($name, $title, 'checkbox', $value); + + $this->_description = $description; + + if ($checked) + $this->_attributes['checked'] = 'checked'; + + $this->column->addClass('checkbox'); + } + + public function displayAsRadio() + { + $this->_attributes['type'] = 'radio'; + + return $this; + } + + protected function _getInput() + { + $input = parent::_getInput(); + + if (!isset($this->_description)) + return $input; + + return '<label>'. $input .' '. htmlspecialchars(gettext($this->_description)) .'</label>'; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Form/Element.class.php b/src/usr/local/www/classes/Form/Element.class.php new file mode 100644 index 0000000..d3ad142 --- /dev/null +++ b/src/usr/local/www/classes/Form/Element.class.php @@ -0,0 +1,94 @@ +<?php +/* + Element.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +class Form_Element +{ + protected $_tagName; + protected $_tagSelfClosing; + protected $_attributes; + protected $_parent; + + public function __construct($tagName = 'div', $selfClose = false, $attributes = array('class' => array())) + { + $this->_tagName = $tagName; + $this->_tagSelfClosing = $selfClose; + $this->_attributes = $attributes; + } + + public function addClass() + { + foreach (func_get_args() as $class) + $this->_attributes['class'][$class] = true; + + return $this; + } + + public function removeClass($class) + { + unset($this->_attributes['class'][$class]); + + return $this; + } + + public function setAttribute($key, $value = null) + { + $this->_attributes[ $key ] = $value; + return $this; + } + + public function __toString() + { + $attributes = ''; + foreach ($this->_attributes as $key => $value) + { + if (is_array($value)) + { + // Used for classes. If it's empty, we don't want the attribute at all + if (!empty($value)) + $value = implode(' ', array_keys($value)); + else + $value = null; + } + + if ($value === null) + continue; + + $attributes .= ' '. $key; + if ($value !== true) + $attributes .= '="' . htmlspecialchars($value) . '"'; + } + + return '<'. $this->_tagName . $attributes . ($this->_tagSelfClosing ? '/' : '') .'>'; + } + + protected function _setParent(Form_Element $parent) + { + $this->_parent = $parent; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Form/Group.class.php b/src/usr/local/www/classes/Form/Group.class.php new file mode 100644 index 0000000..4ddf70d --- /dev/null +++ b/src/usr/local/www/classes/Form/Group.class.php @@ -0,0 +1,152 @@ +<?php +/* + Group.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +class Form_Group extends Form_Element +{ + protected $_tagName = 'div'; + protected $_attributes = array( + 'class' => array('form-group' => true), + ); + protected $_title; + protected $_inputs = array(); + protected $_labelTarget; + protected $_help; + protected $_helpParams = array(); + + public function __construct($title) + { + $this->_title = $title; + } + + public function add(Form_Input $input) + { + array_push($this->_inputs, $input); + $input->_setParent($this); + + // Defaults to first input + if (!isset($this->_labelTarget)) + $this->_labelTarget = $input; + + return $input; + } + + public function setLabelTarget(Form_Input $input) + { + $this->_labelTarget = $input; + } + + public function setHelp($help, array $params = array()) + { + $this->_help = $help; + $this->_helpParams = $params; + + return $this; + } + + public function enableDuplication($max = null, $horiz = false) + { + if($horiz) + $this->addClass('user-duplication-horiz'); // added buttons are 2 cols wide with no offset + else + $this->addClass('user-duplication'); // added buttons 10 cols wide with 2 col offset + + if (isset($max)) + $this->_attributes('data-duplicate-max', $max); + + foreach ($this->_inputs as $input) + $input->setIsRepeated(); + + return $this; + } + + protected function _getHelp() + { + if (!isset($this->_help)) + return null; + + $group = new Form_Element; + $group->addClass('col-sm-'. Form::MAX_INPUT_WIDTH, 'col-sm-offset-'. Form::LABEL_WIDTH); + + $help = gettext($this->_help); + + if (!empty($this->_helpParams)) + $help = call_user_func_array('sprintf', array_merge([$help], $this->_helpParams)); + + return <<<EOT + {$group} + <span class="help-block"> + {$help} + </span> + </div> +EOT; + } + + public function __toString() + { + $element = parent::__toString(); + + // Automatically determine width for inputs without explicit set + $spaceLeft = Form::MAX_INPUT_WIDTH; + $missingWidth = array(); + + foreach ($this->_inputs as $input) + { + if (count($this->_inputs) > 1 && !$input->hasAttribute('placeholder')) + $input->setPlaceholder($input->getTitle()); + + $width = $input->getWidth(); + + if (isset($width)) + $spaceLeft -= $width; + else + array_push($missingWidth, $input); + } + + foreach ($missingWidth as $input) + $input->setWidth($spaceLeft / count($missingWidth)); + + $target = $this->_labelTarget->getId(); + $inputs = implode('', $this->_inputs); + $help = $this->_getHelp(); + + $label = new Form_Element('label', false, ['for' => $target]); + $label->addClass('col-sm-'.Form::LABEL_WIDTH, 'control-label'); + + $title = htmlspecialchars(gettext($this->_title)); + + return <<<EOT + {$element} + {$label} + {$title} + </label> + {$inputs} + {$help} + </div> +EOT; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Form/Input.class.php b/src/usr/local/www/classes/Form/Input.class.php new file mode 100644 index 0000000..ec1cdca --- /dev/null +++ b/src/usr/local/www/classes/Form/Input.class.php @@ -0,0 +1,200 @@ +<?php +/* + Input.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +class Form_Input extends Form_Element +{ + public $column; + protected $_tagName = 'input'; + protected $_tagSelfClosing = true; + protected $_attributes = array( + 'class' => array('form-control' => true), + 'name' => null, + 'id' => null, + 'title' => null, + ); + protected $_title; + protected $_help; + protected $_helpParams = array(); + protected $_columnWidth; + + public function __construct($name, $title, $type = 'text', $value = null, array $attributes = array()) + { + $this->column = new Form_Element; + + $this->_attributes['name'] = $name; + $this->_attributes['id'] = $name; + $this->_title = $title; + + if (isset($type)) + $this->_attributes['type'] = $type; + + switch ($type) + { + case 'number': + $attributes += array('min' => 1, 'step' => 1); + break; + case 'file': + unset($this->_attributes['class']['form-control']); + break; + } + + if (isset($value)) + $this->_attributes['value'] = $value; + + foreach ($attributes as $name => $value) + $this->_attributes[$name] = $value; + } + + public function getTitle() + { + return $this->_title; + } + + public function getName() + { + return $this->_attributes['name']; + } + + public function getId() + { + return $this->_attributes['id']; + } + + public function setHelp($help, array $params = array()) + { + $this->_help = $help; + $this->_helpParams = $params; + + return $this; + } + + public function getWidth() + { + return $this->_columnWidth; + } + + public function setWidth($size) + { + if ($size < 1 || $size > Form::MAX_INPUT_WIDTH) + throw new Exception('Incorrect size, pass a number between 1 and '.Form::MAX_INPUT_WIDTH); + + $this->column->removeClass('col-sm-'. $this->_columnWidth); + + $this->_columnWidth = (int)$size; + + $this->column->addClass('col-sm-'. $this->_columnWidth); + + return $this; + } + + public function setReadonly() + { + $this->_attributes['readonly'] = 'readonly'; + + return $this; + } + + public function setDisabled() + { + $this->_attributes['disabled'] = 'disabled'; + + return $this; + } + + public function toggles($selector = null, $type = 'collapse') + { + if (isset($selector)) + $this->_attributes['data-target'] = $selector; + + $this->_attributes['data-toggle'] = $type; + + return $this; + } + + public function setPattern($regexp) + { + $this->_attributes['pattern'] = $regexp; + + return $this; + } + + public function setPlaceholder($text) + { + $this->_attributes['placeholder'] = $text; + + return $this; + } + + public function hasAttribute($name) + { + // not strict, null should return false as well + return isset($this->_attributes[$name]); + } + + public function setIsRepeated() + { + $this->_attributes['name'] .= '[]'; + // No I don't like this. Yes it works fine + $this->_attributes['id'] .= ':'.substr(uniqid(), 9); + + return $this; + } + + protected function _getInput() + { + return parent::__toString(); + } + + public function __toString() + { + $input = $this->_getInput(); + $column = (string)$this->column; + + // Don't add an empty <div>, skip it instead + if (!isset($this->_help) && '<div>' == $column) + return (string)$input; + + if (isset($this->_help)) + { + $help = gettext($this->_help); + + if (!empty($this->_helpParams)) + $help = call_user_func_array('sprintf', array_merge([$help], $this->_helpParams)); + + $help = '<span class="help-block">'. $help .'</span>'; + } + + return <<<EOT + {$column} + {$input} + + {$help} + </div> +EOT; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Form/IpAddress.class.php b/src/usr/local/www/classes/Form/IpAddress.class.php new file mode 100644 index 0000000..759b65d --- /dev/null +++ b/src/usr/local/www/classes/Form/IpAddress.class.php @@ -0,0 +1,76 @@ +<?php +/* + IpAddress.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +class Form_IpAddress extends Form_Input +{ + protected $_mask; + + public function __construct($name, $title, $value) + { + parent::__construct($name, $title, 'text', $value); + + $this->_attributes['pattern'] = '[a-f0-9:.]*'; + } + + public function addMask($name, $value, $max = 128) + { + $this->_mask = new Form_Select( + $name, + null, + $value, + array_combine(range($max, 1), range($max, 1)) + ); + + return $this; + } + + public function setIsRepeated() + { + if (isset($this->_mask)) + $this->_mask->setIsRepeated(); + + return parent::setIsRepeated(); + } + + protected function _getInput() + { + $input = parent::_getInput(); + + if (!isset($this->_mask)) + return $input; + + return <<<EOT + <div class="input-group"> + $input + <span class="input-group-addon input-group-inbetween pfIpMask">/</span> + {$this->_mask} + </div> +EOT; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Form/MultiCheckbox.class.php b/src/usr/local/www/classes/Form/MultiCheckbox.class.php new file mode 100644 index 0000000..9310977 --- /dev/null +++ b/src/usr/local/www/classes/Form/MultiCheckbox.class.php @@ -0,0 +1,40 @@ +<?php +/* + Form_MultiCheckbox.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +class Form_MultiCheckbox extends Form_Checkbox +{ + public function setHelp($help, array $params = array()) + { + throw new Exception('MultiCheckboxes do not support help-texts, please use $group->setHelp instead'); + } + + public function __toString() + { + return (string)$this->_getInput(); + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Form/MultiCheckboxGroup.class.php b/src/usr/local/www/classes/Form/MultiCheckboxGroup.class.php new file mode 100644 index 0000000..c04bf07 --- /dev/null +++ b/src/usr/local/www/classes/Form/MultiCheckboxGroup.class.php @@ -0,0 +1,64 @@ +<?php +/* + Form_MultiCheckboxGroup.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +class Form_MultiCheckboxGroup extends Form_Group +{ + public function add(Form_MultiCheckbox $input) + { + return parent::add($input); + } + + public function __toString() + { + $element = Form_Element::__toString(); + $column = new Form_Element; + $column->addClass('checkbox', 'multi', 'col-sm-10'); + + $inputs = implode('', $this->_inputs); + $help = $this->_getHelp(); + + $label = new Form_Element('label'); + $label->addClass('col-sm-'.Form::LABEL_WIDTH, 'control-label'); + + $title = htmlspecialchars(gettext($this->_title)); + + return <<<EOT + {$element} + {$label} + {$title} + </label> + + {$column} + {$inputs} + </div> + + {$help} + </div> +EOT; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Form/Section.class.php b/src/usr/local/www/classes/Form/Section.class.php new file mode 100644 index 0000000..b1dfb22 --- /dev/null +++ b/src/usr/local/www/classes/Form/Section.class.php @@ -0,0 +1,82 @@ +<?php +/* + Section.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +class Form_Section extends Form_Element +{ + protected $_tagName = 'div'; + protected $_attributes = array( + 'class' => array( + 'panel' => true, + 'panel-default' => true, + ), + ); + protected $_title; + protected $_groups = array(); + + public function __construct($title) + { + $this->_title = $title; + } + + public function add(Form_Group $group) + { + array_push($this->_groups, $group); + $group->_setParent($this); + + return $group; + } + + // Shortcut, adds a group for the specified input + public function addInput(Form_Input $input) + { + $group = new Form_Group($input->getTitle()); + $group->add($input); + + $this->add($group); + + return $input; + } + + public function __toString() + { + $element = parent::__toString(); + $title = htmlspecialchars(gettext($this->_title)); + $body = implode('', $this->_groups); + + return <<<EOT + {$element} + <div class="panel-heading"> + <h2 class="panel-title">{$title}</h2> + </div> + <div class="panel-body"> + {$body} + </div> + </div> +EOT; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Form/Select.class.php b/src/usr/local/www/classes/Form/Select.class.php new file mode 100644 index 0000000..1c4594d --- /dev/null +++ b/src/usr/local/www/classes/Form/Select.class.php @@ -0,0 +1,71 @@ +<?php +/* + Select.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +class Form_Select extends Form_Input +{ + protected $_tagName = 'select'; + protected $_values; + protected $_value; + + public function __construct($name, $title, $value, array $values, $allowMultiple = false) + { + if ($allowMultiple) + $name .= '[]'; + + parent::__construct($name, $title, null); + + if ($allowMultiple) + $this->_attributes['multiple'] = 'multiple'; + + $this->_value = $value; + $this->_values = $values; + } + + protected function _getInput() + { + $element = parent::_getInput(); + + $options = ''; + foreach ($this->_values as $value => $name) + { + if (isset($this->_attributes['multiple'])) + $selected = in_array($value, (array)$this->_value); + else + $selected = ($this->_value == $value); + + $options .= '<option value="'. htmlspecialchars($value) .'"'.($selected ? ' selected' : '').'>'. htmlspecialchars(gettext($name)) .'</option>'; + } + + return <<<EOT + {$element} + {$options} + </select> +EOT; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Form/StaticText.class.php b/src/usr/local/www/classes/Form/StaticText.class.php new file mode 100644 index 0000000..b6f7983 --- /dev/null +++ b/src/usr/local/www/classes/Form/StaticText.class.php @@ -0,0 +1,45 @@ +<?php +/* + StaticText.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +class Form_StaticText extends Form_Input +{ + protected $_text; + + public function __construct($title, $text) + { + parent::__construct(null, $title); + + $this->_text = $text; + } + + protected function _getInput() + { + return $this->_text; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Form/Textarea.class.php b/src/usr/local/www/classes/Form/Textarea.class.php new file mode 100644 index 0000000..8c1c157 --- /dev/null +++ b/src/usr/local/www/classes/Form/Textarea.class.php @@ -0,0 +1,54 @@ +<?php +/* + Textarea.class.php + + Copyright (C) 2015 Sjon Hortensius + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +class Form_Textarea extends Form_Input +{ + protected $_tagName = 'textarea'; + protected $_value; + protected $_attributes = array( + 'rows' => 5, + 'class' => array('form-control' => true), + ); + + public function __construct($name, $title, $value) + { + parent::__construct($name, $title, null); + + $this->_value = $value; + } + + protected function _getInput() + { + $element = parent::_getInput(); + $value = htmlspecialchars($this->_value); + + return <<<EOT + {$element}{$value}</textarea> +EOT; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/classes/Modal.class.php b/src/usr/local/www/classes/Modal.class.php new file mode 100644 index 0000000..e77c608 --- /dev/null +++ b/src/usr/local/www/classes/Modal.class.php @@ -0,0 +1,67 @@ +<?php + +require_once('classes/Form.class.php'); + +class Modal extends Form_Section +{ + protected $_attributes = array( + 'id' => null, + 'class' => array( + 'modal' => true, + 'fade' => true, + ), + 'role' => 'dialog', + 'aria-labelledby' => null, + 'aria-hidden' => 'true', + ); + protected $_global = array(); + protected $_isLarge; + + public function __construct($title, $id, $isLarge = false, $submit = null) + { + $this->_title = $title; + $this->_attributes['id'] = $this->_attributes['aria-labelledby'] = $id; + $this->_isLarge = $isLarge; + + if (gettype($submit) == 'string') + $submit = (new Form_Button( + 'save', + $submit + ))->setAttribute('data-dismiss', 'modal'); + + if (false !== $submit) + array_push($this->_global, $submit); + } + + public function __toString() + { + $element = Form_Element::__toString(); + $title = htmlspecialchars(gettext($this->_title)); + $body = implode('', $this->_groups); + $footer = implode('', $this->_global); + $modalClass = $this->_isLarge ? 'modal-lg' : 'modal-sm'; + + return <<<EOT + {$element} + <div class="modal-dialog {$modalClass}"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + <h3 class="modal-title">{$title}</h3> + </div> + <form class="form-horizontal" action="" method="post"> + <div class="modal-body"> + {$html} + </div> + <div class="modal-footer"> + {$footer} + </div> + </form> + </div> + </div> + </div> +EOT; + } +}
\ No newline at end of file diff --git a/src/usr/local/www/copyright-master.txt b/src/usr/local/www/copyright-master.txt new file mode 100644 index 0000000..5bfc17c --- /dev/null +++ b/src/usr/local/www/copyright-master.txt @@ -0,0 +1,51 @@ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */
\ No newline at end of file diff --git a/src/usr/local/www/crash_reporter.php b/src/usr/local/www/crash_reporter.php new file mode 100644 index 0000000..dc0a7c9 --- /dev/null +++ b/src/usr/local/www/crash_reporter.php @@ -0,0 +1,178 @@ +<?php +/* $Id$ */ +/* + crash_reporter.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: header +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-crash-reporter +##|*NAME=Crash reporter +##|*DESCR=Uploads crash reports to pfSense and or deletes crash reports. +##|*MATCH=crash_reporter.php* +##|-PRIV + +require("guiconfig.inc"); +require("functions.inc"); +require("captiveportal.inc"); + +define("FILE_SIZE", 450000); + +function upload_crash_report($files) { + global $g; + $post = array(); + $counter = 0; + foreach($files as $file) { + $post["file{$counter}"] = "@{$file}"; + $counter++; + } + $ch = curl_init(); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_VERBOSE, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_USERAGENT, $g['product_name'] . '/' . $g['product_version']); + curl_setopt($ch, CURLOPT_URL, $g['crashreporterurl']); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post); + $response = curl_exec($ch); + return $response; +} + +$pgtitle = array(gettext("Diagnostics"),gettext("Crash reporter")); +include('head.inc'); + +$crash_report_header = "Crash report begins. Anonymous machine information:\n\n"; +$crash_report_header .= php_uname("m") . "\n"; +$crash_report_header .= php_uname("r") . "\n"; +$crash_report_header .= php_uname("v") . "\n"; +$crash_report_header .= "\nCrash report details:\n"; + +exec("/usr/bin/grep -vi warning /tmp/PHP_errors.log", $php_errors); +?> +<?php + if (gettext($_POST['Submit']) == "Yes") { + echo gettext("Processing..."); + if (!is_dir("/var/crash")) { + mkdir("/var/crash", 0750, true); + } + @file_put_contents("/var/crash/crashreport_header.txt", $crash_report_header); + if (file_exists("/tmp/PHP_errors.log")) { + copy("/tmp/PHP_errors.log", "/var/crash/PHP_errors.log"); + } + exec("find /var/crash -type l -exec rm {} +"); + exec("/usr/bin/gzip /var/crash/*"); + $files_to_upload = glob("/var/crash/*"); + echo "<br/>"; + echo gettext("Uploading..."); + ob_flush(); + flush(); + if(is_array($files_to_upload)) { + $resp = upload_crash_report($files_to_upload); + array_map('unlink', glob("/var/crash/*")); + // Erase the contents of the PHP error log + fclose(fopen("/tmp/PHP_errors.log", 'w')); + echo "<br/>"; + print_r($resp); + echo "<p><a href=\"/\">" . gettext("Continue") . "</a>" . gettext(" and delete crash report files from local disk.") . "</p>"; + } else { + echo "Could not find any crash files."; + } + } else if(gettext($_POST['Submit']) == "No") { + array_map('unlink', glob("/var/crash/*")); + // Erase the contents of the PHP error log + fclose(fopen("/tmp/PHP_errors.log", 'w')); + header("Location: /"); + exit; + } else { + $crash_files = glob("/var/crash/*"); + $crash_reports = $crash_report_header; + if (count($php_errors) > 0) { + $crash_reports .= "\nPHP Errors:\n"; + $crash_reports .= implode("\n", $php_errors) . "\n\n"; + } + if(is_array($crash_files)) { + foreach($crash_files as $cf) { + if(filesize($cf) < FILE_SIZE) { + $crash_reports .= "\nFilename: {$cf}\n"; + $crash_reports .= file_get_contents($cf); + } + } + } else { + echo "Could not locate any crash data."; + } +?> + <div class="jumbotron"> + <div class="panel panel-default"> + <div class="panel-heading"><h3><?=gettext("Unfortunately we have detected a programming bug.")?></h3></div> + <div class="panel-body"> + <p> + <?=gettext("Would you like to submit the programming debug logs to the pfSense developers for inspection?")?> + <i><?=gettext("Please double check the contents to ensure you are comfortable sending this information before clicking Yes.")?></i> + </p> + <textarea readonly="readonly" style="width: 100%; height: 350px;"> + <?=$crash_reports?> + </textarea> + <form action="crash_reporter.php" method="post"> + <button class="btn btn-primary" name="Submit" type="submit" value="Yes"><?=gettext("Yes")?> - <?=gettext("Submit this to the developers for inspection")?></button> + <button class="btn btn-default" name="Submit" type="submit" value="No"><?=gettext("No")?> - <?=gettext("Just delete the crash report and take me back to the Dashboard")?></button> + </form> + </div> + </div> +<?php + } +?> + +<?php include("foot.inc")?>
\ No newline at end of file diff --git a/src/usr/local/www/csrf/csrf-magic.js b/src/usr/local/www/csrf/csrf-magic.js new file mode 100644 index 0000000..d358b0f --- /dev/null +++ b/src/usr/local/www/csrf/csrf-magic.js @@ -0,0 +1,186 @@ +/** + * @file + * + * Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory + * plays nice with other JavaScript libraries, needs testing though. + */ + +// Here are the basic overloaded method definitions +// The wrapper must be set BEFORE onreadystatechange is written to, since +// a bug in ActiveXObject prevents us from properly testing for it. +CsrfMagic = function(real) { + // try to make it ourselves, if you didn't pass it + if (!real) try { real = new XMLHttpRequest; } catch (e) {;} + if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;} + if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;} + if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;} + this.csrf = real; + // properties + var csrfMagic = this; + real.onreadystatechange = function() { + csrfMagic._updateProps(); + return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null; + }; + csrfMagic._updateProps(); +} + +CsrfMagic.prototype = { + + open: function(method, url, async, username, password) { + if (method == 'POST') this.csrf_isPost = true; + // deal with Opera bug, thanks jQuery + if (username) return this.csrf_open(method, url, async, username, password); + else return this.csrf_open(method, url, async); + }, + csrf_open: function(method, url, async, username, password) { + if (username) return this.csrf.open(method, url, async, username, password); + else return this.csrf.open(method, url, async); + }, + + send: function(data) { + if (!this.csrf_isPost) return this.csrf_send(data); + prepend = csrfMagicName + '=' + csrfMagicToken + '&'; + if (this.csrf_purportedLength === undefined) { + this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length); + delete this.csrf_purportedLength; + } + delete this.csrf_isPost; + return this.csrf_send(prepend + data); + }, + csrf_send: function(data) { + return this.csrf.send(data); + }, + + setRequestHeader: function(header, value) { + // We have to auto-set this at the end, since we don't know how long the + // nonce is when added to the data. + if (this.csrf_isPost && header == "Content-length") { + this.csrf_purportedLength = value; + return; + } + return this.csrf_setRequestHeader(header, value); + }, + csrf_setRequestHeader: function(header, value) { + return this.csrf.setRequestHeader(header, value); + }, + + abort: function() { + return this.csrf.abort(); + }, + getAllResponseHeaders: function() { + return this.csrf.getAllResponseHeaders(); + }, + getResponseHeader: function(header) { + return this.csrf.getResponseHeader(header); + } // , +} + +// proprietary +CsrfMagic.prototype._updateProps = function() { + this.readyState = this.csrf.readyState; + if (this.readyState == 4) { + this.responseText = this.csrf.responseText; + this.responseXML = this.csrf.responseXML; + this.status = this.csrf.status; + this.statusText = this.csrf.statusText; + } +} +CsrfMagic.process = function(base) { + var prepend = csrfMagicName + '=' + csrfMagicToken; + if (base) return prepend + '&' + base; + return prepend; +} +// callback function for when everything on the page has loaded +CsrfMagic.end = function() { + // This rewrites forms AGAIN, so in case buffering didn't work this + // certainly will. + forms = document.getElementsByTagName('form'); + for (var i = 0; i < forms.length; i++) { + form = forms[i]; + if (form.method.toUpperCase() !== 'POST') continue; + if (form.elements[csrfMagicName]) continue; + var input = document.createElement('input'); + input.setAttribute('name', csrfMagicName); + input.setAttribute('value', csrfMagicToken); + input.setAttribute('type', 'hidden'); + form.appendChild(input); + } +} + +// Sets things up for Mozilla/Opera/nice browsers +// We very specifically match against Internet Explorer, since they haven't +// implemented prototypes correctly yet. +if (window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v') { + var x = XMLHttpRequest.prototype; + var c = CsrfMagic.prototype; + + // Save the original functions + x.csrf_open = x.open; + x.csrf_send = x.send; + x.csrf_setRequestHeader = x.setRequestHeader; + + // Notice that CsrfMagic is itself an instantiatable object, but only + // open, send and setRequestHeader are necessary as decorators. + x.open = c.open; + x.send = c.send; + x.setRequestHeader = c.setRequestHeader; +} else { + // The only way we can do this is by modifying a library you have been + // using. We support YUI, script.aculo.us, prototype, MooTools, + // jQuery, Ext and Dojo. + if (window.jQuery) { + // jQuery didn't implement a new XMLHttpRequest function, so we have + // to do this the hard way. + jQuery.csrf_ajax = jQuery.ajax; + jQuery.ajax = function( s ) { + if (s.type && s.type.toUpperCase() == 'POST') { + s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s)); + if ( s.data && s.processData && typeof s.data != "string" ) { + s.data = jQuery.param(s.data); + } + s.data = CsrfMagic.process(s.data); + } + return jQuery.csrf_ajax( s ); + } + } + if (window.Prototype) { + // This works for script.aculo.us too + Ajax.csrf_getTransport = Ajax.getTransport; + Ajax.getTransport = function() { + return new CsrfMagic(Ajax.csrf_getTransport()); + } + } + if (window.MooTools) { + Browser.csrf_Request = Browser.Request; + Browser.Request = function () { + return new CsrfMagic(Browser.csrf_Request()); + } + } + if (window.YAHOO) { + // old YUI API + YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject; + YAHOO.util.Connect.createXhrObject = function (transaction) { + obj = YAHOO.util.Connect.csrf_createXhrObject(transaction); + obj.conn = new CsrfMagic(obj.conn); + return obj; + } + } + if (window.Ext) { + // Ext can use other js libraries as loaders, so it has to come last + // Ext's implementation is pretty identical to Yahoo's, but we duplicate + // it for comprehensiveness's sake. + Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject; + Ext.lib.Ajax.createXhrObject = function (transaction) { + obj = Ext.lib.Ajax.csrf_createXhrObject(transaction); + obj.conn = new CsrfMagic(obj.conn); + return obj; + } + } + if (window.dojo) { + // NOTE: this doesn't work with latest dojo + dojo.csrf__xhrObj = dojo._xhrObj; + dojo._xhrObj = function () { + return new CsrfMagic(dojo.csrf__xhrObj()); + } + } +} diff --git a/src/usr/local/www/csrf/csrf-magic.php b/src/usr/local/www/csrf/csrf-magic.php new file mode 100644 index 0000000..58f4eba --- /dev/null +++ b/src/usr/local/www/csrf/csrf-magic.php @@ -0,0 +1,403 @@ +<?php + +/** + * @file + * + * csrf-magic is a PHP library that makes adding CSRF-protection to your + * web applications a snap. No need to modify every form or create a database + * of valid nonces; just include this file at the top of every + * web-accessible page (or even better, your common include file included + * in every page), and forget about it! (There are, of course, configuration + * options for advanced users). + * + * This library is PHP4 and PHP5 compatible. + */ + +// CONFIGURATION: + +/** + * By default, when you include this file csrf-magic will automatically check + * and exit if the CSRF token is invalid. This will defer executing + * csrf_check() until you're ready. You can also pass false as a parameter to + * that function, in which case the function will not exit but instead return + * a boolean false if the CSRF check failed. This allows for tighter integration + * with your system. + */ +$GLOBALS['csrf']['defer'] = false; + +/** + * This is the amount of seconds you wish to allow before any token becomes + * invalid; the default is two hours, which should be more than enough for + * most websites. + */ +$GLOBALS['csrf']['expires'] = 7200; + +/** + * Callback function to execute when there's the CSRF check fails and + * $fatal == true (see csrf_check). This will usually output an error message + * about the failure. + */ +$GLOBALS['csrf']['callback'] = 'csrf_callback'; + +/** + * Whether or not to include our JavaScript library which also rewrites + * AJAX requests on this domain. Set this to the web path. This setting only works + * with supported JavaScript libraries in Internet Explorer; see README.txt for + * a list of supported libraries. + */ +$GLOBALS['csrf']['rewrite-js'] = false; + +/** + * A secret key used when hashing items. Please generate a random string and + * place it here. If you change this value, all previously generated tokens + * will become invalid. + */ +$GLOBALS['csrf']['secret'] = ''; +// nota bene: library code should use csrf_get_secret() and not access +// this global directly + +/** + * Set this to false to disable csrf-magic's output handler, and therefore, + * its rewriting capabilities. If you're serving non HTML content, you should + * definitely set this false. + */ +$GLOBALS['csrf']['rewrite'] = true; + +/** + * Whether or not to use IP addresses when binding a user to a token. This is + * less reliable and less secure than sessions, but is useful when you need + * to give facilities to anonymous users and do not wish to maintain a database + * of valid keys. + */ +$GLOBALS['csrf']['allow-ip'] = true; + +/** + * If this information is available, use the cookie by this name to determine + * whether or not to allow the request. This is a shortcut implementation + * very similar to 'key', but we randomly set the cookie ourselves. + */ +$GLOBALS['csrf']['cookie'] = '__csrf_cookie'; + +/** + * If this information is available, set this to a unique identifier (it + * can be an integer or a unique username) for the current "user" of this + * application. The token will then be globally valid for all of that user's + * operations, but no one else. This requires that 'secret' be set. + */ +$GLOBALS['csrf']['user'] = false; + +/** + * This is an arbitrary secret value associated with the user's session. This + * will most probably be the contents of a cookie, as an attacker cannot easily + * determine this information. Warning: If the attacker knows this value, they + * can easily spoof a token. This is a generic implementation; sessions should + * work in most cases. + * + * Why would you want to use this? Lets suppose you have a squid cache for your + * website, and the presence of a session cookie bypasses it. Let's also say + * you allow anonymous users to interact with the website; submitting forms + * and AJAX. Previously, you didn't have any CSRF protection for anonymous users + * and so they never got sessions; you don't want to start using sessions either, + * otherwise you'll bypass the Squid cache. Setup a different cookie for CSRF + * tokens, and have Squid ignore that cookie for get requests, for anonymous + * users. (If you haven't guessed, this scheme was(?) used for MediaWiki). + */ +$GLOBALS['csrf']['key'] = false; + +/** + * The name of the magic CSRF token that will be placed in all forms, i.e. + * the contents of <input type="hidden" name="$name" value="CSRF-TOKEN" /> + */ +$GLOBALS['csrf']['input-name'] = '__csrf_magic'; + +/** + * Set this to false if your site must work inside of frame/iframe elements, + * but do so at your own risk: this configuration protects you against CSS + * overlay attacks that defeat tokens. + */ +$GLOBALS['csrf']['frame-breaker'] = true; + +/** + * Whether or not CSRF Magic should be allowed to start a new session in order + * to determine the key. + */ +$GLOBALS['csrf']['auto-session'] = true; + +/** + * Whether or not csrf-magic should produce XHTML style tags. + */ +$GLOBALS['csrf']['xhtml'] = true; + +// FUNCTIONS: + +// Don't edit this! +$GLOBALS['csrf']['version'] = '1.0.4'; + +/** + * Rewrites <form> on the fly to add CSRF tokens to them. This can also + * inject our JavaScript library. + */ +function csrf_ob_handler($buffer, $flags) { + // Even though the user told us to rewrite, we should do a quick heuristic + // to check if the page is *actually* HTML. We don't begin rewriting until + // we hit the first <html tag. + static $is_html = false; + if (!$is_html) { + // not HTML until proven otherwise + if (stripos($buffer, '<html') !== false) { + $is_html = true; + } else { + return $buffer; + } + } + $tokens = csrf_get_tokens(); + $name = $GLOBALS['csrf']['input-name']; + $endslash = $GLOBALS['csrf']['xhtml'] ? ' /' : ''; + $input = "<input type='hidden' name='$name' value=\"$tokens\"$endslash>"; + $buffer = preg_replace('#(<form[^>]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer); + if ($GLOBALS['csrf']['frame-breaker']) { + $buffer = str_ireplace('</head>', '<script type="text/javascript">if (top != self) {top.location.href = self.location.href;}</script></head>', $buffer); + } + if ($js = $GLOBALS['csrf']['rewrite-js']) { + $buffer = str_ireplace( + '</head>', + '<script type="text/javascript">'. + 'var csrfMagicToken = "'.$tokens.'";'. + 'var csrfMagicName = "'.$name.'";</script>'. + '<script src="'.$js.'" type="text/javascript"></script></head>', + $buffer + ); + $script = '<script type="text/javascript">CsrfMagic.end();</script>'; + $buffer = str_ireplace('</body>', $script . '</body>', $buffer, $count); + if (!$count) { + $buffer .= $script; + } + } + return $buffer; +} + +/** + * Checks if this is a post request, and if it is, checks if the nonce is valid. + * @param bool $fatal Whether or not to fatally error out if there is a problem. + * @return True if check passes or is not necessary, false if failure. + */ +function csrf_check($fatal = true) { + if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; + csrf_start(); + $name = $GLOBALS['csrf']['input-name']; + $ok = false; + $tokens = ''; + do { + if (!isset($_POST[$name])) break; + // we don't regenerate a token and check it because some token creation + // schemes are volatile. + $tokens = $_POST[$name]; + if (!csrf_check_tokens($tokens)) break; + $ok = true; + } while (false); + if ($fatal && !$ok) { + $callback = $GLOBALS['csrf']['callback']; + if (trim($tokens, 'A..Za..z0..9:;,') !== '') $tokens = 'hidden'; + $callback($tokens); + exit; + } + return $ok; +} + +/** + * Retrieves a valid token(s) for a particular context. Tokens are separated + * by semicolons. + */ +function csrf_get_tokens() { + $has_cookies = !empty($_COOKIE); + + // $ip implements a composite key, which is sent if the user hasn't sent + // any cookies. It may or may not be used, depending on whether or not + // the cookies "stick" + $secret = csrf_get_secret(); + if (!$has_cookies && $secret) { + // :TODO: Harden this against proxy-spoofing attacks + $ip = ';ip:' . csrf_hash($_SERVER['IP_ADDRESS']); + } else { + $ip = ''; + } + csrf_start(); + + // These are "strong" algorithms that don't require per se a secret + if (session_id()) return 'sid:' . csrf_hash(session_id()) . $ip; + if ($GLOBALS['csrf']['cookie']) { + $val = csrf_generate_secret(); + setcookie($GLOBALS['csrf']['cookie'], $val); + return 'cookie:' . csrf_hash($val) . $ip; + } + if ($GLOBALS['csrf']['key']) return 'key:' . csrf_hash($GLOBALS['csrf']['key']) . $ip; + // These further algorithms require a server-side secret + if (!$secret) return 'invalid'; + if ($GLOBALS['csrf']['user'] !== false) { + return 'user:' . csrf_hash($GLOBALS['csrf']['user']); + } + if ($GLOBALS['csrf']['allow-ip']) { + return ltrim($ip, ';'); + } + return 'invalid'; +} + +function csrf_flattenpost($data) { + $ret = array(); + foreach($data as $n => $v) { + $ret = array_merge($ret, csrf_flattenpost2(1, $n, $v)); + } + return $ret; +} +function csrf_flattenpost2($level, $key, $data) { + if(!is_array($data)) return array($key => $data); + $ret = array(); + foreach($data as $n => $v) { + $nk = $level >= 1 ? $key."[$n]" : "[$n]"; + $ret = array_merge($ret, csrf_flattenpost2($level+1, $nk, $v)); + } + return $ret; +} + +/** + * @param $tokens is safe for HTML consumption + */ +function csrf_callback($tokens) { + // (yes, $tokens is safe to echo without escaping) + header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); + $data = ''; + foreach (csrf_flattenpost($_POST) as $key => $value) { + if ($key == $GLOBALS['csrf']['input-name']) continue; + $data .= '<input type="hidden" name="'.htmlspecialchars($key).'" value="'.htmlspecialchars($value).'" />'; + } + echo "<html><head><title>CSRF check failed</title></head> + <body> + <p>CSRF check failed. Your form session may have expired, or you may not have + cookies enabled.</p> + <form method='post' action=''>$data<input type='submit' value='Try again' /></form> + <p>Debug: $tokens</p></body></html> +"; +} + +/** + * Checks if a composite token is valid. Outward facing code should use this + * instead of csrf_check_token() + */ +function csrf_check_tokens($tokens) { + if (is_string($tokens)) $tokens = explode(';', $tokens); + foreach ($tokens as $token) { + if (csrf_check_token($token)) return true; + } + return false; +} + +/** + * Checks if a token is valid. + */ +function csrf_check_token($token) { + if (strpos($token, ':') === false) return false; + list($type, $value) = explode(':', $token, 2); + if (strpos($value, ',') === false) return false; + list($x, $time) = explode(',', $token, 2); + if ($GLOBALS['csrf']['expires']) { + if (time() > $time + $GLOBALS['csrf']['expires']) return false; + } + switch ($type) { + case 'sid': + return $value === csrf_hash(session_id(), $time); + case 'cookie': + $n = $GLOBALS['csrf']['cookie']; + if (!$n) return false; + if (!isset($_COOKIE[$n])) return false; + return $value === csrf_hash($_COOKIE[$n], $time); + case 'key': + if (!$GLOBALS['csrf']['key']) return false; + return $value === csrf_hash($GLOBALS['csrf']['key'], $time); + // We could disable these 'weaker' checks if 'key' was set, but + // that doesn't make me feel good then about the cookie-based + // implementation. + case 'user': + if (!csrf_get_secret()) return false; + if ($GLOBALS['csrf']['user'] === false) return false; + return $value === csrf_hash($GLOBALS['csrf']['user'], $time); + case 'ip': + if (!csrf_get_secret()) return false; + // do not allow IP-based checks if the username is set, or if + // the browser sent cookies + if ($GLOBALS['csrf']['user'] !== false) return false; + if (!empty($_COOKIE)) return false; + if (!$GLOBALS['csrf']['allow-ip']) return false; + return $value === csrf_hash($_SERVER['IP_ADDRESS'], $time); + } + return false; +} + +/** + * Sets a configuration value. + */ +function csrf_conf($key, $val) { + if (!isset($GLOBALS['csrf'][$key])) { + trigger_error('No such configuration ' . $key, E_USER_WARNING); + return; + } + $GLOBALS['csrf'][$key] = $val; +} + +/** + * Starts a session if we're allowed to. + */ +function csrf_start() { + if ($GLOBALS['csrf']['auto-session'] && !session_id()) { + session_start(); + } +} + +/** + * Retrieves the secret, and generates one if necessary. + */ +function csrf_get_secret() { + if ($GLOBALS['csrf']['secret']) return $GLOBALS['csrf']['secret']; + $dir = dirname(__FILE__); + $file = $dir . '/csrf-secret.php'; + $secret = ''; + if (file_exists($file)) { + include $file; + return $secret; + } + if (is_writable($dir)) { + $secret = csrf_generate_secret(); + $fh = fopen($file, 'w'); + fwrite($fh, '<?php $secret = "'.$secret.'";' . PHP_EOL); + fclose($fh); + return $secret; + } + return ''; +} + +/** + * Generates a random string as the hash of time, microtime, and mt_rand. + */ +function csrf_generate_secret($len = 32) { + $r = ''; + for ($i = 0; $i < 32; $i++) { + $r .= chr(mt_rand(0, 255)); + } + $r .= time() . microtime(); + return sha1($r); +} + +/** + * Generates a hash/expiry double. If time isn't set it will be calculated + * from the current time. + */ +function csrf_hash($value, $time = null) { + if (!$time) $time = time(); + return sha1(csrf_get_secret() . $value . $time) . ',' . $time; +} + +// Load user configuration +if (function_exists('csrf_startup')) csrf_startup(); +// Initialize our handler +if ($GLOBALS['csrf']['rewrite']) ob_start('csrf_ob_handler'); +// Perform check +if (!$GLOBALS['csrf']['defer']) csrf_check(); diff --git a/src/usr/local/www/d3pie/d3.min.js b/src/usr/local/www/d3pie/d3.min.js new file mode 100644 index 0000000..7b7b9b1 --- /dev/null +++ b/src/usr/local/www/d3pie/d3.min.js @@ -0,0 +1,5 @@ +!function(){function n(n,t){return t>n?-1:n>t?1:n>=t?0:0/0}function t(n){return null!=n&&!isNaN(n)}function e(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)<0?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)>0?u=i:r=i+1}return r}}}function r(n){return n.length}function u(n){for(var t=1;n*t%1;)t*=10;return t}function i(n,t){try{for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}catch(r){n.prototype=t}}function o(){}function a(n){return ha+n in this}function c(n){return n=ha+n,n in this&&delete this[n]}function s(){var n=[];return this.forEach(function(t){n.push(t)}),n}function l(){var n=0;for(var t in this)t.charCodeAt(0)===ga&&++n;return n}function f(){for(var n in this)if(n.charCodeAt(0)===ga)return!1;return!0}function h(){}function g(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function p(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.substring(1);for(var e=0,r=pa.length;r>e;++e){var u=pa[e]+t;if(u in n)return u}}function v(){}function d(){}function m(n){function t(){for(var t,r=e,u=-1,i=r.length;++u<i;)(t=r[u].on)&&t.apply(this,arguments);return n}var e=[],r=new o;return t.on=function(t,u){var i,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,i=e.indexOf(o)).concat(e.slice(i+1)),r.remove(t)),u&&e.push(r.set(t,{on:u})),n)},t}function y(){Go.event.preventDefault()}function x(){for(var n,t=Go.event;n=t.sourceEvent;)t=n;return t}function M(n){for(var t=new d,e=0,r=arguments.length;++e<r;)t[arguments[e]]=m(t);return t.of=function(e,r){return function(u){try{var i=u.sourceEvent=Go.event;u.target=n,Go.event=u,t[u.type].apply(e,r)}finally{Go.event=i}}},t}function _(n){return da(n,_a),n}function b(n){return"function"==typeof n?n:function(){return ma(n,this)}}function w(n){return"function"==typeof n?n:function(){return ya(n,this)}}function S(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function u(){this.setAttribute(n,t)}function i(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=Go.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?i:u}function k(n){return n.trim().replace(/\s+/g," ")}function E(n){return new RegExp("(?:^|\\s+)"+Go.requote(n)+"(?:\\s+|$)","g")}function N(n){return n.trim().split(/^|\s+/)}function A(n,t){function e(){for(var e=-1;++e<u;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<u;)n[e](this,r)}n=N(n).map(C);var u=n.length;return"function"==typeof t?r:e}function C(n){var t=E(n);return function(e,r){if(u=e.classList)return r?u.add(n):u.remove(n);var u=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(u)||e.setAttribute("class",k(u+" "+n))):e.setAttribute("class",k(u.replace(t," ")))}}function L(n,t,e){function r(){this.style.removeProperty(n)}function u(){this.style.setProperty(n,t,e)}function i(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?i:u}function T(n,t){function e(){delete this[n]}function r(){this[n]=t}function u(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?u:r}function q(n){return"function"==typeof n?n:(n=Go.ns.qualify(n)).local?function(){return this.ownerDocument.createElementNS(n.space,n.local)}:function(){return this.ownerDocument.createElementNS(this.namespaceURI,n)}}function z(n){return{__data__:n}}function R(n){return function(){return Ma(this,n)}}function D(t){return arguments.length||(t=n),function(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}}function P(n,t){for(var e=0,r=n.length;r>e;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function U(n){return da(n,wa),n}function j(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t<c;);return o}}function H(){var n=this.__transition__;n&&++n.active}function F(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function u(){var u=c(t,Qo(arguments));r.call(this),this.addEventListener(n,this[o]=u,u.$=e),u._=t}function i(){var t,e=new RegExp("^__on([^.]+)"+Go.requote(n)+"$");for(var r in this)if(t=r.match(e)){var u=this[r];this.removeEventListener(t[1],u,u.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),c=O;a>0&&(n=n.substring(0,a));var s=ka.get(n);return s&&(n=s,c=Y),a?t?u:r:t?v:i}function O(n,t){return function(e){var r=Go.event;Go.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{Go.event=r}}}function Y(n,t){var e=O(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function I(){var n=".dragsuppress-"+ ++Na,t="click"+n,e=Go.select(ea).on("touchmove"+n,y).on("dragstart"+n,y).on("selectstart"+n,y);if(Ea){var r=ta.style,u=r[Ea];r[Ea]="none"}return function(i){function o(){e.on(t,null)}e.on(n,null),Ea&&(r[Ea]=u),i&&(e.on(t,function(){y(),o()},!0),setTimeout(o,0))}}function Z(n,t){t.changedTouches&&(t=t.changedTouches[0]);var e=n.ownerSVGElement||n;if(e.createSVGPoint){var r=e.createSVGPoint();return r.x=t.clientX,r.y=t.clientY,r=r.matrixTransform(n.getScreenCTM().inverse()),[r.x,r.y]}var u=n.getBoundingClientRect();return[t.clientX-u.left-n.clientLeft,t.clientY-u.top-n.clientTop]}function V(){return Go.event.changedTouches[0].identifier}function $(){return Go.event.target}function X(){return ea}function B(n){return n>0?1:0>n?-1:0}function J(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function W(n){return n>1?0:-1>n?Aa:Math.acos(n)}function G(n){return n>1?La:-1>n?-La:Math.asin(n)}function K(n){return((n=Math.exp(n))-1/n)/2}function Q(n){return((n=Math.exp(n))+1/n)/2}function nt(n){return((n=Math.exp(2*n))-1)/(n+1)}function tt(n){return(n=Math.sin(n/2))*n}function et(){}function rt(n,t,e){return new ut(n,t,e)}function ut(n,t,e){this.h=n,this.s=t,this.l=e}function it(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,yt(u(n+120),u(n),u(n-120))}function ot(n,t,e){return new at(n,t,e)}function at(n,t,e){this.h=n,this.c=t,this.l=e}function ct(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),st(e,Math.cos(n*=za)*t,Math.sin(n)*t)}function st(n,t,e){return new lt(n,t,e)}function lt(n,t,e){this.l=n,this.a=t,this.b=e}function ft(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=gt(u)*Za,r=gt(r)*Va,i=gt(i)*$a,yt(vt(3.2404542*u-1.5371385*r-.4985314*i),vt(-.969266*u+1.8760108*r+.041556*i),vt(.0556434*u-.2040259*r+1.0572252*i))}function ht(n,t,e){return n>0?ot(Math.atan2(e,t)*Ra,Math.sqrt(t*t+e*e),n):ot(0/0,0/0,n)}function gt(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function pt(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function vt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function dt(n){return yt(n>>16,255&n>>8,255&n)}function mt(n){return dt(n)+""}function yt(n,t,e){return new xt(n,t,e)}function xt(n,t,e){this.r=n,this.g=t,this.b=e}function Mt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _t(n,t,e){var r,u,i,o=0,a=0,c=0;if(r=/([a-z]+)\((.*)\)/i.exec(n))switch(u=r[2].split(","),r[1]){case"hsl":return e(parseFloat(u[0]),parseFloat(u[1])/100,parseFloat(u[2])/100);case"rgb":return t(kt(u[0]),kt(u[1]),kt(u[2]))}return(i=Ja.get(n))?t(i.r,i.g,i.b):(null==n||"#"!==n.charAt(0)||isNaN(i=parseInt(n.substring(1),16))||(4===n.length?(o=(3840&i)>>4,o=o>>4|o,a=240&i,a=a>>4|a,c=15&i,c=c<<4|c):7===n.length&&(o=(16711680&i)>>16,a=(65280&i)>>8,c=255&i)),t(o,a,c))}function bt(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),rt(r,u,c)}function wt(n,t,e){n=St(n),t=St(t),e=St(e);var r=pt((.4124564*n+.3575761*t+.1804375*e)/Za),u=pt((.2126729*n+.7151522*t+.072175*e)/Va),i=pt((.0193339*n+.119192*t+.9503041*e)/$a);return st(116*u-16,500*(r-u),200*(u-i))}function St(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function kt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function Et(n){return"function"==typeof n?n:function(){return n}}function Nt(n){return n}function At(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Ct(t,e,n,r)}}function Ct(n,t,e,r){function u(){var n,t=c.status;if(!t&&c.responseText||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return o.error.call(i,r),void 0}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=Go.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,s=null;return!ea.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=Go.event;Go.event=n;try{o.progress.call(i,c)}finally{Go.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(s=n,i):s},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(Qo(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var l in a)c.setRequestHeader(l,a[l]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=s&&(c.responseType=s),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},Go.rebind(i,o,"on"),null==r?i:i.get(Lt(r))}function Lt(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Tt(){var n=qt(),t=zt()-n;t>24?(isFinite(t)&&(clearTimeout(Qa),Qa=setTimeout(Tt,t)),Ka=0):(Ka=1,tc(Tt))}function qt(){var n=Date.now();for(nc=Wa;nc;)n>=nc.t&&(nc.f=nc.c(n-nc.t)),nc=nc.n;return n}function zt(){for(var n,t=Wa,e=1/0;t;)t.f?t=n?n.n=t.n:Wa=t.n:(t.t<e&&(e=t.t),t=(n=t).n);return Ga=n,e}function Rt(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Dt(n,t){var e=Math.pow(10,3*fa(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Pt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r?function(n){for(var t=n.length,u=[],i=0,o=r[0];t>0&&o>0;)u.push(n.substring(t-=o,t+o)),o=r[i=(i+1)%r.length];return u.reverse().join(e)}:Nt;return function(n){var e=rc.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"",c=e[4]||"",s=e[5],l=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1;switch(h&&(h=+h.substring(1)),(s||"0"===r&&"="===o)&&(s=r="0",o="=",f&&(l-=Math.floor((l-1)/4))),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=uc.get(g)||Ut;var y=s&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):a;if(0>p){var c=Go.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x=n.lastIndexOf("."),M=0>x?n:n.substring(0,x),_=0>x?"":t+n.substring(x+1);!s&&f&&(M=i(M));var b=v.length+M.length+_.length+(y?0:u.length),w=l>b?new Array(b=l-b+1).join(r):"";return y&&(M=i(w+M)),u+=v,n=M+_,("<"===o?u+n+w:">"===o?w+u+n:"^"===o?w.substring(0,b>>=1)+u+n+w.substring(b):u+(y?n:w+n))+e}}}function Ut(n){return n+""}function jt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Ht(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new oc(e-1)),1),e}function i(n,e){return t(n=new oc(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{oc=jt;var r=new jt;return r._=n,o(r,t,e)}finally{oc=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Ft(n);return c.floor=c,c.round=Ft(r),c.ceil=Ft(u),c.offset=Ft(i),c.range=a,n}function Ft(n){return function(t,e){try{oc=jt;var r=new jt;return r._=t,n(r,e)._}finally{oc=Date}}}function Ot(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.substring(c,a)),null!=(u=cc[e=n.charAt(++a)])&&(e=n.charAt(++a)),(i=A[e])&&(e=i(t,null==u?"e"===e?" ":"0":u)),o.push(e),c=a+1);return o.push(n.substring(c,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},u=e(r,n,t,0);if(u!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var i=null!=r.Z&&oc!==jt,o=new(i?jt:oc);return"j"in r?o.setFullYear(r.y,0,r.j):"w"in r&&("W"in r||"U"in r)?(o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+Math.floor(r.Z/100),r.M+r.Z%100,r.S,r.L),i?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var u,i,o,a=0,c=t.length,s=e.length;c>a;){if(r>=s)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=C[o in cc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){b.lastIndex=0;var r=b.exec(t.substring(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){M.lastIndex=0;var r=M.exec(t.substring(e));return r?(n.w=_.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){E.lastIndex=0;var r=E.exec(t.substring(e));return r?(n.m=N.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.substring(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function c(n,t,r){return e(n,A.x.toString(),t,r)}function s(n,t,r){return e(n,A.X.toString(),t,r)}function l(n,t,e){var r=x.get(t.substring(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{oc=jt;var t=new oc;return t._=n,r(t)}finally{oc=Date}}var r=t(n);return e.parse=function(n){try{oc=jt;var t=r.parse(n);return t&&t._}finally{oc=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ae;var x=Go.map(),M=It(v),_=Zt(v),b=It(d),w=Zt(d),S=It(m),k=Zt(m),E=It(y),N=Zt(y);p.forEach(function(n,t){x.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return Yt(n.getDate(),t,2)},e:function(n,t){return Yt(n.getDate(),t,2)},H:function(n,t){return Yt(n.getHours(),t,2)},I:function(n,t){return Yt(n.getHours()%12||12,t,2)},j:function(n,t){return Yt(1+ic.dayOfYear(n),t,3)},L:function(n,t){return Yt(n.getMilliseconds(),t,3)},m:function(n,t){return Yt(n.getMonth()+1,t,2)},M:function(n,t){return Yt(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return Yt(n.getSeconds(),t,2)},U:function(n,t){return Yt(ic.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Yt(ic.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return Yt(n.getFullYear()%100,t,2)},Y:function(n,t){return Yt(n.getFullYear()%1e4,t,4)},Z:ie,"%":function(){return"%"}},C={a:r,A:u,b:i,B:o,c:a,d:Qt,e:Qt,H:te,I:te,j:ne,L:ue,m:Kt,M:ee,p:l,S:re,U:$t,w:Vt,W:Xt,x:c,X:s,y:Jt,Y:Bt,Z:Wt,"%":oe};return t}function Yt(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function It(n){return new RegExp("^(?:"+n.map(Go.requote).join("|")+")","i")}function Zt(n){for(var t=new o,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function Vt(n,t,e){sc.lastIndex=0;var r=sc.exec(t.substring(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function $t(n,t,e){sc.lastIndex=0;var r=sc.exec(t.substring(e));return r?(n.U=+r[0],e+r[0].length):-1}function Xt(n,t,e){sc.lastIndex=0;var r=sc.exec(t.substring(e));return r?(n.W=+r[0],e+r[0].length):-1}function Bt(n,t,e){sc.lastIndex=0;var r=sc.exec(t.substring(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Jt(n,t,e){sc.lastIndex=0;var r=sc.exec(t.substring(e,e+2));return r?(n.y=Gt(+r[0]),e+r[0].length):-1}function Wt(n,t,e){return/^[+-]\d{4}$/.test(t=t.substring(e,e+5))?(n.Z=+t,e+5):-1}function Gt(n){return n+(n>68?1900:2e3)}function Kt(n,t,e){sc.lastIndex=0;var r=sc.exec(t.substring(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Qt(n,t,e){sc.lastIndex=0;var r=sc.exec(t.substring(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function ne(n,t,e){sc.lastIndex=0;var r=sc.exec(t.substring(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function te(n,t,e){sc.lastIndex=0;var r=sc.exec(t.substring(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function ee(n,t,e){sc.lastIndex=0;var r=sc.exec(t.substring(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function re(n,t,e){sc.lastIndex=0;var r=sc.exec(t.substring(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ue(n,t,e){sc.lastIndex=0;var r=sc.exec(t.substring(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ie(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=~~(fa(t)/60),u=fa(t)%60;return e+Yt(r,"0",2)+Yt(u,"0",2)}function oe(n,t,e){lc.lastIndex=0;var r=lc.exec(t.substring(e,e+1));return r?e+r[0].length:-1}function ae(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function ce(){}function se(n,t,e){var r=e.s=n+t,u=r-n,i=r-u;e.t=n-i+(t-u)}function le(n,t){n&&pc.hasOwnProperty(n.type)&&pc[n.type](n,t)}function fe(n,t,e){var r,u=-1,i=n.length-e;for(t.lineStart();++u<i;)r=n[u],t.point(r[0],r[1],r[2]);t.lineEnd()}function he(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)fe(n[e],t,1);t.polygonEnd()}function ge(){function n(n,t){n*=za,t=t*za/2+Aa/4;var e=n-r,o=e>=0?1:-1,a=o*e,c=Math.cos(t),s=Math.sin(t),l=i*s,f=u*c+l*Math.cos(a),h=l*o*Math.sin(a);dc.add(Math.atan2(h,f)),r=n,u=c,i=s}var t,e,r,u,i;mc.point=function(o,a){mc.point=n,r=(t=o)*za,u=Math.cos(a=(e=a)*za/2+Aa/4),i=Math.sin(a)},mc.lineEnd=function(){n(t,e)}}function pe(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function ve(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function de(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function me(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ye(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function xe(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function Me(n){return[Math.atan2(n[1],n[0]),G(n[2])]}function _e(n,t){return fa(n[0]-t[0])<Ta&&fa(n[1]-t[1])<Ta}function be(n,t){n*=za;var e=Math.cos(t*=za);we(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function we(n,t,e){++yc,Mc+=(n-Mc)/yc,_c+=(t-_c)/yc,bc+=(e-bc)/yc}function Se(){function n(n,u){n*=za;var i=Math.cos(u*=za),o=i*Math.cos(n),a=i*Math.sin(n),c=Math.sin(u),s=Math.atan2(Math.sqrt((s=e*c-r*a)*s+(s=r*o-t*c)*s+(s=t*a-e*o)*s),t*o+e*a+r*c);xc+=s,wc+=s*(t+(t=o)),Sc+=s*(e+(e=a)),kc+=s*(r+(r=c)),we(t,e,r)}var t,e,r;Cc.point=function(u,i){u*=za;var o=Math.cos(i*=za);t=o*Math.cos(u),e=o*Math.sin(u),r=Math.sin(i),Cc.point=n,we(t,e,r)}}function ke(){Cc.point=be}function Ee(){function n(n,t){n*=za;var e=Math.cos(t*=za),o=e*Math.cos(n),a=e*Math.sin(n),c=Math.sin(t),s=u*c-i*a,l=i*o-r*c,f=r*a-u*o,h=Math.sqrt(s*s+l*l+f*f),g=r*o+u*a+i*c,p=h&&-W(g)/h,v=Math.atan2(h,g);Ec+=p*s,Nc+=p*l,Ac+=p*f,xc+=v,wc+=v*(r+(r=o)),Sc+=v*(u+(u=a)),kc+=v*(i+(i=c)),we(r,u,i)}var t,e,r,u,i;Cc.point=function(o,a){t=o,e=a,Cc.point=n,o*=za;var c=Math.cos(a*=za);r=c*Math.cos(o),u=c*Math.sin(o),i=Math.sin(a),we(r,u,i)},Cc.lineEnd=function(){n(t,e),Cc.lineEnd=ke,Cc.point=be}}function Ne(){return!0}function Ae(n,t,e,r,u){var i=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(_e(e,r)){u.lineStart();for(var a=0;t>a;++a)u.point((e=n[a])[0],e[1]);return u.lineEnd(),void 0}var c=new Le(e,n,null,!0),s=new Le(e,null,c,!1);c.o=s,i.push(c),o.push(s),c=new Le(r,n,null,!1),s=new Le(r,null,c,!0),c.o=s,i.push(c),o.push(s)}}),o.sort(t),Ce(i),Ce(o),i.length){for(var a=0,c=e,s=o.length;s>a;++a)o[a].e=c=!c;for(var l,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;l=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,s=l.length;s>a;++a)u.point((f=l[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){l=g.p.z;for(var a=l.length-1;a>=0;--a)u.point((f=l[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,l=g.z,p=!p}while(!g.v);u.lineEnd()}}}function Ce(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r<t;)u.n=e=n[r],e.p=u,u=e;u.n=e=n[0],e.p=u}}function Le(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Te(n,t,e,r){return function(u,i){function o(t,e){var r=u(t,e);n(t=r[0],e=r[1])&&i.point(t,e)}function a(n,t){var e=u(n,t);d.point(e[0],e[1])}function c(){y.point=a,d.lineStart()}function s(){y.point=o,d.lineEnd()}function l(n,t){v.push([n,t]);var e=u(n,t);M.point(e[0],e[1])}function f(){M.lineStart(),v=[]}function h(){l(v[0][0],v[0][1]),M.lineEnd();var n,t=M.clean(),e=x.buffer(),r=e.length;if(v.pop(),p.push(v),v=null,r){if(1&t){n=e[0];var u,r=n.length-1,o=-1;for(i.lineStart();++o<r;)i.point((u=n[o])[0],u[1]);return i.lineEnd(),void 0}r>1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(qe))}}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:s,polygonStart:function(){y.point=l,y.lineStart=f,y.lineEnd=h,g=[],p=[],i.polygonStart()},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=s,g=Go.merge(g);var n=De(m,p);g.length?Ae(g,Re,n,e,i):n&&(i.lineStart(),e(null,null,1,i),i.lineEnd()),i.polygonEnd(),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},x=ze(),M=t(x);return y}}function qe(n){return n.length>1}function ze(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:v,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Re(n,t){return((n=n.x)[0]<0?n[1]-La-Ta:La-n[1])-((t=t.x)[0]<0?t[1]-La-Ta:La-t[1])}function De(n,t){var e=n[0],r=n[1],u=[Math.sin(e),-Math.cos(e),0],i=0,o=0;dc.reset();for(var a=0,c=t.length;c>a;++a){var s=t[a],l=s.length;if(l)for(var f=s[0],h=f[0],g=f[1]/2+Aa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===l&&(d=0),n=s[d];var m=n[0],y=n[1]/2+Aa/4,x=Math.sin(y),M=Math.cos(y),_=m-h,b=_>=0?1:-1,w=b*_,S=w>Aa,k=p*x;if(dc.add(Math.atan2(k*b*Math.sin(w),v*M+k*Math.cos(w))),i+=S?_+b*Ca:_,S^h>=e^m>=e){var E=de(pe(f),pe(n));xe(E);var N=de(u,E);xe(N);var A=(S^_>=0?-1:1)*G(N[2]);(r>A||r===A&&(E[0]||E[1]))&&(o+=S^_>=0?1:-1)}if(!d++)break;h=m,p=x,v=M,f=n}}return(-Ta>i||Ta>i&&0>dc)^1&o}function Pe(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?Aa:-Aa,c=fa(i-e);fa(c-Aa)<Ta?(n.point(e,r=(r+o)/2>0?La:-La),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=Aa&&(fa(e-u)<Ta&&(e-=u*Ta),fa(i-a)<Ta&&(i-=a*Ta),r=Ue(e,r,i,o),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=i,r=o),u=a},lineEnd:function(){n.lineEnd(),e=r=0/0},clean:function(){return 2-t}}}function Ue(n,t,e,r){var u,i,o=Math.sin(n-e);return fa(o)>Ta?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function je(n,t,e,r){var u;if(null==n)u=e*La,r.point(-Aa,u),r.point(0,u),r.point(Aa,u),r.point(Aa,0),r.point(Aa,-u),r.point(0,-u),r.point(-Aa,-u),r.point(-Aa,0),r.point(-Aa,u);else if(fa(n[0]-t[0])>Ta){var i=n[0]<t[0]?Aa:-Aa;u=e*i/2,r.point(-i,u),r.point(0,u),r.point(i,u)}else r.point(t[0],t[1])}function He(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,s,l;return{lineStart:function(){s=c=!1,l=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?Aa:-Aa),h):0;if(!e&&(s=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(_e(e,g)||_e(p,g))&&(p[0]+=Ta,p[1]+=Ta,v=t(p[0],p[1]))),v!==c)l=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(l=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&_e(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return l|(s&&c)<<1}}}function r(n,t,e){var r=pe(n),u=pe(t),o=[1,0,0],a=de(r,u),c=ve(a,a),s=a[0],l=c-s*s;if(!l)return!e&&n;var f=i*c/l,h=-i*s/l,g=de(o,a),p=ye(o,f),v=ye(a,h);me(p,v);var d=g,m=ve(p,d),y=ve(d,d),x=m*m-y*(ve(p,p)-1);if(!(0>x)){var M=Math.sqrt(x),_=ye(d,(-m-M)/y);if(me(_,p),_=Me(_),!e)return _;var b,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(b=w,w=S,S=b);var N=S-w,A=fa(N-Aa)<Ta,C=A||Ta>N;if(!A&&k>E&&(b=k,k=E,E=b),C?A?k+E>0^_[1]<(fa(_[0]-w)<Ta?k:E):k<=_[1]&&_[1]<=E:N>Aa^(w<=_[0]&&_[0]<=S)){var L=ye(d,(-m+M)/y);return me(L,p),[_,Me(L)]}}}function u(t,e){var r=o?n:Aa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=fa(i)>Ta,c=gr(n,6*za);return Te(t,e,c,o?[0,-n]:[-Aa,n-Aa])}function Fe(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,s=o.y,l=a.x,f=a.y,h=0,g=1,p=l-c,v=f-s;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-s,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-s,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:s+h*v}),1>g&&(u.b={x:c+g*p,y:s+g*v}),u}}}}}}function Oe(n,t,e,r){function u(r,u){return fa(r[0]-n)<Ta?u>0?0:3:fa(r[0]-e)<Ta?u>0?2:1:fa(r[1]-t)<Ta?u>0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,s=a[0];c>o;++o)i=a[o],s[1]<=r?i[1]>r&&J(s,i,n)>0&&++t:i[1]<=r&&J(s,i,n)<0&&--t,s=i;return 0!==t}function s(i,a,c,s){var l=0,f=0;if(null==i||(l=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do s.point(0===l||3===l?n:e,l>1?r:t);while((l=(l+c+4)%4)!==f)}else s.point(a[0],a[1])}function l(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){l(n,t)&&a.point(n,t)}function h(){C.point=p,d&&d.push(m=[]),S=!0,w=!1,_=b=0/0}function g(){v&&(p(y,x),M&&w&&N.rejoin(),v.push(N.buffer())),C.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Tc,Math.min(Tc,n)),t=Math.max(-Tc,Math.min(Tc,t));var e=l(n,t);if(d&&m.push([n,t]),S)y=n,x=t,M=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:_,y:b},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}_=n,b=t,w=e}var v,d,m,y,x,M,_,b,w,S,k,E=a,N=ze(),A=Fe(n,t,e,r),C={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=N,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=Go.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),s(null,null,1,a),a.lineEnd()),u&&Ae(v,i,t,s,a),a.polygonEnd()),v=d=m=null}};return C}}function Ye(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function Ie(n){var t=0,e=Aa/3,r=ir(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*Aa/180,e=n[1]*Aa/180):[180*(t/Aa),180*(e/Aa)]},u}function Ze(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,G((i-(n*n+e*e)*u*u)/(2*u))]},e}function Ve(){function n(n,t){zc+=u*n-r*t,r=n,u=t}var t,e,r,u;jc.point=function(i,o){jc.point=n,t=r=i,e=u=o},jc.lineEnd=function(){n(t,e)}}function $e(n,t){Rc>n&&(Rc=n),n>Pc&&(Pc=n),Dc>t&&(Dc=t),t>Uc&&(Uc=t)}function Xe(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=Be(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Be(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Be(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Je(n,t){Mc+=n,_c+=t,++bc}function We(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);wc+=o*(t+n)/2,Sc+=o*(e+r)/2,kc+=o,Je(t=n,e=r)}var t,e;Fc.point=function(r,u){Fc.point=n,Je(t=r,e=u)}}function Ge(){Fc.point=Je}function Ke(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);wc+=o*(r+n)/2,Sc+=o*(u+t)/2,kc+=o,o=u*n-r*t,Ec+=o*(r+n),Nc+=o*(u+t),Ac+=3*o,Je(r=n,u=t)}var t,e,r,u;Fc.point=function(i,o){Fc.point=n,Je(t=r=i,e=u=o)},Fc.lineEnd=function(){n(t,e)}}function Qe(n){function t(t,e){n.moveTo(t,e),n.arc(t,e,o,0,Ca)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:v};return a}function nr(n){function t(n){return(a?r:e)(n)}function e(t){return rr(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){x=0/0,S.point=i,t.lineStart()}function i(e,r){var i=pe([e,r]),o=n(e,r);u(x,M,y,_,b,w,x=o[0],M=o[1],y=e,_=i[0],b=i[1],w=i[2],a,t),t.point(x,M)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=s,S.lineEnd=l}function s(n,t){i(f=n,h=t),g=x,p=M,v=_,d=b,m=w,S.point=i}function l(){u(x,M,y,_,b,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,x,M,_,b,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,s,l,f,h,g,p,v,d,m){var y=l-t,x=f-e,M=y*y+x*x;if(M>4*i&&d--){var _=a+g,b=c+p,w=s+v,S=Math.sqrt(_*_+b*b+w*w),k=Math.asin(w/=S),E=fa(fa(w)-1)<Ta||fa(r-h)<Ta?(r+h)/2:Math.atan2(b,_),N=n(E,k),A=N[0],C=N[1],L=A-t,T=C-e,q=x*L-y*T;(q*q/M>i||fa((y*L+x*T)/M-.5)>.3||o>a*g+c*p+s*v)&&(u(t,e,r,a,c,s,A,C,E,_/=S,b/=S,w,d,m),m.point(A,C),u(A,C,E,_,b,w,l,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*za),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function tr(n){var t=nr(function(t,e){return n([t*Ra,e*Ra])});return function(n){return or(t(n))}}function er(n){this.stream=n}function rr(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function ur(n){return ir(function(){return n})()}function ir(n){function t(n){return n=a(n[0]*za,n[1]*za),[n[0]*h+c,s-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(s-n[1])/h),n&&[n[0]*Ra,n[1]*Ra]}function r(){a=Ye(o=sr(m,y,x),i);var n=i(v,d);return c=g-n[0]*h,s=p+n[1]*h,u()}function u(){return l&&(l.valid=!1,l=null),t}var i,o,a,c,s,l,f=nr(function(n,t){return n=i(n,t),[n[0]*h+c,s-n[1]*h] +}),h=150,g=480,p=250,v=0,d=0,m=0,y=0,x=0,M=Lc,_=Nt,b=null,w=null;return t.stream=function(n){return l&&(l.valid=!1),l=or(M(o,f(_(n)))),l.valid=!0,l},t.clipAngle=function(n){return arguments.length?(M=null==n?(b=n,Lc):He((b=+n)*za),u()):b},t.clipExtent=function(n){return arguments.length?(w=n,_=n?Oe(n[0][0],n[0][1],n[1][0],n[1][1]):Nt,u()):w},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*za,d=n[1]%360*za,r()):[v*Ra,d*Ra]},t.rotate=function(n){return arguments.length?(m=n[0]%360*za,y=n[1]%360*za,x=n.length>2?n[2]%360*za:0,r()):[m*Ra,y*Ra,x*Ra]},Go.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function or(n){return rr(n,function(t,e){n.point(t*za,e*za)})}function ar(n,t){return[n,t]}function cr(n,t){return[n>Aa?n-Ca:-Aa>n?n+Ca:n,t]}function sr(n,t,e){return n?t||e?Ye(fr(n),hr(t,e)):fr(n):t||e?hr(t,e):cr}function lr(n){return function(t,e){return t+=n,[t>Aa?t-Ca:-Aa>t?t+Ca:t,e]}}function fr(n){var t=lr(n);return t.invert=lr(-n),t}function hr(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*r+a*u;return[Math.atan2(c*i-l*o,a*r-s*u),G(l*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*i-c*o;return[Math.atan2(c*i+s*o,a*r+l*u),G(l*r-a*u)]},e}function gr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=pr(e,u),i=pr(e,i),(o>0?i>u:u>i)&&(u+=o*Ca)):(u=n+o*Ca,i=n-.5*c);for(var s,l=u;o>0?l>i:i>l;l-=c)a.point((s=Me([e,-r*Math.cos(l),-r*Math.sin(l)]))[0],s[1])}}function pr(n,t){var e=pe(t);e[0]-=n,xe(e);var r=W(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Ta)%(2*Math.PI)}function vr(n,t,e){var r=Go.range(n,t-Ta,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function dr(n,t,e){var r=Go.range(n,t-Ta,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function mr(n){return n.source}function yr(n){return n.target}function xr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),s=u*Math.sin(n),l=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(tt(r-t)+u*o*tt(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*l,u=e*s+t*f,o=e*i+t*a;return[Math.atan2(u,r)*Ra,Math.atan2(o,Math.sqrt(r*r+u*u))*Ra]}:function(){return[n*Ra,t*Ra]};return p.distance=h,p}function Mr(){function n(n,u){var i=Math.sin(u*=za),o=Math.cos(u),a=fa((n*=za)-t),c=Math.cos(a);Oc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;Yc.point=function(u,i){t=u*za,e=Math.sin(i*=za),r=Math.cos(i),Yc.point=n},Yc.lineEnd=function(){Yc.point=Yc.lineEnd=v}}function _r(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function br(n,t){function e(n,t){o>0?-La+Ta>t&&(t=-La+Ta):t>La-Ta&&(t=La-Ta);var e=o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(Aa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=B(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-La]},e):Sr}function wr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return fa(u)<Ta?ar:(e.invert=function(n,t){var e=i-t;return[Math.atan2(n,e)/u,i-B(u)*Math.sqrt(n*n+e*e)]},e)}function Sr(n,t){return[n,Math.log(Math.tan(Aa/4+t/2))]}function kr(n){var t,e=ur(n),r=e.scale,u=e.translate,i=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=u.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=i.apply(e,arguments);if(o===e){if(t=null==n){var a=Aa*r(),c=u();i([[c[0]-a,c[1]-a],[c[0]+a,c[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function Er(n,t){return[Math.log(Math.tan(Aa/4+t/2)),-n]}function Nr(n){return n[0]}function Ar(n){return n[1]}function Cr(n){for(var t=n.length,e=[0,1],r=2,u=2;t>u;u++){for(;r>1&&J(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function Lr(n,t){return n[0]-t[0]||n[1]-t[1]}function Tr(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function qr(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],s=e[1],l=t[1]-c,f=r[1]-s,h=(a*(c-s)-f*(u-i))/(f*o-a*l);return[u+h*o,c+h*l]}function zr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Rr(){tu(this),this.edge=this.site=this.circle=null}function Dr(n){var t=ns.pop()||new Rr;return t.site=n,t}function Pr(n){$r(n),Gc.remove(n),ns.push(n),tu(n)}function Ur(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Pr(n);for(var c=i;c.circle&&fa(e-c.circle.x)<Ta&&fa(r-c.circle.cy)<Ta;)i=c.P,a.unshift(c),Pr(c),c=i;a.unshift(c),$r(c);for(var s=o;s.circle&&fa(e-s.circle.x)<Ta&&fa(r-s.circle.cy)<Ta;)o=s.N,a.push(s),Pr(s),s=o;a.push(s),$r(s);var l,f=a.length;for(l=1;f>l;++l)s=a[l],c=a[l-1],Kr(s.edge,c.site,s.site,u);c=a[0],s=a[f-1],s.edge=Wr(c.site,s.site,null,u),Vr(c),Vr(s)}function jr(n){for(var t,e,r,u,i=n.x,o=n.y,a=Gc._;a;)if(r=Hr(a,o)-i,r>Ta)a=a.L;else{if(u=i-Fr(a,o),!(u>Ta)){r>-Ta?(t=a.P,e=a):u>-Ta?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Dr(n);if(Gc.insert(t,c),t||e){if(t===e)return $r(t),e=Dr(t.site),Gc.insert(c,e),c.edge=e.edge=Wr(t.site,c.site),Vr(t),Vr(e),void 0;if(!e)return c.edge=Wr(t.site,c.site),void 0;$r(t),$r(e);var s=t.site,l=s.x,f=s.y,h=n.x-l,g=n.y-f,p=e.site,v=p.x-l,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,x=v*v+d*d,M={x:(d*y-g*x)/m+l,y:(h*x-v*y)/m+f};Kr(e.edge,s,p,M),c.edge=Wr(s,n,null,M),e.edge=Wr(n,p,null,M),Vr(t),Vr(e)}}function Hr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,s=c-t;if(!s)return a;var l=a-r,f=1/i-1/s,h=l/s;return f?(-h+Math.sqrt(h*h-2*f*(l*l/(-2*s)-c+s/2+u-i/2)))/f+r:(r+a)/2}function Fr(n,t){var e=n.N;if(e)return Hr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Or(n){this.site=n,this.edges=[]}function Yr(n){for(var t,e,r,u,i,o,a,c,s,l,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Wc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)l=a[o].end(),r=l.x,u=l.y,s=a[++o%c].start(),t=s.x,e=s.y,(fa(r-t)>Ta||fa(u-e)>Ta)&&(a.splice(o,0,new Qr(Gr(i.site,l,fa(r-f)<Ta&&p-u>Ta?{x:f,y:fa(t-f)<Ta?e:p}:fa(u-p)<Ta&&h-r>Ta?{x:fa(e-p)<Ta?t:h,y:p}:fa(r-h)<Ta&&u-g>Ta?{x:h,y:fa(t-h)<Ta?e:g}:fa(u-g)<Ta&&r-f>Ta?{x:fa(e-g)<Ta?t:f,y:g}:null),i.site,null)),++c)}function Ir(n,t){return t.angle-n.angle}function Zr(){tu(this),this.x=this.y=this.arc=this.site=this.cy=null}function Vr(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,u=n.site,i=e.site;if(r!==i){var o=u.x,a=u.y,c=r.x-o,s=r.y-a,l=i.x-o,f=i.y-a,h=2*(c*f-s*l);if(!(h>=-qa)){var g=c*c+s*s,p=l*l+f*f,v=(f*g-s*p)/h,d=(c*p-l*g)/h,f=d+a,m=ts.pop()||new Zr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,x=Qc._;x;)if(m.y<x.y||m.y===x.y&&m.x<=x.x){if(!x.L){y=x.P;break}x=x.L}else{if(!x.R){y=x;break}x=x.R}Qc.insert(y,m),y||(Kc=m)}}}}function $r(n){var t=n.circle;t&&(t.P||(Kc=t.N),Qc.remove(t),ts.push(t),tu(t),n.circle=null)}function Xr(n){for(var t,e=Jc,r=Fe(n[0][0],n[0][1],n[1][0],n[1][1]),u=e.length;u--;)t=e[u],(!Br(t,n)||!r(t)||fa(t.a.x-t.b.x)<Ta&&fa(t.a.y-t.b.y)<Ta)&&(t.a=t.b=null,e.splice(u,1))}function Br(n,t){var e=n.b;if(e)return!0;var r,u,i=n.a,o=t[0][0],a=t[1][0],c=t[0][1],s=t[1][1],l=n.l,f=n.r,h=l.x,g=l.y,p=f.x,v=f.y,d=(h+p)/2,m=(g+v)/2;if(v===g){if(o>d||d>=a)return;if(h>p){if(i){if(i.y>=s)return}else i={x:d,y:c};e={x:d,y:s}}else{if(i){if(i.y<c)return}else i={x:d,y:s};e={x:d,y:c}}}else if(r=(h-p)/(v-g),u=m-r*d,-1>r||r>1)if(h>p){if(i){if(i.y>=s)return}else i={x:(c-u)/r,y:c};e={x:(s-u)/r,y:s}}else{if(i){if(i.y<c)return}else i={x:(s-u)/r,y:s};e={x:(c-u)/r,y:c}}else if(v>g){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.x<o)return}else i={x:a,y:r*a+u};e={x:o,y:r*o+u}}return n.a=i,n.b=e,!0}function Jr(n,t){this.l=n,this.r=t,this.a=this.b=null}function Wr(n,t,e,r){var u=new Jr(n,t);return Jc.push(u),e&&Kr(u,n,t,e),r&&Kr(u,t,n,r),Wc[n.i].edges.push(new Qr(u,n,t)),Wc[t.i].edges.push(new Qr(u,t,n)),u}function Gr(n,t,e){var r=new Jr(n,null);return r.a=t,r.b=e,Jc.push(r),r}function Kr(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function Qr(n,t,e){var r=n.a,u=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(u.x-r.x,r.y-u.y):Math.atan2(r.x-u.x,u.y-r.y)}function nu(){this._=null}function tu(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function eu(n,t){var e=t,r=t.R,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function ru(n,t){var e=t,r=t.L,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function uu(n){for(;n.L;)n=n.L;return n}function iu(n,t){var e,r,u,i=n.sort(ou).pop();for(Jc=[],Wc=new Array(n.length),Gc=new nu,Qc=new nu;;)if(u=Kc,i&&(!u||i.y<u.y||i.y===u.y&&i.x<u.x))(i.x!==e||i.y!==r)&&(Wc[i.i]=new Or(i),jr(i),e=i.x,r=i.y),i=n.pop();else{if(!u)break;Ur(u.arc)}t&&(Xr(t),Yr(t));var o={cells:Wc,edges:Jc};return Gc=Qc=Jc=Wc=null,o}function ou(n,t){return t.y-n.y||t.x-n.x}function au(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function cu(n){return n.x}function su(n){return n.y}function lu(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function fu(n,t,e,r,u,i){if(!n(t,e,r,u,i)){var o=.5*(e+u),a=.5*(r+i),c=t.nodes;c[0]&&fu(n,c[0],e,r,o,a),c[1]&&fu(n,c[1],o,r,u,a),c[2]&&fu(n,c[2],e,a,o,i),c[3]&&fu(n,c[3],o,a,u,i)}}function hu(n,t){n=Go.rgb(n),t=Go.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+Mt(Math.round(e+i*n))+Mt(Math.round(r+o*n))+Mt(Math.round(u+a*n))}}function gu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=du(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function pu(n,t){return t-=n=+n,function(e){return n+t*e}}function vu(n,t){var e,r,u,i,o,a=0,c=0,s=[],l=[];for(n+="",t+="",rs.lastIndex=0,r=0;e=rs.exec(t);++r)e.index&&s.push(t.substring(a,c=e.index)),l.push({i:s.length,x:e[0]}),s.push(null),a=rs.lastIndex;for(a<t.length&&s.push(t.substring(a)),r=0,i=l.length;(e=rs.exec(n))&&i>r;++r)if(o=l[r],o.x==e[0]){if(o.i)if(null==s[o.i+1])for(s[o.i-1]+=o.x,s.splice(o.i,1),u=r+1;i>u;++u)l[u].i--;else for(s[o.i-1]+=o.x+s[o.i+1],s.splice(o.i,2),u=r+1;i>u;++u)l[u].i-=2;else if(null==s[o.i+1])s[o.i]=o.x;else for(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1),u=r+1;i>u;++u)l[u].i--;l.splice(r,1),i--,r--}else o.x=pu(parseFloat(e[0]),parseFloat(o.x));for(;i>r;)o=l.pop(),null==s[o.i+1]?s[o.i]=o.x:(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1)),i--;return 1===s.length?null==s[0]?(o=l[0].x,function(n){return o(n)+""}):function(){return t}:function(n){for(r=0;i>r;++r)s[(o=l[r]).i]=o.x(n);return s.join("")}}function du(n,t){for(var e,r=Go.interpolators.length;--r>=0&&!(e=Go.interpolators[r](n,t)););return e}function mu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(du(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function yu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function xu(n){return function(t){return 1-n(1-t)}}function Mu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function _u(n){return n*n}function bu(n){return n*n*n}function wu(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Su(n){return function(t){return Math.pow(t,n)}}function ku(n){return 1-Math.cos(n*La)}function Eu(n){return Math.pow(2,10*(n-1))}function Nu(n){return 1-Math.sqrt(1-n*n)}function Au(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ca*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ca/t)}}function Cu(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Lu(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Tu(n,t){n=Go.hcl(n),t=Go.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return ct(e+i*n,r+o*n,u+a*n)+""}}function qu(n,t){n=Go.hsl(n),t=Go.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return it(e+i*n,r+o*n,u+a*n)+""}}function zu(n,t){n=Go.lab(n),t=Go.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ft(e+i*n,r+o*n,u+a*n)+""}}function Ru(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Du(n){var t=[n.a,n.b],e=[n.c,n.d],r=Uu(t),u=Pu(t,e),i=Uu(ju(e,t,-u))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,u*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*Ra,this.translate=[n.e,n.f],this.scale=[r,i],this.skew=i?Math.atan2(u,i)*Ra:0}function Pu(n,t){return n[0]*t[0]+n[1]*t[1]}function Uu(n){var t=Math.sqrt(Pu(n,n));return t&&(n[0]/=t,n[1]/=t),t}function ju(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Hu(n,t){var e,r=[],u=[],i=Go.transform(n),o=Go.transform(t),a=i.translate,c=o.translate,s=i.rotate,l=o.rotate,f=i.skew,h=o.skew,g=i.scale,p=o.scale;return a[0]!=c[0]||a[1]!=c[1]?(r.push("translate(",null,",",null,")"),u.push({i:1,x:pu(a[0],c[0])},{i:3,x:pu(a[1],c[1])})):c[0]||c[1]?r.push("translate("+c+")"):r.push(""),s!=l?(s-l>180?l+=360:l-s>180&&(s+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:pu(s,l)})):l&&r.push(r.pop()+"rotate("+l+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:pu(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:pu(g[0],p[0])},{i:e-2,x:pu(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i<e;)r[(t=u[i]).i]=t.x(n);return r.join("")}}function Fu(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return(e-n)*t}}function Ou(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return Math.max(0,Math.min(1,(e-n)*t))}}function Yu(n){for(var t=n.source,e=n.target,r=Zu(t,e),u=[t];t!==r;)t=t.parent,u.push(t);for(var i=u.length;e!==r;)u.splice(i,0,e),e=e.parent;return u}function Iu(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Zu(n,t){if(n===t)return n;for(var e=Iu(n),r=Iu(t),u=e.pop(),i=r.pop(),o=null;u===i;)o=u,u=e.pop(),i=r.pop();return o}function Vu(n){n.fixed|=2}function $u(n){n.fixed&=-7}function Xu(n){n.fixed|=4,n.px=n.x,n.py=n.y}function Bu(n){n.fixed&=-5}function Ju(n,t,e){var r=0,u=0;if(n.charge=0,!n.leaf)for(var i,o=n.nodes,a=o.length,c=-1;++c<a;)i=o[c],null!=i&&(Ju(i,t,e),n.charge+=i.charge,r+=i.charge*i.cx,u+=i.charge*i.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var s=t*e[n.point.index];n.charge+=n.pointCharge=s,r+=s*n.point.x,u+=s*n.point.y}n.cx=r/n.charge,n.cy=u/n.charge}function Wu(n,t){return Go.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=ni,n}function Gu(n){return n.children}function Ku(n){return n.value}function Qu(n,t){return t.value-n.value}function ni(n){return Go.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function ti(n){return n.x}function ei(n){return n.y}function ri(n,t,e){n.y0=t,n.y=e}function ui(n){return Go.range(n.length)}function ii(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function oi(n){for(var t,e=1,r=0,u=n[0][1],i=n.length;i>e;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function ai(n){return n.reduce(ci,0)}function ci(n,t){return n+t[1]}function si(n,t){return li(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function li(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function fi(n){return[Go.min(n),Go.max(n)]}function hi(n,t){return n.parent==t.parent?1:2}function gi(n){var t=n.children;return t&&t.length?t[0]:n._tree.thread}function pi(n){var t,e=n.children;return e&&(t=e.length)?e[t-1]:n._tree.thread}function vi(n,t){var e=n.children;if(e&&(u=e.length))for(var r,u,i=-1;++i<u;)t(r=vi(e[i],t),n)>0&&(n=r);return n}function di(n,t){return n.x-t.x}function mi(n,t){return t.x-n.x}function yi(n,t){return n.depth-t.depth}function xi(n,t){function e(n,r){var u=n.children;if(u&&(o=u.length))for(var i,o,a=null,c=-1;++c<o;)i=u[c],e(i,a),a=i;t(n,r)}e(n,null)}function Mi(n){for(var t,e=0,r=0,u=n.children,i=u.length;--i>=0;)t=u[i]._tree,t.prelim+=e,t.mod+=e,e+=t.shift+(r+=t.change)}function _i(n,t,e){n=n._tree,t=t._tree;var r=e/(t.number-n.number);n.change+=r,t.change-=r,t.shift+=e,t.prelim+=e,t.mod+=e}function bi(n,t,e){return n._tree.ancestor.parent==t.parent?n._tree.ancestor:e}function wi(n,t){return n.value-t.value}function Si(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function ki(n,t){n._pack_next=t,t._pack_prev=n}function Ei(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function Ni(n){function t(n){l=Math.min(n.x-n.r,l),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(s=e.length)){var e,r,u,i,o,a,c,s,l=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(Ai),r=e[0],r.x=-r.r,r.y=0,t(r),s>1&&(u=e[1],u.x=u.r,u.y=0,t(u),s>2))for(i=e[2],Ti(r,u,i),t(i),Si(r,i),r._pack_prev=i,Si(i,u),u=r._pack_next,o=3;s>o;o++){Ti(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(Ei(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!Ei(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.r<r.r?ki(r,u=a):ki(r=c,u),o--):(Si(r,i),u=i,t(i))}var m=(l+f)/2,y=(h+g)/2,x=0;for(o=0;s>o;o++)i=e[o],i.x-=m,i.y-=y,x=Math.max(x,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=x,e.forEach(Ci)}}function Ai(n){n._pack_next=n._pack_prev=n}function Ci(n){delete n._pack_next,delete n._pack_prev}function Li(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i<o;)Li(u[i],t,e,r)}function Ti(n,t,e){var r=n.r+e.r,u=t.x-n.x,i=t.y-n.y;if(r&&(u||i)){var o=t.r+e.r,a=u*u+i*i;o*=o,r*=r;var c=.5+(r-o)/(2*a),s=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+c*u+s*i,e.y=n.y+c*i-s*u}else e.x=n.x+r,e.y=n.y}function qi(n){return 1+Go.max(n,function(n){return n.y})}function zi(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Ri(n){var t=n.children;return t&&t.length?Ri(t[0]):n}function Di(n){var t,e=n.children;return e&&(t=e.length)?Di(e[t-1]):n}function Pi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ui(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function ji(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Hi(n){return n.rangeExtent?n.rangeExtent():ji(n.range())}function Fi(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Oi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Yi(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ps}function Ii(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)u.push(e(n[o-1],n[o])),i.push(r(t[o-1],t[o]));return function(t){var e=Go.bisect(n,t,1,a)-1;return i[e](u[e](t))}}function Zi(n,t,e,r){function u(){var u=Math.min(n.length,t.length)>2?Ii:Fi,c=r?Ou:Fu;return o=u(n,t,c,e),a=u(t,n,c,du),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Ru)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Bi(n,t)},i.tickFormat=function(t,e){return Ji(n,t,e)},i.nice=function(t){return $i(n,t),u()},i.copy=function(){return Zi(n,t,e,r)},u()}function Vi(n,t){return Go.rebind(n,t,"range","rangeRound","interpolate","clamp")}function $i(n,t){return Oi(n,Yi(Xi(n,t)[2]))}function Xi(n,t){null==t&&(t=10);var e=ji(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Bi(n,t){return Go.range.apply(Go,Xi(n,t))}function Ji(n,t,e){var r=Xi(n,t);if(e){var u=rc.exec(e);if(u.shift(),"s"===u[8]){var i=Go.formatPrefix(Math.max(fa(r[0]),fa(r[1])));return u[7]||(u[7]="."+Wi(i.scale(r[2]))),u[8]="f",e=Go.format(u.join("")),function(n){return e(i.scale(n))+i.symbol}}u[7]||(u[7]="."+Gi(u[8],r)),e=u.join("")}else e=",."+Wi(r[2])+"f";return Go.format(e)}function Wi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Gi(n,t){var e=Wi(t[2]);return n in vs?Math.abs(e-Wi(Math.max(fa(t[0]),fa(t[1]))))+ +("e"!==n):e-2*("%"===n)}function Ki(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Oi(r.map(u),e?Math:ms);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=ji(r),o=[],a=n[0],c=n[1],s=Math.floor(u(a)),l=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(l-s)){if(e){for(;l>s;s++)for(var h=1;f>h;h++)o.push(i(s)*h);o.push(i(s))}else for(o.push(i(s));s++<l;)for(var h=f-1;h>0;h--)o.push(i(s)*h);for(s=0;o[s]<a;s++);for(l=o.length;o[l-1]>c;l--);o=o.slice(s,l)}return o},o.tickFormat=function(n,t){if(!arguments.length)return ds;arguments.length<2?t=ds:"function"!=typeof t&&(t=Go.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return Ki(n.copy(),t,e,r)},Vi(o,n)}function Qi(n,t,e){function r(t){return n(u(t))}var u=no(t),i=no(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Bi(e,n)},r.tickFormat=function(n,t){return Ji(e,n,t)},r.nice=function(n){return r.domain($i(e,n))},r.exponent=function(o){return arguments.length?(u=no(t=o),i=no(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Qi(n.copy(),t,e)},Vi(r,n)}function no(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function to(n,t){function e(e){return i[((u.get(e)||("range"===t.t?u.set(e,n.push(e)):0/0))-1)%i.length]}function r(t,e){return Go.range(n.length).map(function(n){return t+e*n})}var u,i,a;return e.domain=function(r){if(!arguments.length)return n;n=[],u=new o;for(var i,a=-1,c=r.length;++a<c;)u.has(i=r[a])||u.set(i,n.push(i));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(i=n,a=0,t={t:"range",a:arguments},e):i},e.rangePoints=function(u,o){arguments.length<2&&(o=0);var c=u[0],s=u[1],l=(s-c)/(Math.max(1,n.length-1)+o);return i=r(n.length<2?(c+s)/2:c+l*o/2,l),a=0,t={t:"rangePoints",a:arguments},e},e.rangeBands=function(u,o,c){arguments.length<2&&(o=0),arguments.length<3&&(c=o);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=(f-l)/(n.length-o+2*c);return i=r(l+h*c,h),s&&i.reverse(),a=h*(1-o),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(u,o,c){arguments.length<2&&(o=0),arguments.length<3&&(c=o);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=Math.floor((f-l)/(n.length-o+2*c)),g=f-l-(n.length-o)*h;return i=r(l+Math.round(g/2),h),s&&i.reverse(),a=Math.round(h*(1-o)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return a},e.rangeExtent=function(){return ji(t.a[0])},e.copy=function(){return to(n,t)},e.domain(n)}function eo(t,e){function r(){var n=0,r=e.length;for(i=[];++n<r;)i[n-1]=Go.quantile(t,n/r);return u}function u(n){return isNaN(n=+n)?void 0:e[Go.bisect(i,n)]}var i;return u.domain=function(e){return arguments.length?(t=e.filter(function(n){return!isNaN(n)}).sort(n),r()):t},u.range=function(n){return arguments.length?(e=n,r()):e},u.quantiles=function(){return i},u.invertExtent=function(n){return n=e.indexOf(n),0>n?[0/0,0/0]:[n>0?i[n-1]:t[0],n<i.length?i[n]:t[t.length-1]]},u.copy=function(){return eo(t,e)},r()}function ro(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(i*(t-n))))]}function u(){return i=e.length/(t-n),o=e.length-1,r}var i,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],u()):[n,t]},r.range=function(n){return arguments.length?(e=n,u()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return ro(n,t,e)},u()}function uo(n,t){function e(e){return e>=e?t[Go.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return uo(n,t)},e}function io(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Bi(n,t)},t.tickFormat=function(t,e){return Ji(n,t,e)},t.copy=function(){return io(n)},t}function oo(n){return n.innerRadius}function ao(n){return n.outerRadius}function co(n){return n.startAngle}function so(n){return n.endAngle}function lo(n){function t(t){function o(){s.push("M",i(n(l),a))}for(var c,s=[],l=[],f=-1,h=t.length,g=Et(e),p=Et(r);++f<h;)u.call(this,c=t[f],f)?l.push([+g.call(this,c,f),+p.call(this,c,f)]):l.length&&(o(),l=[]);return l.length&&o(),s.length?s.join(""):null}var e=Nr,r=Ar,u=Ne,i=fo,o=i.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(u=n,t):u},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?i=n:(i=Ss.get(n)||fo).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function fo(n){return n.join("L")}function ho(n){return fo(n)+"Z"}function go(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&u.push("H",r[0]),u.join("")}function po(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("V",(r=n[t])[1],"H",r[0]);return u.join("")}function vo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r=n[t])[0],"V",r[1]);return u.join("")}function mo(n,t){return n.length<4?fo(n):n[1]+Mo(n.slice(1,n.length-1),_o(n,t))}function yo(n,t){return n.length<3?fo(n):n[0]+Mo((n.push(n[0]),n),_o([n[n.length-2]].concat(n,[n[1]]),t))}function xo(n,t){return n.length<3?fo(n):n[0]+Mo(n,_o(n,t))}function Mo(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return fo(n);var e=n.length!=t.length,r="",u=n[0],i=n[1],o=t[0],a=o,c=1;if(e&&(r+="Q"+(i[0]-2*o[0]/3)+","+(i[1]-2*o[1]/3)+","+i[0]+","+i[1],u=n[1],c=2),t.length>1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var s=2;s<t.length;s++,c++)i=n[c],a=t[s],r+="S"+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1]}if(e){var l=n[c];r+="Q"+(i[0]+2*a[0]/3)+","+(i[1]+2*a[1]/3)+","+l[0]+","+l[1]}return r}function _o(n,t){for(var e,r=[],u=(1-t)/2,i=n[0],o=n[1],a=1,c=n.length;++a<c;)e=i,i=o,o=n[a],r.push([u*(o[0]-e[0]),u*(o[1]-e[1])]);return r}function bo(n){if(n.length<3)return fo(n);var t=1,e=n.length,r=n[0],u=r[0],i=r[1],o=[u,u,u,(r=n[1])[0]],a=[i,i,i,r[1]],c=[u,",",i,"L",Eo(Ns,o),",",Eo(Ns,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),No(c,o,a);return n.pop(),c.push("L",r),c.join("")}function wo(n){if(n.length<4)return fo(n);for(var t,e=[],r=-1,u=n.length,i=[0],o=[0];++r<3;)t=n[r],i.push(t[0]),o.push(t[1]);for(e.push(Eo(Ns,i)+","+Eo(Ns,o)),--r;++r<u;)t=n[r],i.shift(),i.push(t[0]),o.shift(),o.push(t[1]),No(e,i,o);return e.join("")}function So(n){for(var t,e,r=-1,u=n.length,i=u+4,o=[],a=[];++r<4;)e=n[r%u],o.push(e[0]),a.push(e[1]);for(t=[Eo(Ns,o),",",Eo(Ns,a)],--r;++r<i;)e=n[r%u],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),No(t,o,a);return t.join("")}function ko(n,t){var e=n.length-1;if(e)for(var r,u,i=n[0][0],o=n[0][1],a=n[e][0]-i,c=n[e][1]-o,s=-1;++s<=e;)r=n[s],u=s/e,r[0]=t*r[0]+(1-t)*(i+u*a),r[1]=t*r[1]+(1-t)*(o+u*c);return bo(n)}function Eo(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function No(n,t,e){n.push("C",Eo(ks,t),",",Eo(ks,e),",",Eo(Es,t),",",Eo(Es,e),",",Eo(Ns,t),",",Eo(Ns,e))}function Ao(n,t){return(t[1]-n[1])/(t[0]-n[0])}function Co(n){for(var t=0,e=n.length-1,r=[],u=n[0],i=n[1],o=r[0]=Ao(u,i);++t<e;)r[t]=(o+(o=Ao(u=i,i=n[t+1])))/2;return r[t]=o,r}function Lo(n){for(var t,e,r,u,i=[],o=Co(n),a=-1,c=n.length-1;++a<c;)t=Ao(n[a],n[a+1]),fa(t)<Ta?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,u=e*e+r*r,u>9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function To(n){return n.length<3?fo(n):n[0]+Mo(n,Lo(n))}function qo(n){for(var t,e,r,u=-1,i=n.length;++u<i;)t=n[u],e=t[0],r=t[1]+bs,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function zo(n){function t(t){function c(){v.push("M",a(n(m),f),l,s(n(d.reverse()),f),"Z")}for(var h,g,p,v=[],d=[],m=[],y=-1,x=t.length,M=Et(e),_=Et(u),b=e===r?function(){return g}:Et(r),w=u===i?function(){return p}:Et(i);++y<x;)o.call(this,h=t[y],y)?(d.push([g=+M.call(this,h,y),p=+_.call(this,h,y)]),m.push([+b.call(this,h,y),+w.call(this,h,y)])):d.length&&(c(),d=[],m=[]);return d.length&&c(),v.length?v.join(""):null}var e=Nr,r=Nr,u=0,i=Ar,o=Ne,a=fo,c=a.key,s=a,l="L",f=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(u=i=n,t):i},t.y0=function(n){return arguments.length?(u=n,t):u},t.y1=function(n){return arguments.length?(i=n,t):i},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(c="function"==typeof n?a=n:(a=Ss.get(n)||fo).key,s=a.reverse||a,l=a.closed?"M":"L",t):c},t.tension=function(n){return arguments.length?(f=n,t):f},t}function Ro(n){return n.radius}function Do(n){return[n.x,n.y]}function Po(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]+bs;return[e*Math.cos(r),e*Math.sin(r)]}}function Uo(){return 64}function jo(){return"circle"}function Ho(n){var t=Math.sqrt(n/Aa);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Fo(n,t){return da(n,zs),n.id=t,n}function Oo(n,t,e,r){var u=n.id;return P(n,"function"==typeof e?function(n,i,o){n.__transition__[u].tween.set(t,r(e.call(n,n.__data__,i,o)))}:(e=r(e),function(n){n.__transition__[u].tween.set(t,e)}))}function Yo(n){return null==n&&(n=""),function(){this.textContent=n}}function Io(n,t,e,r){var u=n.__transition__||(n.__transition__={active:0,count:0}),i=u[e];if(!i){var a=r.time;i=u[e]={tween:new o,time:a,ease:r.ease,delay:r.delay,duration:r.duration},++u.count,Go.timer(function(r){function o(r){return u.active>e?s():(u.active=e,i.event&&i.event.start.call(n,l,t),i.tween.forEach(function(e,r){(r=r.call(n,l,t))&&v.push(r)}),Go.timer(function(){return p.c=c(r||1)?Ne:c,1},0,a),void 0)}function c(r){if(u.active!==e)return s();for(var o=r/g,a=f(o),c=v.length;c>0;)v[--c].call(n,a);return o>=1?(i.event&&i.event.end.call(n,l,t),s()):void 0}function s(){return--u.count?delete u[e]:delete n.__transition__,1}var l=n.__data__,f=i.ease,h=i.delay,g=i.duration,p=nc,v=[];return p.t=h+a,r>=h?o(r-h):(p.c=o,void 0) +},0,a)}}function Zo(n,t){n.attr("transform",function(n){return"translate("+t(n)+",0)"})}function Vo(n,t){n.attr("transform",function(n){return"translate(0,"+t(n)+")"})}function $o(n){return n.toISOString()}function Xo(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=Go.bisect(Ys,u);return i==Ys.length?[t.year,Xi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/Ys[i-1]<Ys[i]/u?i-1:i]:[Vs,Xi(n,e)[2]]}return r.invert=function(t){return Bo(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(Bo)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,Bo(+e+1),t).length}var i=r.domain(),o=ji(i),a=null==n?u(o,10):"number"==typeof n&&u(o,n);return a&&(n=a[0],t=a[1]),r.domain(Oi(i,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=Bo(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Bo(+t+1);return t}}:n))},r.ticks=function(n,t){var e=ji(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Bo(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Xo(n.copy(),t,e)},Vi(r,n)}function Bo(n){return new Date(n)}function Jo(n){return JSON.parse(n.responseText)}function Wo(n){var t=na.createRange();return t.selectNode(na.body),t.createContextualFragment(n.responseText)}var Go={version:"3.4.4"};Date.now||(Date.now=function(){return+new Date});var Ko=[].slice,Qo=function(n){return Ko.call(n)},na=document,ta=na.documentElement,ea=window;try{Qo(ta.childNodes)[0].nodeType}catch(ra){Qo=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}try{na.createElement("div").style.setProperty("opacity",0,"")}catch(ua){var ia=ea.Element.prototype,oa=ia.setAttribute,aa=ia.setAttributeNS,ca=ea.CSSStyleDeclaration.prototype,sa=ca.setProperty;ia.setAttribute=function(n,t){oa.call(this,n,t+"")},ia.setAttributeNS=function(n,t,e){aa.call(this,n,t,e+"")},ca.setProperty=function(n,t,e){sa.call(this,n,t+"",e)}}Go.ascending=n,Go.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},Go.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&e>r&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&e>r&&(e=r)}return e},Go.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&r>e&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&r>e&&(e=r)}return e},Go.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i<o&&!(null!=(e=u=n[i])&&e>=e);)e=u=void 0;for(;++i<o;)null!=(r=n[i])&&(e>r&&(e=r),r>u&&(u=r))}else{for(;++i<o&&!(null!=(e=u=t.call(n,n[i],i))&&e>=e);)e=void 0;for(;++i<o;)null!=(r=t.call(n,n[i],i))&&(e>r&&(e=r),r>u&&(u=r))}return[e,u]},Go.sum=function(n,t){var e,r=0,u=n.length,i=-1;if(1===arguments.length)for(;++i<u;)isNaN(e=+n[i])||(r+=e);else for(;++i<u;)isNaN(e=+t.call(n,n[i],i))||(r+=e);return r},Go.mean=function(n,e){var r,u=n.length,i=0,o=-1,a=0;if(1===arguments.length)for(;++o<u;)t(r=n[o])&&(i+=(r-i)/++a);else for(;++o<u;)t(r=e.call(n,n[o],o))&&(i+=(r-i)/++a);return a?i:void 0},Go.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),u=+n[r-1],i=e-r;return i?u+i*(n[r]-u):u},Go.median=function(e,r){return arguments.length>1&&(e=e.map(r)),e=e.filter(t),e.length?Go.quantile(e.sort(n),.5):void 0};var la=e(n);Go.bisectLeft=la.left,Go.bisect=Go.bisectRight=la.right,Go.bisector=function(t){return e(1===t.length?function(e,r){return n(t(e),r)}:t)},Go.shuffle=function(n){for(var t,e,r=n.length;r;)e=0|Math.random()*r--,t=n[r],n[r]=n[e],n[e]=t;return n},Go.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},Go.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},Go.zip=function(){if(!(u=arguments.length))return[];for(var n=-1,t=Go.min(arguments,r),e=new Array(t);++n<t;)for(var u,i=-1,o=e[n]=new Array(u);++i<u;)o[i]=arguments[i][n];return e},Go.transpose=function(n){return Go.zip.apply(Go,n)},Go.keys=function(n){var t=[];for(var e in n)t.push(e);return t},Go.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},Go.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},Go.merge=function(n){for(var t,e,r,u=n.length,i=-1,o=0;++i<u;)o+=n[i].length;for(e=new Array(o);--u>=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var fa=Math.abs;Go.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),1/0===(t-n)/e)throw new Error("infinite range");var r,i=[],o=u(fa(e)),a=-1;if(n*=o,t*=o,e*=o,0>e)for(;(r=n+e*++a)>t;)i.push(r/o);else for(;(r=n+e*++a)<t;)i.push(r/o);return i},Go.map=function(n){var t=new o;if(n instanceof o)n.forEach(function(n,e){t.set(n,e)});else for(var e in n)t.set(e,n[e]);return t},i(o,{has:a,get:function(n){return this[ha+n]},set:function(n,t){return this[ha+n]=t},remove:c,keys:s,values:function(){var n=[];return this.forEach(function(t,e){n.push(e)}),n},entries:function(){var n=[];return this.forEach(function(t,e){n.push({key:t,value:e})}),n},size:l,empty:f,forEach:function(n){for(var t in this)t.charCodeAt(0)===ga&&n.call(this,t.substring(1),this[t])}});var ha="\x00",ga=ha.charCodeAt(0);Go.nest=function(){function n(t,a,c){if(c>=i.length)return r?r.call(u,a):e?a.sort(e):a;for(var s,l,f,h,g=-1,p=a.length,v=i[c++],d=new o;++g<p;)(h=d.get(s=v(l=a[g])))?h.push(l):d.set(s,[l]);return t?(l=t(),f=function(e,r){l.set(e,n(t,r,c))}):(l={},f=function(e,r){l[e]=n(t,r,c)}),d.forEach(f),l}function t(n,e){if(e>=i.length)return n;var r=[],u=a[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,u={},i=[],a=[];return u.map=function(t,e){return n(e,t,0)},u.entries=function(e){return t(n(Go.map,e,0),0)},u.key=function(n){return i.push(n),u},u.sortKeys=function(n){return a[i.length-1]=n,u},u.sortValues=function(n){return e=n,u},u.rollup=function(n){return r=n,u},u},Go.set=function(n){var t=new h;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},i(h,{has:a,add:function(n){return this[ha+n]=!0,n},remove:function(n){return n=ha+n,n in this&&delete this[n]},values:s,size:l,empty:f,forEach:function(n){for(var t in this)t.charCodeAt(0)===ga&&n.call(this,t.substring(1))}}),Go.behavior={},Go.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r<u;)n[e=arguments[r]]=g(n,t,t[e]);return n};var pa=["webkit","ms","moz","Moz","o","O"];Go.dispatch=function(){for(var n=new d,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=m(n);return n},d.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.substring(e+1),n=n.substring(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},Go.event=null,Go.requote=function(n){return n.replace(va,"\\$&")};var va=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,da={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},ma=function(n,t){return t.querySelector(n)},ya=function(n,t){return t.querySelectorAll(n)},xa=ta[p(ta,"matchesSelector")],Ma=function(n,t){return xa.call(n,t)};"function"==typeof Sizzle&&(ma=function(n,t){return Sizzle(n,t)[0]||null},ya=Sizzle,Ma=Sizzle.matchesSelector),Go.selection=function(){return Sa};var _a=Go.selection.prototype=[];_a.select=function(n){var t,e,r,u,i=[];n=b(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var c=-1,s=r.length;++c<s;)(u=r[c])?(t.push(e=n.call(u,u.__data__,c,o)),e&&"__data__"in u&&(e.__data__=u.__data__)):t.push(null)}return _(i)},_a.selectAll=function(n){var t,e,r=[];n=w(n);for(var u=-1,i=this.length;++u<i;)for(var o=this[u],a=-1,c=o.length;++a<c;)(e=o[a])&&(r.push(t=Qo(n.call(e,e.__data__,a,u))),t.parentNode=e);return _(r)};var ba={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};Go.ns={prefix:ba,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&(e=n.substring(0,t),n=n.substring(t+1)),ba.hasOwnProperty(e)?{space:ba[e],local:n}:n}},_a.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=Go.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(S(t,n[t]));return this}return this.each(S(n,t))},_a.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=N(n)).length,u=-1;if(t=e.classList){for(;++u<r;)if(!t.contains(n[u]))return!1}else for(t=e.getAttribute("class");++u<r;)if(!E(n[u]).test(t))return!1;return!0}for(t in n)this.each(A(t,n[t]));return this}return this.each(A(n,t))},_a.style=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t="");for(e in n)this.each(L(e,n[e],t));return this}if(2>r)return ea.getComputedStyle(this.node(),null).getPropertyValue(n);e=""}return this.each(L(n,t,e))},_a.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(T(t,n[t]));return this}return this.each(T(n,t))},_a.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},_a.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},_a.append=function(n){return n=q(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},_a.insert=function(n,t){return n=q(n),t=b(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},_a.remove=function(){return this.each(function(){var n=this.parentNode;n&&n.removeChild(this)})},_a.data=function(n,t){function e(n,e){var r,u,i,a=n.length,f=e.length,h=Math.min(a,f),g=new Array(f),p=new Array(f),v=new Array(a);if(t){var d,m=new o,y=new o,x=[];for(r=-1;++r<a;)d=t.call(u=n[r],u.__data__,r),m.has(d)?v[r]=u:m.set(d,u),x.push(d);for(r=-1;++r<f;)d=t.call(e,i=e[r],r),(u=m.get(d))?(g[r]=u,u.__data__=i):y.has(d)||(p[r]=z(i)),y.set(d,i),m.remove(d);for(r=-1;++r<a;)m.has(x[r])&&(v[r]=n[r])}else{for(r=-1;++r<h;)u=n[r],i=e[r],u?(u.__data__=i,g[r]=u):p[r]=z(i);for(;f>r;++r)p[r]=z(e[r]);for(;a>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,c.push(p),s.push(g),l.push(v)}var r,u,i=-1,a=this.length;if(!arguments.length){for(n=new Array(a=(r=this[0]).length);++i<a;)(u=r[i])&&(n[i]=u.__data__);return n}var c=U([]),s=_([]),l=_([]);if("function"==typeof n)for(;++i<a;)e(r=this[i],n.call(r,r.parentNode.__data__,i));else for(;++i<a;)e(r=this[i],n);return s.enter=function(){return c},s.exit=function(){return l},s},_a.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},_a.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=R(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return _(u)},_a.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],u=r.length-1,i=r[u];--u>=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},_a.sort=function(n){n=D.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},_a.each=function(n){return P(this,function(t,e,r){n.call(t,t.__data__,e,r)})},_a.call=function(n){var t=Qo(arguments);return n.apply(t[0]=this,t),this},_a.empty=function(){return!this.node()},_a.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},_a.size=function(){var n=0;return this.each(function(){++n}),n};var wa=[];Go.selection.enter=U,Go.selection.enter.prototype=wa,wa.append=_a.append,wa.empty=_a.empty,wa.node=_a.node,wa.call=_a.call,wa.size=_a.size,wa.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++a<c;){r=(u=this[a]).update,o.push(t=[]),t.parentNode=u.parentNode;for(var s=-1,l=u.length;++s<l;)(i=u[s])?(t.push(r[s]=e=n.call(u.parentNode,i.__data__,s,a)),e.__data__=i.__data__):t.push(null)}return _(o)},wa.insert=function(n,t){return arguments.length<2&&(t=j(this)),_a.insert.call(this,n,t)},_a.transition=function(){for(var n,t,e=Cs||++Rs,r=[],u=Ls||{time:Date.now(),ease:wu,delay:0,duration:250},i=-1,o=this.length;++i<o;){r.push(n=[]);for(var a=this[i],c=-1,s=a.length;++c<s;)(t=a[c])&&Io(t,c,e,u),n.push(t)}return Fo(r,e)},_a.interrupt=function(){return this.each(H)},Go.select=function(n){var t=["string"==typeof n?ma(n,na):n];return t.parentNode=ta,_([t])},Go.selectAll=function(n){var t=Qo("string"==typeof n?ya(n,na):n);return t.parentNode=ta,_([t])};var Sa=Go.select(ta);_a.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(F(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(F(n,t,e))};var ka=Go.map({mouseenter:"mouseover",mouseleave:"mouseout"});ka.forEach(function(n){"on"+n in na&&ka.remove(n)});var Ea="onselectstart"in na?null:p(ta.style,"userSelect"),Na=0;Go.mouse=function(n){return Z(n,x())},Go.touches=function(n,t){return arguments.length<2&&(t=x().touches),t?Qo(t).map(function(t){var e=Z(n,t);return e.identifier=t.identifier,e}):[]},Go.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",i)}function t(n,t,u,i,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-x[0],e=r[1]-x[1],p|=n|e,x=r,g({type:"drag",x:r[0]+s[0],y:r[1]+s[1],dx:n,dy:e}))}function c(){t(h,v)&&(m.on(i+d,null).on(o+d,null),y(p&&Go.event.target===f),g({type:"dragend"}))}var s,l=this,f=Go.event.target,h=l.parentNode,g=e.of(l,arguments),p=0,v=n(),d=".drag"+(null==v?"":"-"+v),m=Go.select(u()).on(i+d,a).on(o+d,c),y=I(),x=t(h,v);r?(s=r.apply(l,arguments),s=[s.x-x[0],s.y-x[1]]):s=[0,0],g({type:"dragstart"})}}var e=M(n,"drag","dragstart","dragend"),r=null,u=t(v,Go.mouse,X,"mousemove","mouseup"),i=t(V,Go.touch,$,"touchmove","touchend");return n.origin=function(t){return arguments.length?(r=t,n):r},Go.rebind(n,e,"on")};var Aa=Math.PI,Ca=2*Aa,La=Aa/2,Ta=1e-6,qa=Ta*Ta,za=Aa/180,Ra=180/Aa,Da=Math.SQRT2,Pa=2,Ua=4;Go.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=Q(v),o=i/(Pa*h)*(e*nt(Da*t+v)-K(v));return[r+o*s,u+o*l,i*e/Q(Da*t+v)]}return[r+n*s,u+n*l,i*Math.exp(Da*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],s=o-r,l=a-u,f=s*s+l*l,h=Math.sqrt(f),g=(c*c-i*i+Ua*f)/(2*i*Pa*h),p=(c*c-i*i-Ua*f)/(2*c*Pa*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Da;return e.duration=1e3*y,e},Go.behavior.zoom=function(){function n(n){n.on(N,s).on(Fa+".zoom",f).on(A,h).on("dblclick.zoom",g).on(L,l)}function t(n){return[(n[0]-S.x)/S.k,(n[1]-S.y)/S.k]}function e(n){return[n[0]*S.k+S.x,n[1]*S.k+S.y]}function r(n){S.k=Math.max(E[0],Math.min(E[1],n))}function u(n,t){t=e(t),S.x+=n[0]-t[0],S.y+=n[1]-t[1]}function i(){_&&_.domain(x.range().map(function(n){return(n-S.x)/S.k}).map(x.invert)),w&&w.domain(b.range().map(function(n){return(n-S.y)/S.k}).map(b.invert))}function o(n){n({type:"zoomstart"})}function a(n){i(),n({type:"zoom",scale:S.k,translate:[S.x,S.y]})}function c(n){n({type:"zoomend"})}function s(){function n(){l=1,u(Go.mouse(r),g),a(s)}function e(){f.on(A,ea===r?h:null).on(C,null),p(l&&Go.event.target===i),c(s)}var r=this,i=Go.event.target,s=T.of(r,arguments),l=0,f=Go.select(ea).on(A,n).on(C,e),g=t(Go.mouse(r)),p=I();H.call(r),o(s)}function l(){function n(){var n=Go.touches(g);return h=S.k,n.forEach(function(n){n.identifier in v&&(v[n.identifier]=t(n))}),n}function e(){for(var t=Go.event.changedTouches,e=0,i=t.length;i>e;++e)v[t[e].identifier]=null;var o=n(),c=Date.now();if(1===o.length){if(500>c-m){var s=o[0],l=v[s.identifier];r(2*S.k),u(s,l),y(),a(p)}m=c}else if(o.length>1){var s=o[0],f=o[1],h=s[0]-f[0],g=s[1]-f[1];d=h*h+g*g}}function i(){for(var n,t,e,i,o=Go.touches(g),c=0,s=o.length;s>c;++c,i=null)if(e=o[c],i=v[e.identifier]){if(t)break;n=e,t=i}if(i){var l=(l=e[0]-n[0])*l+(l=e[1]-n[1])*l,f=d&&Math.sqrt(l/d);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+i[0])/2,(t[1]+i[1])/2],r(f*h)}m=null,u(n,t),a(p)}function f(){if(Go.event.touches.length){for(var t=Go.event.changedTouches,e=0,r=t.length;r>e;++e)delete v[t[e].identifier];for(var u in v)return void n()}b.on(x,null),w.on(N,s).on(L,l),k(),c(p)}var h,g=this,p=T.of(g,arguments),v={},d=0,x=".zoom-"+Go.event.changedTouches[0].identifier,M="touchmove"+x,_="touchend"+x,b=Go.select(Go.event.target).on(M,i).on(_,f),w=Go.select(g).on(N,null).on(L,e),k=I();H.call(g),e(),o(p)}function f(){var n=T.of(this,arguments);d?clearTimeout(d):(H.call(this),o(n)),d=setTimeout(function(){d=null,c(n)},50),y();var e=v||Go.mouse(this);p||(p=t(e)),r(Math.pow(2,.002*ja())*S.k),u(e,p),a(n)}function h(){p=null}function g(){var n=T.of(this,arguments),e=Go.mouse(this),i=t(e),s=Math.log(S.k)/Math.LN2;o(n),r(Math.pow(2,Go.event.shiftKey?Math.ceil(s)-1:Math.floor(s)+1)),u(e,i),a(n),c(n)}var p,v,d,m,x,_,b,w,S={x:0,y:0,k:1},k=[960,500],E=Ha,N="mousedown.zoom",A="mousemove.zoom",C="mouseup.zoom",L="touchstart.zoom",T=M(n,"zoomstart","zoom","zoomend");return n.event=function(n){n.each(function(){var n=T.of(this,arguments),t=S;Cs?Go.select(this).transition().each("start.zoom",function(){S=this.__chart__||{x:0,y:0,k:1},o(n)}).tween("zoom:zoom",function(){var e=k[0],r=k[1],u=e/2,i=r/2,o=Go.interpolateZoom([(u-S.x)/S.k,(i-S.y)/S.k,e/S.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),c=e/r[2];this.__chart__=S={x:u-r[0]*c,y:i-r[1]*c,k:c},a(n)}}).each("end.zoom",function(){c(n)}):(this.__chart__=S,o(n),a(n),c(n))})},n.translate=function(t){return arguments.length?(S={x:+t[0],y:+t[1],k:S.k},i(),n):[S.x,S.y]},n.scale=function(t){return arguments.length?(S={x:S.x,y:S.y,k:+t},i(),n):S.k},n.scaleExtent=function(t){return arguments.length?(E=null==t?Ha:[+t[0],+t[1]],n):E},n.center=function(t){return arguments.length?(v=t&&[+t[0],+t[1]],n):v},n.size=function(t){return arguments.length?(k=t&&[+t[0],+t[1]],n):k},n.x=function(t){return arguments.length?(_=t,x=t.copy(),S={x:0,y:0,k:1},n):_},n.y=function(t){return arguments.length?(w=t,b=t.copy(),S={x:0,y:0,k:1},n):w},Go.rebind(n,T,"on")};var ja,Ha=[0,1/0],Fa="onwheel"in na?(ja=function(){return-Go.event.deltaY*(Go.event.deltaMode?120:1)},"wheel"):"onmousewheel"in na?(ja=function(){return Go.event.wheelDelta},"mousewheel"):(ja=function(){return-Go.event.detail},"MozMousePixelScroll");et.prototype.toString=function(){return this.rgb()+""},Go.hsl=function(n,t,e){return 1===arguments.length?n instanceof ut?rt(n.h,n.s,n.l):_t(""+n,bt,rt):rt(+n,+t,+e)};var Oa=ut.prototype=new et;Oa.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),rt(this.h,this.s,this.l/n)},Oa.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),rt(this.h,this.s,n*this.l)},Oa.rgb=function(){return it(this.h,this.s,this.l)},Go.hcl=function(n,t,e){return 1===arguments.length?n instanceof at?ot(n.h,n.c,n.l):n instanceof lt?ht(n.l,n.a,n.b):ht((n=wt((n=Go.rgb(n)).r,n.g,n.b)).l,n.a,n.b):ot(+n,+t,+e)};var Ya=at.prototype=new et;Ya.brighter=function(n){return ot(this.h,this.c,Math.min(100,this.l+Ia*(arguments.length?n:1)))},Ya.darker=function(n){return ot(this.h,this.c,Math.max(0,this.l-Ia*(arguments.length?n:1)))},Ya.rgb=function(){return ct(this.h,this.c,this.l).rgb()},Go.lab=function(n,t,e){return 1===arguments.length?n instanceof lt?st(n.l,n.a,n.b):n instanceof at?ct(n.l,n.c,n.h):wt((n=Go.rgb(n)).r,n.g,n.b):st(+n,+t,+e)};var Ia=18,Za=.95047,Va=1,$a=1.08883,Xa=lt.prototype=new et;Xa.brighter=function(n){return st(Math.min(100,this.l+Ia*(arguments.length?n:1)),this.a,this.b)},Xa.darker=function(n){return st(Math.max(0,this.l-Ia*(arguments.length?n:1)),this.a,this.b)},Xa.rgb=function(){return ft(this.l,this.a,this.b)},Go.rgb=function(n,t,e){return 1===arguments.length?n instanceof xt?yt(n.r,n.g,n.b):_t(""+n,yt,it):yt(~~n,~~t,~~e)};var Ba=xt.prototype=new et;Ba.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),yt(Math.min(255,~~(t/n)),Math.min(255,~~(e/n)),Math.min(255,~~(r/n)))):yt(u,u,u)},Ba.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),yt(~~(n*this.r),~~(n*this.g),~~(n*this.b))},Ba.hsl=function(){return bt(this.r,this.g,this.b)},Ba.toString=function(){return"#"+Mt(this.r)+Mt(this.g)+Mt(this.b)};var Ja=Go.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});Ja.forEach(function(n,t){Ja.set(n,dt(t))}),Go.functor=Et,Go.xhr=At(Nt),Go.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=Ct(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(l>=s)return o;if(u)return u=!1,i;var t=l;if(34===n.charCodeAt(t)){for(var e=t;e++<s;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}l=e+2;var r=n.charCodeAt(e+1);return 13===r?(u=!0,10===n.charCodeAt(e+2)&&++l):10===r&&(u=!0),n.substring(t+1,e).replace(/""/g,'"')}for(;s>l;){var r=n.charCodeAt(l++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(l)&&(++l,++a);else if(r!==c)continue;return n.substring(t,l-a)}return n.substring(t)}for(var r,u,i={},o={},a=[],s=n.length,l=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();(!t||(h=t(h,f++)))&&a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new h,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},Go.csv=Go.dsv(",","text/csv"),Go.tsv=Go.dsv(" ","text/tab-separated-values"),Go.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=x().changedTouches),t)for(var r,u=0,i=t.length;i>u;++u)if((r=t[u]).identifier===e)return Z(n,r)};var Wa,Ga,Ka,Qa,nc,tc=ea[p(ea,"requestAnimationFrame")]||function(n){setTimeout(n,17)};Go.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};Ga?Ga.n=i:Wa=i,Ga=i,Ka||(Qa=clearTimeout(Qa),Ka=1,tc(Tt))},Go.timer.flush=function(){qt(),zt()},Go.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var ec=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Dt);Go.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=Go.round(n,Rt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),ec[8+e/3]};var rc=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,uc=Go.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=Go.round(n,Rt(n,t))).toFixed(Math.max(0,Math.min(20,Rt(n*(1+1e-15),t))))}}),ic=Go.time={},oc=Date;jt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){ac.setUTCDate.apply(this._,arguments)},setDay:function(){ac.setUTCDay.apply(this._,arguments)},setFullYear:function(){ac.setUTCFullYear.apply(this._,arguments)},setHours:function(){ac.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){ac.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){ac.setUTCMinutes.apply(this._,arguments)},setMonth:function(){ac.setUTCMonth.apply(this._,arguments)},setSeconds:function(){ac.setUTCSeconds.apply(this._,arguments)},setTime:function(){ac.setTime.apply(this._,arguments)}};var ac=Date.prototype;ic.year=Ht(function(n){return n=ic.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ic.years=ic.year.range,ic.years.utc=ic.year.utc.range,ic.day=Ht(function(n){var t=new oc(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ic.days=ic.day.range,ic.days.utc=ic.day.utc.range,ic.dayOfYear=function(n){var t=ic.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ic[n]=Ht(function(n){return(n=ic.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ic.year(n).getDay();return Math.floor((ic.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ic[n+"s"]=e.range,ic[n+"s"].utc=e.utc.range,ic[n+"OfYear"]=function(n){var e=ic.year(n).getDay();return Math.floor((ic.dayOfYear(n)+(e+t)%7)/7)}}),ic.week=ic.sunday,ic.weeks=ic.sunday.range,ic.weeks.utc=ic.sunday.utc.range,ic.weekOfYear=ic.sundayOfYear;var cc={"-":"",_:" ",0:"0"},sc=/^\s*\d+/,lc=/^%/;Go.locale=function(n){return{numberFormat:Pt(n),timeFormat:Ot(n)}};var fc=Go.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});Go.format=fc.numberFormat,Go.geo={},ce.prototype={s:0,t:0,add:function(n){se(n,this.t,hc),se(hc.s,this.s,this),this.s?this.t+=hc.t:this.s=hc.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var hc=new ce;Go.geo.stream=function(n,t){n&&gc.hasOwnProperty(n.type)?gc[n.type](n,t):le(n,t)};var gc={Feature:function(n,t){le(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++r<u;)le(e[r].geometry,t)}},pc={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)n=e[r],t.point(n[0],n[1],n[2])},LineString:function(n,t){fe(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)fe(e[r],t,0)},Polygon:function(n,t){he(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)he(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,u=e.length;++r<u;)le(e[r],t)}};Go.geo.area=function(n){return vc=0,Go.geo.stream(n,mc),vc};var vc,dc=new ce,mc={sphere:function(){vc+=4*Aa},point:v,lineStart:v,lineEnd:v,polygonStart:function(){dc.reset(),mc.lineStart=ge},polygonEnd:function(){var n=2*dc;vc+=0>n?4*Aa+n:n,mc.lineStart=mc.lineEnd=mc.point=v}};Go.geo.bounds=function(){function n(n,t){x.push(M=[l=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=pe([t*za,e*za]);if(m){var u=de(m,r),i=[u[1],-u[0],0],o=de(i,u);xe(o),o=Me(o);var c=t-p,s=c>0?1:-1,v=o[0]*Ra*s,d=fa(c)>180;if(d^(v>s*p&&s*t>v)){var y=o[1]*Ra;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>s*p&&s*t>v)){var y=-o[1]*Ra;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t):h>=l?(l>t&&(l=t),t>h&&(h=t)):t>p?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t)}else n(t,e);m=r,p=t}function e(){_.point=t}function r(){M[0]=l,M[1]=h,_.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=fa(r)>180?r+(r>0?360:-360):r}else v=n,d=e;mc.point(n,e),t(n,e)}function i(){mc.lineStart()}function o(){u(v,d),mc.lineEnd(),fa(y)>Ta&&(l=-(h=180)),M[0]=l,M[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function s(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var l,f,h,g,p,v,d,m,y,x,M,_={point:n,lineStart:e,lineEnd:r,polygonStart:function(){_.point=u,_.lineStart=i,_.lineEnd=o,y=0,mc.polygonStart()},polygonEnd:function(){mc.polygonEnd(),_.point=n,_.lineStart=e,_.lineEnd=r,0>dc?(l=-(h=180),f=-(g=90)):y>Ta?g=90:-Ta>y&&(f=-90),M[0]=l,M[1]=h +}};return function(n){g=h=-(l=f=1/0),x=[],Go.geo.stream(n,_);var t=x.length;if(t){x.sort(c);for(var e,r=1,u=x[0],i=[u];t>r;++r)e=x[r],s(e[0],u)||s(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,l=e[0],h=u[1])}return x=M=null,1/0===l||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[l,f],[h,g]]}}(),Go.geo.centroid=function(n){yc=xc=Mc=_c=bc=wc=Sc=kc=Ec=Nc=Ac=0,Go.geo.stream(n,Cc);var t=Ec,e=Nc,r=Ac,u=t*t+e*e+r*r;return qa>u&&(t=wc,e=Sc,r=kc,Ta>xc&&(t=Mc,e=_c,r=bc),u=t*t+e*e+r*r,qa>u)?[0/0,0/0]:[Math.atan2(e,t)*Ra,G(r/Math.sqrt(u))*Ra]};var yc,xc,Mc,_c,bc,wc,Sc,kc,Ec,Nc,Ac,Cc={sphere:v,point:be,lineStart:Se,lineEnd:ke,polygonStart:function(){Cc.lineStart=Ee},polygonEnd:function(){Cc.lineStart=Se}},Lc=Te(Ne,Pe,je,[-Aa,-Aa/2]),Tc=1e9;Go.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Oe(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(Go.geo.conicEqualArea=function(){return Ie(Ze)}).raw=Ze,Go.geo.albers=function(){return Go.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},Go.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=Go.geo.albers(),o=Go.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=Go.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var s=i.scale(),l=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[l-.455*s,f-.238*s],[l+.455*s,f+.238*s]]).stream(c).point,r=o.translate([l-.307*s,f+.201*s]).clipExtent([[l-.425*s+Ta,f+.12*s+Ta],[l-.214*s-Ta,f+.234*s-Ta]]).stream(c).point,u=a.translate([l-.205*s,f+.212*s]).clipExtent([[l-.214*s+Ta,f+.166*s+Ta],[l-.115*s-Ta,f+.234*s-Ta]]).stream(c).point,n},n.scale(1070)};var qc,zc,Rc,Dc,Pc,Uc,jc={point:v,lineStart:v,lineEnd:v,polygonStart:function(){zc=0,jc.lineStart=Ve},polygonEnd:function(){jc.lineStart=jc.lineEnd=jc.point=v,qc+=fa(zc/2)}},Hc={point:$e,lineStart:v,lineEnd:v,polygonStart:v,polygonEnd:v},Fc={point:Je,lineStart:We,lineEnd:Ge,polygonStart:function(){Fc.lineStart=Ke},polygonEnd:function(){Fc.point=Je,Fc.lineStart=We,Fc.lineEnd=Ge}};Go.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),Go.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return qc=0,Go.geo.stream(n,u(jc)),qc},n.centroid=function(n){return Mc=_c=bc=wc=Sc=kc=Ec=Nc=Ac=0,Go.geo.stream(n,u(Fc)),Ac?[Ec/Ac,Nc/Ac]:kc?[wc/kc,Sc/kc]:bc?[Mc/bc,_c/bc]:[0/0,0/0]},n.bounds=function(n){return Pc=Uc=-(Rc=Dc=1/0),Go.geo.stream(n,u(Hc)),[[Rc,Dc],[Pc,Uc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||tr(n):Nt,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new Xe:new Qe(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(Go.geo.albersUsa()).context(null)},Go.geo.transform=function(n){return{stream:function(t){var e=new er(t);for(var r in n)e[r]=n[r];return e}}},er.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},Go.geo.projection=ur,Go.geo.projectionMutator=ir,(Go.geo.equirectangular=function(){return ur(ar)}).raw=ar.invert=ar,Go.geo.rotation=function(n){function t(t){return t=n(t[0]*za,t[1]*za),t[0]*=Ra,t[1]*=Ra,t}return n=sr(n[0]%360*za,n[1]*za,n.length>2?n[2]*za:0),t.invert=function(t){return t=n.invert(t[0]*za,t[1]*za),t[0]*=Ra,t[1]*=Ra,t},t},cr.invert=ar,Go.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=sr(-n[0]*za,-n[1]*za,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=Ra,n[1]*=Ra}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=gr((t=+r)*za,u*za),n):t},n.precision=function(r){return arguments.length?(e=gr(t*za,(u=+r)*za),n):u},n.angle(90)},Go.geo.distance=function(n,t){var e,r=(t[0]-n[0])*za,u=n[1]*za,i=t[1]*za,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),s=Math.cos(u),l=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=s*l-c*f*a)*e),c*l+s*f*a)},Go.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return Go.range(Math.ceil(i/d)*d,u,d).map(h).concat(Go.range(Math.ceil(s/m)*m,c,m).map(g)).concat(Go.range(Math.ceil(r/p)*p,e,p).filter(function(n){return fa(n%d)>Ta}).map(l)).concat(Go.range(Math.ceil(a/v)*v,o,v).filter(function(n){return fa(n%m)>Ta}).map(f))}var e,r,u,i,o,a,c,s,l,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(s).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],s=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),s>c&&(t=s,s=c,c=t),n.precision(y)):[[i,s],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,l=vr(a,o,90),f=dr(r,e,y),h=vr(s,c,90),g=dr(i,u,y),n):y},n.majorExtent([[-180,-90+Ta],[180,90-Ta]]).minorExtent([[-180,-80-Ta],[180,80+Ta]])},Go.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=mr,u=yr;return n.distance=function(){return Go.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},Go.geo.interpolate=function(n,t){return xr(n[0]*za,n[1]*za,t[0]*za,t[1]*za)},Go.geo.length=function(n){return Oc=0,Go.geo.stream(n,Yc),Oc};var Oc,Yc={sphere:v,point:v,lineStart:Mr,lineEnd:v,polygonStart:v,polygonEnd:v},Ic=_r(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(Go.geo.azimuthalEqualArea=function(){return ur(Ic)}).raw=Ic;var Zc=_r(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},Nt);(Go.geo.azimuthalEquidistant=function(){return ur(Zc)}).raw=Zc,(Go.geo.conicConformal=function(){return Ie(br)}).raw=br,(Go.geo.conicEquidistant=function(){return Ie(wr)}).raw=wr;var Vc=_r(function(n){return 1/n},Math.atan);(Go.geo.gnomonic=function(){return ur(Vc)}).raw=Vc,Sr.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-La]},(Go.geo.mercator=function(){return kr(Sr)}).raw=Sr;var $c=_r(function(){return 1},Math.asin);(Go.geo.orthographic=function(){return ur($c)}).raw=$c;var Xc=_r(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(Go.geo.stereographic=function(){return ur(Xc)}).raw=Xc,Er.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-La]},(Go.geo.transverseMercator=function(){var n=kr(Er),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[-n[1],n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},n.rotate([0,0])}).raw=Er,Go.geom={},Go.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=Et(e),i=Et(r),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(Lr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var s=Cr(a),l=Cr(c),f=l[0]===s[0],h=l[l.length-1]===s[s.length-1],g=[];for(t=s.length-1;t>=0;--t)g.push(n[a[s[t]][2]]);for(t=+f;t<l.length-h;++t)g.push(n[a[l[t]][2]]);return g}var e=Nr,r=Ar;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},Go.geom.polygon=function(n){return da(n,Bc),n};var Bc=Go.geom.polygon.prototype=[];Bc.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],u=0;++t<e;)n=r,r=this[t],u+=n[1]*r[0]-n[0]*r[1];return.5*u},Bc.centroid=function(n){var t,e,r=-1,u=this.length,i=0,o=0,a=this[u-1];for(arguments.length||(n=-1/(6*this.area()));++r<u;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],i+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[i*n,o*n]},Bc.clip=function(n){for(var t,e,r,u,i,o,a=zr(n),c=-1,s=this.length-zr(this),l=this[s-1];++c<s;){for(t=n.slice(),n.length=0,u=this[c],i=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Tr(o,l,u)?(Tr(i,l,u)||n.push(qr(i,o,l,u)),n.push(o)):Tr(i,l,u)&&n.push(qr(i,o,l,u)),i=o;a&&n.push(n[0]),l=u}return n};var Jc,Wc,Gc,Kc,Qc,ns=[],ts=[];Or.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(Ir),t.length},Qr.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},nu.prototype={insert:function(n,t){var e,r,u;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=uu(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(u=r.R,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.R&&(eu(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ru(this,r))):(u=r.L,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.L&&(ru(this,e),n=e,e=n.U),e.C=!1,r.C=!0,eu(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,u=n.U,i=n.L,o=n.R;if(e=i?o?uu(o):i:o,u?u.L===n?u.L=e:u.R=e:this._=e,i&&o?(r=e.C,e.C=n.C,e.L=i,i.U=e,e!==o?(u=e.U,e.U=n.U,n=e.R,u.L=n,e.R=o,o.U=e):(e.U=u,u=e,n=e.R)):(r=n.C,n=e),n&&(n.U=u),!r){if(n&&n.C)return n.C=!1,void 0;do{if(n===this._)break;if(n===u.L){if(t=u.R,t.C&&(t.C=!1,u.C=!0,eu(this,u),t=u.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,ru(this,t),t=u.R),t.C=u.C,u.C=t.R.C=!1,eu(this,u),n=this._;break}}else if(t=u.L,t.C&&(t.C=!1,u.C=!0,ru(this,u),t=u.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,eu(this,t),t=u.L),t.C=u.C,u.C=t.L.C=!1,ru(this,u),n=this._;break}t.C=!0,n=u,u=u.U}while(!n.C);n&&(n.C=!1)}}},Go.geom.voronoi=function(n){function t(n){var t=new Array(n.length),r=a[0][0],u=a[0][1],i=a[1][0],o=a[1][1];return iu(e(n),a).cells.forEach(function(e,a){var c=e.edges,s=e.site,l=t[a]=c.length?c.map(function(n){var t=n.start();return[t.x,t.y]}):s.x>=r&&s.x<=i&&s.y>=u&&s.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];l.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Ta)*Ta,y:Math.round(o(n,t)/Ta)*Ta,i:t}})}var r=Nr,u=Ar,i=r,o=u,a=es;return n?t(n):(t.links=function(n){return iu(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return iu(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(Ir),c=-1,s=a.length,l=a[s-1].edge,f=l.l===o?l.r:l.l;++c<s;)u=l,i=f,l=a[c].edge,f=l.l===o?l.r:l.l,r<i.i&&r<f.i&&au(o,i,f)<0&&t.push([n[r],n[i.i],n[f.i]])}),t},t.x=function(n){return arguments.length?(i=Et(r=n),t):r},t.y=function(n){return arguments.length?(o=Et(u=n),t):u},t.clipExtent=function(n){return arguments.length?(a=null==n?es:n,t):a===es?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===es?null:a&&a[1]},t)};var es=[[-1e6,-1e6],[1e6,1e6]];Go.geom.delaunay=function(n){return Go.geom.voronoi().triangles(n)},Go.geom.quadtree=function(n,t,e,r,u){function i(n){function i(n,t,e,r,u,i,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var c=n.x,l=n.y;if(null!=c)if(fa(c-e)+fa(l-r)<.01)s(n,t,e,r,u,i,o,a);else{var f=n.point;n.x=n.y=n.point=null,s(n,f,c,l,u,i,o,a),s(n,t,e,r,u,i,o,a)}else n.x=e,n.y=r,n.point=t}else s(n,t,e,r,u,i,o,a)}function s(n,t,e,r,u,o,a,c){var s=.5*(u+a),l=.5*(o+c),f=e>=s,h=r>=l,g=(h<<1)+f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=lu()),f?u=s:a=s,h?o=l:c=l,i(n,t,e,r,u,o,a,c)}var l,f,h,g,p,v,d,m,y,x=Et(a),M=Et(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)l=n[g],l.x<v&&(v=l.x),l.y<d&&(d=l.y),l.x>m&&(m=l.x),l.y>y&&(y=l.y),f.push(l.x),h.push(l.y);else for(g=0;p>g;++g){var _=+x(l=n[g],g),b=+M(l,g);v>_&&(v=_),d>b&&(d=b),_>m&&(m=_),b>y&&(y=b),f.push(_),h.push(b)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=lu();if(k.add=function(n){i(k,n,+x(n,++g),+M(n,g),v,d,m,y)},k.visit=function(n){fu(n,k,v,d,m,y)},g=-1,null==t){for(;++g<p;)i(k,n[g],f[g],h[g],v,d,m,y);--g}else n.forEach(k.add);return f=h=n=l=null,k}var o,a=Nr,c=Ar;return(o=arguments.length)?(a=cu,c=su,3===o&&(u=e,r=t,e=t=0),i(n)):(i.x=function(n){return arguments.length?(a=n,i):a},i.y=function(n){return arguments.length?(c=n,i):c},i.extent=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],u=+n[1][1]),i):null==t?null:[[t,e],[r,u]]},i.size=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=e=0,r=+n[0],u=+n[1]),i):null==t?null:[r-t,u-e]},i)},Go.interpolateRgb=hu,Go.interpolateObject=gu,Go.interpolateNumber=pu,Go.interpolateString=vu;var rs=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;Go.interpolate=du,Go.interpolators=[function(n,t){var e=typeof t;return("string"===e?Ja.has(t)||/^(#|rgb\(|hsl\()/.test(t)?hu:vu:t instanceof et?hu:Array.isArray(t)?mu:"object"===e&&isNaN(t)?gu:pu)(n,t)}],Go.interpolateArray=mu;var us=function(){return Nt},is=Go.map({linear:us,poly:Su,quad:function(){return _u},cubic:function(){return bu},sin:function(){return ku},exp:function(){return Eu},circle:function(){return Nu},elastic:Au,back:Cu,bounce:function(){return Lu}}),os=Go.map({"in":Nt,out:xu,"in-out":Mu,"out-in":function(n){return Mu(xu(n))}});Go.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.substring(0,t):n,r=t>=0?n.substring(t+1):"in";return e=is.get(e)||us,r=os.get(r)||Nt,yu(r(e.apply(null,Ko.call(arguments,1))))},Go.interpolateHcl=Tu,Go.interpolateHsl=qu,Go.interpolateLab=zu,Go.interpolateRound=Ru,Go.transform=function(n){var t=na.createElementNS(Go.ns.prefix.svg,"g");return(Go.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Du(e?e.matrix:as)})(n)},Du.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var as={a:1,b:0,c:0,d:1,e:0,f:0};Go.interpolateTransform=Hu,Go.layout={},Go.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Yu(n[e]));return t}},Go.layout.chord=function(){function n(){var n,s,f,h,g,p={},v=[],d=Go.range(i),m=[];for(e=[],r=[],n=0,h=-1;++h<i;){for(s=0,g=-1;++g<i;)s+=u[h][g];v.push(s),m.push(Go.range(i)),n+=s}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&m.forEach(function(n,t){n.sort(function(n,e){return a(u[t][n],u[t][e])})}),n=(Ca-l*i)/n,s=0,h=-1;++h<i;){for(f=s,g=-1;++g<i;){var y=d[h],x=m[y][g],M=u[y][x],_=s,b=s+=M*n;p[y+"-"+x]={index:y,subindex:x,startAngle:_,endAngle:b,value:M}}r[y]={index:y,startAngle:f,endAngle:s,value:(s-f)/n},s+=l}for(h=-1;++h<i;)for(g=h-1;++g<i;){var w=p[h+"-"+g],S=p[g+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}c&&t()}function t(){e.sort(function(n,t){return c((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,u,i,o,a,c,s={},l=0;return s.matrix=function(n){return arguments.length?(i=(u=n)&&u.length,e=r=null,s):u},s.padding=function(n){return arguments.length?(l=n,e=r=null,s):l},s.sortGroups=function(n){return arguments.length?(o=n,e=r=null,s):o},s.sortSubgroups=function(n){return arguments.length?(a=n,e=null,s):a},s.sortChords=function(n){return arguments.length?(c=n,e&&t(),s):c},s.chords=function(){return e||n(),e},s.groups=function(){return r||n(),r},s},Go.layout.force=function(){function n(n){return function(t,e,r,u){if(t.point!==n){var i=t.cx-n.x,o=t.cy-n.y,a=u-e,c=i*i+o*o;if(c>a*a/d){if(p>c){var s=t.charge/c;n.px-=i*s,n.py-=o*s}return!0}if(t.point&&c&&p>c){var s=t.pointCharge/c;n.px-=i*s,n.py-=o*s}}return!t.charge}}function t(n){n.px=Go.event.x,n.py=Go.event.y,a.resume()}var e,r,u,i,o,a={},c=Go.dispatch("start","tick","end"),s=[1,1],l=.9,f=cs,h=ss,g=-30,p=ls,v=.1,d=.64,m=[],y=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,p,d,x,M,_=m.length,b=y.length;for(e=0;b>e;++e)a=y[e],f=a.source,h=a.target,x=h.x-f.x,M=h.y-f.y,(p=x*x+M*M)&&(p=r*i[e]*((p=Math.sqrt(p))-u[e])/p,x*=p,M*=p,h.x-=x*(d=f.weight/(h.weight+f.weight)),h.y-=M*d,f.x+=x*(d=1-d),f.y+=M*d);if((d=r*v)&&(x=s[0]/2,M=s[1]/2,e=-1,d))for(;++e<_;)a=m[e],a.x+=(x-a.x)*d,a.y+=(M-a.y)*d;if(g)for(Ju(t=Go.geom.quadtree(m),r,o),e=-1;++e<_;)(a=m[e]).fixed||t.visit(n(a));for(e=-1;++e<_;)a=m[e],a.fixed?(a.x=a.px,a.y=a.py):(a.x-=(a.px-(a.px=a.x))*l,a.y-=(a.py-(a.py=a.y))*l);c.tick({type:"tick",alpha:r})},a.nodes=function(n){return arguments.length?(m=n,a):m},a.links=function(n){return arguments.length?(y=n,a):y},a.size=function(n){return arguments.length?(s=n,a):s},a.linkDistance=function(n){return arguments.length?(f="function"==typeof n?n:+n,a):f},a.distance=a.linkDistance,a.linkStrength=function(n){return arguments.length?(h="function"==typeof n?n:+n,a):h},a.friction=function(n){return arguments.length?(l=+n,a):l},a.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,a):g},a.chargeDistance=function(n){return arguments.length?(p=n*n,a):Math.sqrt(p)},a.gravity=function(n){return arguments.length?(v=+n,a):v},a.theta=function(n){return arguments.length?(d=n*n,a):Math.sqrt(d)},a.alpha=function(n){return arguments.length?(n=+n,r?r=n>0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),Go.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=y[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,s=o.length;++a<s;)if(!isNaN(i=o[a][n]))return i;return Math.random()*r}var t,e,r,c=m.length,l=y.length,p=s[0],v=s[1];for(t=0;c>t;++t)(r=m[t]).index=t,r.weight=0;for(t=0;l>t;++t)r=y[t],"number"==typeof r.source&&(r.source=m[r.source]),"number"==typeof r.target&&(r.target=m[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;l>t;++t)u[t]=+f.call(this,y[t],t);else for(t=0;l>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;l>t;++t)i[t]=+h.call(this,y[t],t);else for(t=0;l>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=Go.behavior.drag().origin(Nt).on("dragstart.force",Vu).on("drag.force",t).on("dragend.force",$u)),arguments.length?(this.on("mouseover.force",Xu).on("mouseout.force",Bu).call(e),void 0):e},Go.rebind(a,c,"on")};var cs=20,ss=1,ls=1/0;Go.layout.hierarchy=function(){function n(t,o,a){var c=u.call(e,t,o);if(t.depth=o,a.push(t),c&&(s=c.length)){for(var s,l,f=-1,h=t.children=new Array(s),g=0,p=o+1;++f<s;)l=h[f]=n(c[f],p,a),l.parent=t,g+=l.value;r&&h.sort(r),i&&(t.value=g)}else delete t.children,i&&(t.value=+i.call(e,t,o)||0);return t}function t(n,r){var u=n.children,o=0;if(u&&(a=u.length))for(var a,c=-1,s=r+1;++c<a;)o+=t(u[c],s);else i&&(o=+i.call(e,n,r)||0);return i&&(n.value=o),o}function e(t){var e=[];return n(t,0,e),e}var r=Qu,u=Gu,i=Ku;return e.sort=function(n){return arguments.length?(r=n,e):r},e.children=function(n){return arguments.length?(u=n,e):u},e.value=function(n){return arguments.length?(i=n,e):i},e.revalue=function(n){return t(n,0),n},e},Go.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,s=-1;for(r=t.value?r/t.value:0;++s<o;)n(a=i[s],e,c=a.value*r,u),e+=c}}function t(n){var e=n.children,r=0;if(e&&(u=e.length))for(var u,i=-1;++i<u;)r=Math.max(r,t(e[i]));return 1+r}function e(e,i){var o=r.call(this,e,i);return n(o[0],0,u[0],u[1]/t(o[0])),o}var r=Go.layout.hierarchy(),u=[1,1];return e.size=function(n){return arguments.length?(u=n,e):u},Wu(e,r)},Go.layout.pie=function(){function n(i){var o=i.map(function(e,r){return+t.call(n,e,r)}),a=+("function"==typeof r?r.apply(this,arguments):r),c=(("function"==typeof u?u.apply(this,arguments):u)-a)/Go.sum(o),s=Go.range(i.length);null!=e&&s.sort(e===fs?function(n,t){return o[t]-o[n]}:function(n,t){return e(i[n],i[t])});var l=[];return s.forEach(function(n){var t;l[n]={data:i[n],value:t=o[n],startAngle:a,endAngle:a+=t*c}}),l}var t=Number,e=fs,r=0,u=Ca;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(u=t,n):u},n};var fs={};Go.layout.stack=function(){function n(a,c){var s=a.map(function(e,r){return t.call(n,e,r)}),l=s.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,l,c);s=Go.permute(s,f),l=Go.permute(l,f);var h,g,p,v=r.call(n,l,c),d=s.length,m=s[0].length;for(g=0;m>g;++g)for(u.call(n,s[0][g],p=v[g],l[0][g][1]),h=1;d>h;++h)u.call(n,s[h][g],p+=l[h-1][g][1],l[h][g][1]);return a}var t=Nt,e=ui,r=ii,u=ri,i=ti,o=ei;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:hs.get(t)||ui,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:gs.get(t)||ii,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var hs=Go.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(oi),i=n.map(ai),o=Go.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,s=[],l=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],s.push(e)):(c+=i[e],l.push(e));return l.reverse().concat(s)},reverse:function(n){return Go.range(n.length).reverse()},"default":ui}),gs=Go.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,s,l=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=s=0,e=1;h>e;++e){for(t=0,u=0;l>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];l>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,s>c&&(s=c)}for(e=0;h>e;++e)g[e]-=s;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ii});Go.layout.histogram=function(){function n(n,i){for(var o,a,c=[],s=n.map(e,this),l=r.call(this,s,i),f=u.call(this,l,s,i),i=-1,h=s.length,g=f.length-1,p=t?1:1/h;++i<g;)o=c[i]=[],o.dx=f[i+1]-(o.x=f[i]),o.y=0;if(g>0)for(i=-1;++i<h;)a=s[i],a>=l[0]&&a<=l[1]&&(o=c[Go.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=fi,u=si;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=Et(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return li(n,t)}:Et(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},Go.layout.tree=function(){function n(n,i){function o(n,t){var r=n.children,u=n._tree;if(r&&(i=r.length)){for(var i,a,s,l=r[0],f=l,h=-1;++h<i;)s=r[h],o(s,a),f=c(s,a,f),a=s;Mi(n);var g=.5*(l._tree.prelim+s._tree.prelim);t?(u.prelim=t._tree.prelim+e(n,t),u.mod=u.prelim-g):u.prelim=g}else t&&(u.prelim=t._tree.prelim+e(n,t))}function a(n,t){n.x=n._tree.prelim+t;var e=n.children;if(e&&(r=e.length)){var r,u=-1;for(t+=n._tree.mod;++u<r;)a(e[u],t)}}function c(n,t,r){if(t){for(var u,i=n,o=n,a=t,c=n.parent.children[0],s=i._tree.mod,l=o._tree.mod,f=a._tree.mod,h=c._tree.mod;a=pi(a),i=gi(i),a&&i;)c=gi(c),o=pi(o),o._tree.ancestor=n,u=a._tree.prelim+f-i._tree.prelim-s+e(a,i),u>0&&(_i(bi(a,n,r),n,u),s+=u,l+=u),f+=a._tree.mod,s+=i._tree.mod,h+=c._tree.mod,l+=o._tree.mod;a&&!pi(o)&&(o._tree.thread=a,o._tree.mod+=f-l),i&&!gi(c)&&(c._tree.thread=i,c._tree.mod+=s-h,r=n)}return r}var s=t.call(this,n,i),l=s[0];xi(l,function(n,t){n._tree={ancestor:n,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),o(l),a(l,-l._tree.prelim);var f=vi(l,mi),h=vi(l,di),g=vi(l,yi),p=f.x-e(f,h)/2,v=h.x+e(h,f)/2,d=g.depth||1;return xi(l,u?function(n){n.x*=r[0],n.y=n.depth*r[1],delete n._tree}:function(n){n.x=(n.x-p)/(v-p)*r[0],n.y=n.depth/d*r[1],delete n._tree}),s}var t=Go.layout.hierarchy().sort(null).value(null),e=hi,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Wu(n,t)},Go.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],s=u[1],l=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,xi(a,function(n){n.r=+l(n.value)}),xi(a,Ni),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/s))/2;xi(a,function(n){n.r+=f}),xi(a,Ni),xi(a,function(n){n.r-=f})}return Li(a,c/2,s/2,t?1:1/Math.max(2*a.r/c,2*a.r/s)),o}var t,e=Go.layout.hierarchy().sort(wi),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},Wu(n,e)},Go.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],s=0;xi(c,function(n){var t=n.children;t&&t.length?(n.x=zi(t),n.y=qi(t)):(n.x=o?s+=e(n,o):0,n.y=0,o=n)});var l=Ri(c),f=Di(c),h=l.x-e(l,f)/2,g=f.x+e(f,l)/2;return xi(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=Go.layout.hierarchy().sort(null).value(null),e=hi,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Wu(n,t)},Go.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++u<i;)r=(e=n[u]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,s=f(e),l=[],h=i.slice(),p=1/0,v="slice"===g?s.dx:"dice"===g?s.dy:"slice-dice"===g?1&e.depth?s.dy:s.dx:Math.min(s.dx,s.dy);for(n(h,s.dx*s.dy/e.value),l.area=0;(c=h.length)>0;)l.push(o=h[c-1]),l.area+=o.area,"squarify"!==g||(a=r(l,v))<=p?(h.pop(),p=a):(l.area-=l.pop().area,u(l,v,s,!1),v=Math.min(s.dx,s.dy),l.length=l.area=0,p=1/0);l.length&&(u(l,v,s,!0),l.length=l.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(i>e&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,s=e.y,l=t?c(n.area/t):0;if(t==e.dx){for((r||l>e.dy)&&(l=e.dy);++i<o;)u=n[i],u.x=a,u.y=s,u.dy=l,a+=u.dx=Math.min(e.x+e.dx-a,l?c(u.area/l):0);u.z=!0,u.dx+=e.x+e.dx-a,e.y+=l,e.dy-=l}else{for((r||l>e.dx)&&(l=e.dx);++i<o;)u=n[i],u.x=a,u.y=s,u.dx=l,s+=u.dy=Math.min(e.y+e.dy-s,l?c(u.area/l):0);u.z=!1,u.dy+=e.y+e.dy-s,e.x+=l,e.dx-=l}}function i(r){var u=o||a(r),i=u[0];return i.x=0,i.y=0,i.dx=s[0],i.dy=s[1],o&&a.revalue(i),n([i],i.dx*i.dy/i.value),(o?e:t)(i),h&&(o=u),u}var o,a=Go.layout.hierarchy(),c=Math.round,s=[1,1],l=null,f=Pi,h=!1,g="squarify",p=.5*(1+Math.sqrt(5));return i.size=function(n){return arguments.length?(s=n,i):s},i.padding=function(n){function t(t){var e=n.call(i,t,t.depth);return null==e?Pi(t):Ui(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return Ui(t,n)}if(!arguments.length)return l;var r;return f=null==(l=n)?Pi:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,i},i.round=function(n){return arguments.length?(c=n?Math.round:Number,i):c!=Number},i.sticky=function(n){return arguments.length?(h=n,o=null,i):h},i.ratio=function(n){return arguments.length?(p=n,i):p},i.mode=function(n){return arguments.length?(g=n+"",i):g},Wu(i,a)},Go.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=Go.random.normal.apply(Go,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=Go.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},Go.scale={};var ps={floor:Nt,ceil:Nt};Go.scale.linear=function(){return Zi([0,1],[0,1],du,!1)};var vs={s:1,g:1,p:1,r:1,e:1};Go.scale.log=function(){return Ki(Go.scale.linear().domain([0,1]),10,!0,[1,10])};var ds=Go.format(".0e"),ms={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};Go.scale.pow=function(){return Qi(Go.scale.linear(),1,[0,1])},Go.scale.sqrt=function(){return Go.scale.pow().exponent(.5)},Go.scale.ordinal=function(){return to([],{t:"range",a:[[]]})},Go.scale.category10=function(){return Go.scale.ordinal().range(ys)},Go.scale.category20=function(){return Go.scale.ordinal().range(xs)},Go.scale.category20b=function(){return Go.scale.ordinal().range(Ms)},Go.scale.category20c=function(){return Go.scale.ordinal().range(_s)};var ys=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(mt),xs=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(mt),Ms=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(mt),_s=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(mt);Go.scale.quantile=function(){return eo([],[]) +},Go.scale.quantize=function(){return ro(0,1,[0,1])},Go.scale.threshold=function(){return uo([.5],[0,1])},Go.scale.identity=function(){return io([0,1])},Go.svg={},Go.svg.arc=function(){function n(){var n=t.apply(this,arguments),i=e.apply(this,arguments),o=r.apply(this,arguments)+bs,a=u.apply(this,arguments)+bs,c=(o>a&&(c=o,o=a,a=c),a-o),s=Aa>c?"0":"1",l=Math.cos(o),f=Math.sin(o),h=Math.cos(a),g=Math.sin(a);return c>=ws?n?"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"M0,"+n+"A"+n+","+n+" 0 1,0 0,"+-n+"A"+n+","+n+" 0 1,0 0,"+n+"Z":"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"Z":n?"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L"+n*h+","+n*g+"A"+n+","+n+" 0 "+s+",0 "+n*l+","+n*f+"Z":"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L0,0"+"Z"}var t=oo,e=ao,r=co,u=so;return n.innerRadius=function(e){return arguments.length?(t=Et(e),n):t},n.outerRadius=function(t){return arguments.length?(e=Et(t),n):e},n.startAngle=function(t){return arguments.length?(r=Et(t),n):r},n.endAngle=function(t){return arguments.length?(u=Et(t),n):u},n.centroid=function(){var n=(t.apply(this,arguments)+e.apply(this,arguments))/2,i=(r.apply(this,arguments)+u.apply(this,arguments))/2+bs;return[Math.cos(i)*n,Math.sin(i)*n]},n};var bs=-La,ws=Ca-Ta;Go.svg.line=function(){return lo(Nt)};var Ss=Go.map({linear:fo,"linear-closed":ho,step:go,"step-before":po,"step-after":vo,basis:bo,"basis-open":wo,"basis-closed":So,bundle:ko,cardinal:xo,"cardinal-open":mo,"cardinal-closed":yo,monotone:To});Ss.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var ks=[0,2/3,1/3,0],Es=[0,1/3,2/3,0],Ns=[0,1/6,2/3,1/6];Go.svg.line.radial=function(){var n=lo(qo);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},po.reverse=vo,vo.reverse=po,Go.svg.area=function(){return zo(Nt)},Go.svg.area.radial=function(){var n=zo(qo);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},Go.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),s=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,s)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,s.r,s.p0)+r(s.r,s.p1,s.a1-s.a0)+u(s.r,s.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)+bs,l=s.call(n,u,r)+bs;return{r:i,a0:o,a1:l,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(l),i*Math.sin(l)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Aa)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=mr,o=yr,a=Ro,c=co,s=so;return n.radius=function(t){return arguments.length?(a=Et(t),n):a},n.source=function(t){return arguments.length?(i=Et(t),n):i},n.target=function(t){return arguments.length?(o=Et(t),n):o},n.startAngle=function(t){return arguments.length?(c=Et(t),n):c},n.endAngle=function(t){return arguments.length?(s=Et(t),n):s},n},Go.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=mr,e=yr,r=Do;return n.source=function(e){return arguments.length?(t=Et(e),n):t},n.target=function(t){return arguments.length?(e=Et(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},Go.svg.diagonal.radial=function(){var n=Go.svg.diagonal(),t=Do,e=n.projection;return n.projection=function(n){return arguments.length?e(Po(t=n)):t},n},Go.svg.symbol=function(){function n(n,r){return(As.get(t.call(this,n,r))||Ho)(e.call(this,n,r))}var t=jo,e=Uo;return n.type=function(e){return arguments.length?(t=Et(e),n):t},n.size=function(t){return arguments.length?(e=Et(t),n):e},n};var As=Go.map({circle:Ho,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*qs)),e=t*qs;return"M0,"+-t+"L"+e+",0"+" 0,"+t+" "+-e+",0"+"Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/Ts),e=t*Ts/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/Ts),e=t*Ts/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});Go.svg.symbolTypes=As.keys();var Cs,Ls,Ts=Math.sqrt(3),qs=Math.tan(30*za),zs=[],Rs=0;zs.call=_a.call,zs.empty=_a.empty,zs.node=_a.node,zs.size=_a.size,Go.transition=function(n){return arguments.length?Cs?n.transition():n:Sa.transition()},Go.transition.prototype=zs,zs.select=function(n){var t,e,r,u=this.id,i=[];n=b(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]);for(var c=this[o],s=-1,l=c.length;++s<l;)(r=c[s])&&(e=n.call(r,r.__data__,s,o))?("__data__"in r&&(e.__data__=r.__data__),Io(e,s,u,r.__transition__[u]),t.push(e)):t.push(null)}return Fo(i,u)},zs.selectAll=function(n){var t,e,r,u,i,o=this.id,a=[];n=w(n);for(var c=-1,s=this.length;++c<s;)for(var l=this[c],f=-1,h=l.length;++f<h;)if(r=l[f]){i=r.__transition__[o],e=n.call(r,r.__data__,f,c),a.push(t=[]);for(var g=-1,p=e.length;++g<p;)(u=e[g])&&Io(u,g,o,i),t.push(u)}return Fo(a,o)},zs.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=R(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return Fo(u,this.id)},zs.tween=function(n,t){var e=this.id;return arguments.length<2?this.node().__transition__[e].tween.get(n):P(this,null==t?function(t){t.__transition__[e].tween.remove(n)}:function(r){r.__transition__[e].tween.set(n,t)})},zs.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Hu:du,a=Go.ns.qualify(n);return Oo(this,"attr."+n,t,a.local?i:u)},zs.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=Go.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},zs.style=function(n,t,e){function r(){this.style.removeProperty(n)}function u(t){return null==t?r:(t+="",function(){var r,u=ea.getComputedStyle(this,null).getPropertyValue(n);return u!==t&&(r=du(u,t),function(t){this.style.setProperty(n,r(t),e)})})}var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(t="");for(e in n)this.style(e,n[e],t);return this}e=""}return Oo(this,"style."+n,t,u)},zs.styleTween=function(n,t,e){function r(r,u){var i=t.call(this,r,u,ea.getComputedStyle(this,null).getPropertyValue(n));return i&&function(t){this.style.setProperty(n,i(t),e)}}return arguments.length<3&&(e=""),this.tween("style."+n,r)},zs.text=function(n){return Oo(this,"text",n,Yo)},zs.remove=function(){return this.each("end.transition",function(){var n;this.__transition__.count<2&&(n=this.parentNode)&&n.removeChild(this)})},zs.ease=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].ease:("function"!=typeof n&&(n=Go.ease.apply(Go,arguments)),P(this,function(e){e.__transition__[t].ease=n}))},zs.delay=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].delay:P(this,"function"==typeof n?function(e,r,u){e.__transition__[t].delay=+n.call(e,e.__data__,r,u)}:(n=+n,function(e){e.__transition__[t].delay=n}))},zs.duration=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].duration:P(this,"function"==typeof n?function(e,r,u){e.__transition__[t].duration=Math.max(1,n.call(e,e.__data__,r,u))}:(n=Math.max(1,n),function(e){e.__transition__[t].duration=n}))},zs.each=function(n,t){var e=this.id;if(arguments.length<2){var r=Ls,u=Cs;Cs=e,P(this,function(t,r,u){Ls=t.__transition__[e],n.call(t,t.__data__,r,u)}),Ls=r,Cs=u}else P(this,function(r){var u=r.__transition__[e];(u.event||(u.event=Go.dispatch("start","end"))).on(n,t)});return this},zs.transition=function(){for(var n,t,e,r,u=this.id,i=++Rs,o=[],a=0,c=this.length;c>a;a++){o.push(n=[]);for(var t=this[a],s=0,l=t.length;l>s;s++)(e=t[s])&&(r=Object.create(e.__transition__[u]),r.delay+=r.duration,Io(e,s,i,r)),n.push(e)}return Fo(o,i)},Go.svg.axis=function(){function n(n){n.each(function(){var n,s=Go.select(this),l=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):Nt:t,p=s.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Ta),d=Go.transition(p.exit()).style("opacity",Ta).remove(),m=Go.transition(p.order()).style("opacity",1),y=Hi(f),x=s.selectAll(".domain").data([0]),M=(x.enter().append("path").attr("class","domain"),Go.transition(x));v.append("line"),v.append("text");var _=v.select("line"),b=m.select("line"),w=p.select("text").text(g),S=v.select("text"),k=m.select("text");switch(r){case"bottom":n=Zo,_.attr("y2",u),S.attr("y",Math.max(u,0)+o),b.attr("x2",0).attr("y2",u),k.attr("x",0).attr("y",Math.max(u,0)+o),w.attr("dy",".71em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+i+"V0H"+y[1]+"V"+i);break;case"top":n=Zo,_.attr("y2",-u),S.attr("y",-(Math.max(u,0)+o)),b.attr("x2",0).attr("y2",-u),k.attr("x",0).attr("y",-(Math.max(u,0)+o)),w.attr("dy","0em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+-i+"V0H"+y[1]+"V"+-i);break;case"left":n=Vo,_.attr("x2",-u),S.attr("x",-(Math.max(u,0)+o)),b.attr("x2",-u).attr("y2",0),k.attr("x",-(Math.max(u,0)+o)).attr("y",0),w.attr("dy",".32em").style("text-anchor","end"),M.attr("d","M"+-i+","+y[0]+"H0V"+y[1]+"H"+-i);break;case"right":n=Vo,_.attr("x2",u),S.attr("x",Math.max(u,0)+o),b.attr("x2",u).attr("y2",0),k.attr("x",Math.max(u,0)+o).attr("y",0),w.attr("dy",".32em").style("text-anchor","start"),M.attr("d","M"+i+","+y[0]+"H0V"+y[1]+"H"+i)}if(f.rangeBand){var E=f,N=E.rangeBand()/2;l=f=function(n){return E(n)+N}}else l.rangeBand?l=f:d.call(n,f);v.call(n,l),m.call(n,f)})}var t,e=Go.scale.linear(),r=Ds,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Ps?t+"":Ds,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Ds="bottom",Ps={top:1,right:1,bottom:1,left:1};Go.svg.brush=function(){function n(i){i.each(function(){var i=Go.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=i.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),i.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=i.selectAll(".resize").data(p,Nt);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return Us[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,f=Go.transition(i),h=Go.transition(o);c&&(l=Hi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),e(f)),s&&(l=Hi(s),h.attr("y",l[0]).attr("height",l[1]-l[0]),r(f)),t(f)})}function t(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+l[+/e$/.test(n)]+","+f[+/^s/.test(n)]+")"})}function e(n){n.select(".extent").attr("x",l[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",l[1]-l[0])}function r(n){n.select(".extent").attr("y",f[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",f[1]-f[0])}function u(){function u(){32==Go.event.keyCode&&(A||(x=null,L[0]-=l[1],L[1]-=f[1],A=2),y())}function p(){32==Go.event.keyCode&&2==A&&(L[0]+=l[1],L[1]+=f[1],A=0,y())}function v(){var n=Go.mouse(_),u=!1;M&&(n[0]+=M[0],n[1]+=M[1]),A||(Go.event.altKey?(x||(x=[(l[0]+l[1])/2,(f[0]+f[1])/2]),L[0]=l[+(n[0]<x[0])],L[1]=f[+(n[1]<x[1])]):x=null),E&&d(n,c,0)&&(e(S),u=!0),N&&d(n,s,1)&&(r(S),u=!0),u&&(t(S),w({type:"brush",mode:A?"move":"resize"}))}function d(n,t,e){var r,u,a=Hi(t),c=a[0],s=a[1],p=L[e],v=e?f:l,d=v[1]-v[0];return A&&(c-=p,s-=d+p),r=(e?g:h)?Math.max(c,Math.min(s,n[e])):n[e],A?u=(r+=p)+d:(x&&(p=Math.max(c,Math.min(s,2*x[e]-r))),r>p?(u=r,r=p):u=p),v[0]!=r||v[1]!=u?(e?o=null:i=null,v[0]=r,v[1]=u,!0):void 0}function m(){v(),S.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),Go.select("body").style("cursor",null),T.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),C(),w({type:"brushend"})}var x,M,_=this,b=Go.select(Go.event.target),w=a.of(_,arguments),S=Go.select(_),k=b.datum(),E=!/^(n|s)$/.test(k)&&c,N=!/^(e|w)$/.test(k)&&s,A=b.classed("extent"),C=I(),L=Go.mouse(_),T=Go.select(ea).on("keydown.brush",u).on("keyup.brush",p);if(Go.event.changedTouches?T.on("touchmove.brush",v).on("touchend.brush",m):T.on("mousemove.brush",v).on("mouseup.brush",m),S.interrupt().selectAll("*").interrupt(),A)L[0]=l[0]-L[0],L[1]=f[0]-L[1];else if(k){var q=+/w$/.test(k),z=+/^n/.test(k);M=[l[1-q]-L[0],f[1-z]-L[1]],L[0]=l[q],L[1]=f[z]}else Go.event.altKey&&(x=L.slice());S.style("pointer-events","none").selectAll(".resize").style("display",null),Go.select("body").style("cursor",b.style("cursor")),w({type:"brushstart"}),v()}var i,o,a=M(n,"brushstart","brush","brushend"),c=null,s=null,l=[0,0],f=[0,0],h=!0,g=!0,p=js[0];return n.event=function(n){n.each(function(){var n=a.of(this,arguments),t={x:l,y:f,i:i,j:o},e=this.__chart__||t;this.__chart__=t,Cs?Go.select(this).transition().each("start.brush",function(){i=e.i,o=e.j,l=e.x,f=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=mu(l,t.x),r=mu(f,t.y);return i=o=null,function(u){l=t.x=e(u),f=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){i=t.i,o=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,p=js[!c<<1|!s],n):c},n.y=function(t){return arguments.length?(s=t,p=js[!c<<1|!s],n):s},n.clamp=function(t){return arguments.length?(c&&s?(h=!!t[0],g=!!t[1]):c?h=!!t:s&&(g=!!t),n):c&&s?[h,g]:c?h:s?g:null},n.extent=function(t){var e,r,u,a,h;return arguments.length?(c&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),i=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(h=e,e=r,r=h),(e!=l[0]||r!=l[1])&&(l=[e,r])),s&&(u=t[0],a=t[1],c&&(u=u[1],a=a[1]),o=[u,a],s.invert&&(u=s(u),a=s(a)),u>a&&(h=u,u=a,a=h),(u!=f[0]||a!=f[1])&&(f=[u,a])),n):(c&&(i?(e=i[0],r=i[1]):(e=l[0],r=l[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(h=e,e=r,r=h))),s&&(o?(u=o[0],a=o[1]):(u=f[0],a=f[1],s.invert&&(u=s.invert(u),a=s.invert(a)),u>a&&(h=u,u=a,a=h))),c&&s?[[e,u],[r,a]]:c?[e,r]:s&&[u,a])},n.clear=function(){return n.empty()||(l=[0,0],f=[0,0],i=o=null),n},n.empty=function(){return!!c&&l[0]==l[1]||!!s&&f[0]==f[1]},Go.rebind(n,a,"on")};var Us={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},js=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Hs=ic.format=fc.timeFormat,Fs=Hs.utc,Os=Fs("%Y-%m-%dT%H:%M:%S.%LZ");Hs.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?$o:Os,$o.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},$o.toString=Os.toString,ic.second=Ht(function(n){return new oc(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ic.seconds=ic.second.range,ic.seconds.utc=ic.second.utc.range,ic.minute=Ht(function(n){return new oc(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ic.minutes=ic.minute.range,ic.minutes.utc=ic.minute.utc.range,ic.hour=Ht(function(n){var t=n.getTimezoneOffset()/60;return new oc(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ic.hours=ic.hour.range,ic.hours.utc=ic.hour.utc.range,ic.month=Ht(function(n){return n=ic.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ic.months=ic.month.range,ic.months.utc=ic.month.utc.range;var Ys=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Is=[[ic.second,1],[ic.second,5],[ic.second,15],[ic.second,30],[ic.minute,1],[ic.minute,5],[ic.minute,15],[ic.minute,30],[ic.hour,1],[ic.hour,3],[ic.hour,6],[ic.hour,12],[ic.day,1],[ic.day,2],[ic.week,1],[ic.month,1],[ic.month,3],[ic.year,1]],Zs=Hs.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",Ne]]),Vs={range:function(n,t,e){return Go.range(Math.ceil(n/e)*e,+t,e).map(Bo)},floor:Nt,ceil:Nt};Is.year=ic.year,ic.scale=function(){return Xo(Go.scale.linear(),Is,Zs)};var $s=Is.map(function(n){return[n[0].utc,n[1]]}),Xs=Fs.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",Ne]]);$s.year=ic.year.utc,ic.scale.utc=function(){return Xo(Go.scale.linear(),$s,Xs)},Go.text=At(function(n){return n.responseText}),Go.json=function(n,t){return Ct(n,"application/json",Jo,t)},Go.html=function(n,t){return Ct(n,"text/html",Wo,t)},Go.xml=At(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(Go):"object"==typeof module&&module.exports?module.exports=Go:this.d3=Go}();
\ No newline at end of file diff --git a/src/usr/local/www/d3pie/d3pie.min.js b/src/usr/local/www/d3pie/d3pie.min.js new file mode 100644 index 0000000..8bf6e37 --- /dev/null +++ b/src/usr/local/www/d3pie/d3pie.min.js @@ -0,0 +1,9 @@ +/*! +* d3pie +* @author Ben Keen +* @version 0.1.8 +* @date April 2015 +* @repo http://github.com/benkeen/d3pie +*/ +!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b(require()):a.d3pie=b(a)}(this,function(){var a="d3pie",b="0.1.6",c=0,d={header:{title:{text:"",color:"#333333",fontSize:18,font:"arial"},subtitle:{text:"",color:"#666666",fontSize:14,font:"arial"},location:"top-center",titleSubtitlePadding:8},footer:{text:"",color:"#666666",fontSize:14,font:"arial",location:"left"},size:{canvasHeight:500,canvasWidth:500,pieInnerRadius:"0%",pieOuterRadius:null},data:{sortOrder:"none",ignoreSmallSegments:{enabled:!1,valueType:"percentage",value:null},smallSegmentGrouping:{enabled:!1,value:1,valueType:"percentage",label:"Other",color:"#cccccc"},content:[]},labels:{outer:{format:"label",hideWhenLessThanPercentage:null,pieDistance:30},inner:{format:"percentage",hideWhenLessThanPercentage:null},mainLabel:{color:"#333333",font:"arial",fontSize:10},percentage:{color:"#dddddd",font:"arial",fontSize:10,decimalPlaces:0},value:{color:"#cccc44",font:"arial",fontSize:10},lines:{enabled:!0,style:"curved",color:"segment"},truncation:{enabled:!1,truncateLength:30},formatter:null},effects:{load:{effect:"default",speed:1e3},pullOutSegmentOnClick:{effect:"bounce",speed:300,size:10},highlightSegmentOnMouseover:!0,highlightLuminosity:-.2},tooltips:{enabled:!1,type:"placeholder",string:"",placeholderParser:null,styles:{fadeInSpeed:250,backgroundColor:"#000000",backgroundOpacity:.5,color:"#efefef",borderRadius:2,font:"arial",fontSize:10,padding:4}},misc:{colors:{background:null,segments:["#2484c1","#65a620","#7b6888","#a05d56","#961a1a","#d8d23a","#e98125","#d0743c","#635222","#6ada6a","#0c6197","#7d9058","#207f33","#44b9b0","#bca44a","#e4a14b","#a3acb2","#8cc3e9","#69a6f9","#5b388f","#546e91","#8bde95","#d2ab58","#273c71","#98bf6e","#4daa4b","#98abc5","#cc1010","#31383b","#006391","#c2643f","#b0a474","#a5a39c","#a9c2bc","#22af8c","#7fcecf","#987ac6","#3d3b87","#b77b1c","#c9c2b6","#807ece","#8db27c","#be66a2","#9ed3c6","#00644b","#005064","#77979f","#77e079","#9c73ab","#1f79a7"],segmentStroke:"#ffffff"},gradient:{enabled:!1,percentage:95,color:"#000000"},canvasPadding:{top:5,right:5,bottom:5,left:5},pieCenterOffset:{x:0,y:0},cssPrefix:null},callbacks:{onload:null,onMouseoverSegment:null,onMouseoutSegment:null,onClickSegment:null}},e={initialCheck:function(a){var b=a.cssPrefix,c=a.element,d=a.options;if(!window.d3||!window.d3.hasOwnProperty("version"))return console.error("d3pie error: d3 is not available"),!1;if(!(c instanceof HTMLElement||c instanceof SVGElement))return console.error("d3pie error: the first d3pie() param must be a valid DOM element (not jQuery) or a ID string."),!1;if(!/[a-zA-Z][a-zA-Z0-9_-]*$/.test(b))return console.error("d3pie error: invalid options.misc.cssPrefix"),!1;if(!f.isArray(d.data.content))return console.error("d3pie error: invalid config structure: missing data.content property."),!1;if(0===d.data.content.length)return console.error("d3pie error: no data supplied."),!1;for(var e=[],g=0;g<d.data.content.length;g++)"number"!=typeof d.data.content[g].value||isNaN(d.data.content[g].value)?console.log("not valid: ",d.data.content[g]):d.data.content[g].value<=0?console.log("not valid - should have positive value: ",d.data.content[g]):e.push(d.data.content[g]);return a.options.data.content=e,!0}},f={addSVGSpace:function(a){var b=a.element,c=a.options.size.canvasWidth,d=a.options.size.canvasHeight,e=a.options.misc.colors.background,f=d3.select(b).append("svg:svg").attr("width",c).attr("height",d);return"transparent"!==e&&f.style("background-color",function(){return e}),f},whenIdExists:function(a,b){var c=1,d=1e3,e=setInterval(function(){document.getElementById(a)&&(clearInterval(e),b()),c>d&&clearInterval(e),c++},1)},whenElementsExist:function(a,b){var c=1,d=1e3,e=setInterval(function(){for(var f=!0,g=0;g<a.length;g++)if(!document.getElementById(a[g])){f=!1;break}f&&(clearInterval(e),b()),c>d&&clearInterval(e),c++},1)},shuffleArray:function(a){for(var b,c,d=a.length;0!==d;)c=Math.floor(Math.random()*d),d-=1,b=a[d],a[d]=a[c],a[c]=b;return a},processObj:function(a,b,c){return"string"==typeof b?f.processObj(a,b.split("."),c):1===b.length&&void 0!==c?(a[b[0]]=c,a[b[0]]):0===b.length?a:f.processObj(a[b[0]],b.slice(1),c)},getDimensions:function(a){var b=document.getElementById(a),c=0,d=0;if(b){var e=b.getBBox();c=e.width,d=e.height}else console.log("error: getDimensions() "+a+" not found.");return{w:c,h:d}},rectIntersect:function(a,b){var c=b.x>a.x+a.w||b.x+b.w<a.x||b.y+b.h<a.y||b.y>a.y+a.h;return!c},getColorShade:function(a,b){a=String(a).replace(/[^0-9a-f]/gi,""),a.length<6&&(a=a[0]+a[0]+a[1]+a[1]+a[2]+a[2]),b=b||0;for(var c="#",d=0;3>d;d++){var e=parseInt(a.substr(2*d,2),16);e=Math.round(Math.min(Math.max(0,e+e*b),255)).toString(16),c+=("00"+e).substr(e.length)}return c},initSegmentColors:function(a){for(var b=a.options.data.content,c=a.options.misc.colors.segments,d=[],e=0;e<b.length;e++)d.push(b[e].hasOwnProperty("color")?b[e].color:c[e]);return d},applySmallSegmentGrouping:function(a,b){var c;"percentage"===b.valueType&&(c=h.getTotalPieSize(a));for(var d=[],e=[],f=0,g=0;g<a.length;g++)if("percentage"===b.valueType){var i=a[g].value/c*100;if(i<=b.value){e.push(a[g]),f+=a[g].value;continue}a[g].isGrouped=!1,d.push(a[g])}else{if(a[g].value<=b.value){e.push(a[g]),f+=a[g].value;continue}a[g].isGrouped=!1,d.push(a[g])}return e.length&&d.push({color:b.color,label:b.label,value:f,isGrouped:!0,groupedData:e}),d},showPoint:function(a,b,c){a.append("circle").attr("cx",b).attr("cy",c).attr("r",2).style("fill","black")},isFunction:function(a){var b={};return a&&"[object Function]"===b.toString.call(a)},isArray:function(a){return"[object Array]"===Object.prototype.toString.call(a)}},g=function(){var a,b,c,d,e,f,h=arguments[0]||{},i=1,j=arguments.length,k=!1,l=Object.prototype.toString,m=Object.prototype.hasOwnProperty,n={"[object Boolean]":"boolean","[object Number]":"number","[object String]":"string","[object Function]":"function","[object Array]":"array","[object Date]":"date","[object RegExp]":"regexp","[object Object]":"object"},o={isFunction:function(a){return"function"===o.type(a)},isArray:Array.isArray||function(a){return"array"===o.type(a)},isWindow:function(a){return null!==a&&a===a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return null===a?String(a):n[l.call(a)]||"object"},isPlainObject:function(a){if(!a||"object"!==o.type(a)||a.nodeType)return!1;try{if(a.constructor&&!m.call(a,"constructor")&&!m.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(b){return!1}var c;for(c in a);return void 0===c||m.call(a,c)}};for("boolean"==typeof h&&(k=h,h=arguments[1]||{},i=2),"object"==typeof h||o.isFunction(h)||(h={}),j===i&&(h=this,--i),i;j>i;i++)if(null!==(a=arguments[i]))for(b in a)c=h[b],d=a[b],h!==d&&(k&&d&&(o.isPlainObject(d)||(e=o.isArray(d)))?(e?(e=!1,f=c&&o.isArray(c)?c:[]):f=c&&o.isPlainObject(c)?c:{},h[b]=g(k,f,d)):void 0!==d&&(h[b]=d));return h},h={toRadians:function(a){return a*(Math.PI/180)},toDegrees:function(a){return a*(180/Math.PI)},computePieRadius:function(a){var b=a.options.size,c=a.options.misc.canvasPadding,d=b.canvasWidth-c.left-c.right,e=b.canvasHeight-c.top-c.bottom;"pie-center"!==a.options.header.location&&(e-=a.textComponents.headerHeight),a.textComponents.footer.exists&&(e-=a.textComponents.footer.h),e=0>e?0:e;var f,g,h=(e>d?d:e)/3;if(null!==b.pieOuterRadius)if(/%/.test(b.pieOuterRadius)){g=parseInt(b.pieOuterRadius.replace(/[\D]/,""),10),g=g>99?99:g,g=0>g?0:g;var i=e>d?d:e;if("none"!==a.options.labels.outer.format){var j=2*parseInt(a.options.labels.outer.pieDistance,10);i-j>0&&(i-=j)}h=Math.floor(i/100*g)/2}else h=parseInt(b.pieOuterRadius,10);/%/.test(b.pieInnerRadius)?(g=parseInt(b.pieInnerRadius.replace(/[\D]/,""),10),g=g>99?99:g,g=0>g?0:g,f=Math.floor(h/100*g)):f=parseInt(b.pieInnerRadius,10),a.innerRadius=f,a.outerRadius=h},getTotalPieSize:function(a){for(var b=0,c=0;c<a.length;c++)b+=a[c].value;return b},sortPieData:function(a){var b=a.options.data.content,c=a.options.data.sortOrder;switch(c){case"none":break;case"random":b=f.shuffleArray(b);break;case"value-asc":b.sort(function(a,b){return a.value<b.value?-1:1});break;case"value-desc":b.sort(function(a,b){return a.value<b.value?1:-1});break;case"label-asc":b.sort(function(a,b){return a.label.toLowerCase()>b.label.toLowerCase()?1:-1});break;case"label-desc":b.sort(function(a,b){return a.label.toLowerCase()<b.label.toLowerCase()?1:-1})}return b},getPieTranslateCenter:function(a){return"translate("+a.x+","+a.y+")"},calculatePieCenter:function(a){var b=a.options.misc.pieCenterOffset,c=a.textComponents.title.exists&&"pie-center"!==a.options.header.location,d=a.textComponents.subtitle.exists&&"pie-center"!==a.options.header.location,e=a.options.misc.canvasPadding.top;c&&d?e+=a.textComponents.title.h+a.options.header.titleSubtitlePadding+a.textComponents.subtitle.h:c?e+=a.textComponents.title.h:d&&(e+=a.textComponents.subtitle.h);var f=0;a.textComponents.footer.exists&&(f=a.textComponents.footer.h+a.options.misc.canvasPadding.bottom);var g=(a.options.size.canvasWidth-a.options.misc.canvasPadding.left-a.options.misc.canvasPadding.right)/2+a.options.misc.canvasPadding.left,h=(a.options.size.canvasHeight-f-e)/2+e;g+=b.x,h+=b.y,a.pieCenter={x:g,y:h}},rotate:function(a,b,c,d,e){e=e*Math.PI/180;var f=Math.cos,g=Math.sin,h=(a-c)*f(e)-(b-d)*g(e)+c,i=(a-c)*g(e)+(b-d)*f(e)+d;return{x:h,y:i}},translate:function(a,b,c,d){var e=h.toRadians(d);return{x:a+c*Math.sin(e),y:b-c*Math.cos(e)}},pointIsInArc:function(a,b,c){var d=c.innerRadius()(b),e=c.outerRadius()(b),f=c.startAngle()(b),g=c.endAngle()(b),h=a.x*a.x+a.y*a.y,i=Math.atan2(a.x,-a.y);return i=0>i?i+2*Math.PI:i,h>=d*d&&e*e>=h&&i>=f&&g>=i}},i={add:function(a,b,c){var d=i.getIncludes(c),e=a.options.labels,f=a.svg.insert("g","."+a.cssPrefix+"labels-"+b).attr("class",a.cssPrefix+"labels-"+b),g=f.selectAll("."+a.cssPrefix+"labelGroup-"+b).data(a.options.data.content).enter().append("g").attr("id",function(c,d){return a.cssPrefix+"labelGroup"+d+"-"+b}).attr("data-index",function(a,b){return b}).attr("class",a.cssPrefix+"labelGroup-"+b).style("opacity",0),h={section:b,sectionDisplayType:c};d.mainLabel&&g.append("text").attr("id",function(c,d){return a.cssPrefix+"segmentMainLabel"+d+"-"+b}).attr("class",a.cssPrefix+"segmentMainLabel-"+b).text(function(a,b){var c=a.label;return e.formatter?(h.index=b,h.part="mainLabel",h.value=a.value,h.label=c,c=e.formatter(h)):e.truncation.enabled&&a.label.length>e.truncation.truncateLength&&(c=a.label.substring(0,e.truncation.truncateLength)+"..."),c}).style("font-size",e.mainLabel.fontSize+"px").style("font-family",e.mainLabel.font).style("fill",e.mainLabel.color),d.percentage&&g.append("text").attr("id",function(c,d){return a.cssPrefix+"segmentPercentage"+d+"-"+b}).attr("class",a.cssPrefix+"segmentPercentage-"+b).text(function(b,c){var d=j.getPercentage(a,c,a.options.labels.percentage.decimalPlaces);return e.formatter?(h.index=c,h.part="percentage",h.value=b.value,h.label=d,d=e.formatter(h)):d+="%",d}).style("font-size",e.percentage.fontSize+"px").style("font-family",e.percentage.font).style("fill",e.percentage.color),d.value&&g.append("text").attr("id",function(c,d){return a.cssPrefix+"segmentValue"+d+"-"+b}).attr("class",a.cssPrefix+"segmentValue-"+b).text(function(a,b){return h.index=b,h.part="value",h.value=a.value,h.label=a.value,e.formatter?e.formatter(h,a.value):a.value}).style("font-size",e.value.fontSize+"px").style("font-family",e.value.font).style("fill",e.value.color)},positionLabelElements:function(a,b,c){i["dimensions-"+b]=[];var d=d3.selectAll("."+a.cssPrefix+"labelGroup-"+b);d.each(function(c,d){var e=d3.select(this).selectAll("."+a.cssPrefix+"segmentMainLabel-"+b),f=d3.select(this).selectAll("."+a.cssPrefix+"segmentPercentage-"+b),g=d3.select(this).selectAll("."+a.cssPrefix+"segmentValue-"+b);i["dimensions-"+b].push({mainLabel:null!==e.node()?e.node().getBBox():null,percentage:null!==f.node()?f.node().getBBox():null,value:null!==g.node()?g.node().getBBox():null})});var e=5,f=i["dimensions-"+b];switch(c){case"label-value1":d3.selectAll("."+a.cssPrefix+"segmentValue-"+b).attr("dx",function(a,b){return f[b].mainLabel.width+e});break;case"label-value2":d3.selectAll("."+a.cssPrefix+"segmentValue-"+b).attr("dy",function(a,b){return f[b].mainLabel.height});break;case"label-percentage1":d3.selectAll("."+a.cssPrefix+"segmentPercentage-"+b).attr("dx",function(a,b){return f[b].mainLabel.width+e});break;case"label-percentage2":d3.selectAll("."+a.cssPrefix+"segmentPercentage-"+b).attr("dx",function(a,b){return f[b].mainLabel.width/2-f[b].percentage.width/2}).attr("dy",function(a,b){return f[b].mainLabel.height})}},computeLabelLinePositions:function(a){a.lineCoordGroups=[],d3.selectAll("."+a.cssPrefix+"labelGroup-outer").each(function(b,c){return i.computeLinePosition(a,c)})},computeLinePosition:function(a,b){var c,d,e,f,g=j.getSegmentAngle(b,a.options.data.content,a.totalSize,{midpoint:!0}),i=h.rotate(a.pieCenter.x,a.pieCenter.y-a.outerRadius,a.pieCenter.x,a.pieCenter.y,g),k=a.outerLabelGroupData[b].h/5,l=6,m=Math.floor(g/90),n=4;switch(2===m&&180===g&&(m=1),m){case 0:c=a.outerLabelGroupData[b].x-l-(a.outerLabelGroupData[b].x-l-i.x)/2,d=a.outerLabelGroupData[b].y+(i.y-a.outerLabelGroupData[b].y)/n,e=a.outerLabelGroupData[b].x-l,f=a.outerLabelGroupData[b].y-k;break;case 1:c=i.x+(a.outerLabelGroupData[b].x-i.x)/n,d=i.y+(a.outerLabelGroupData[b].y-i.y)/n,e=a.outerLabelGroupData[b].x-l,f=a.outerLabelGroupData[b].y-k;break;case 2:var o=a.outerLabelGroupData[b].x+a.outerLabelGroupData[b].w+l;c=i.x-(i.x-o)/n,d=i.y+(a.outerLabelGroupData[b].y-i.y)/n,e=a.outerLabelGroupData[b].x+a.outerLabelGroupData[b].w+l,f=a.outerLabelGroupData[b].y-k;break;case 3:var p=a.outerLabelGroupData[b].x+a.outerLabelGroupData[b].w+l;c=p+(i.x-p)/n,d=a.outerLabelGroupData[b].y+(i.y-a.outerLabelGroupData[b].y)/n,e=a.outerLabelGroupData[b].x+a.outerLabelGroupData[b].w+l,f=a.outerLabelGroupData[b].y-k}"straight"===a.options.labels.lines.style?a.lineCoordGroups[b]=[{x:i.x,y:i.y},{x:e,y:f}]:a.lineCoordGroups[b]=[{x:i.x,y:i.y},{x:c,y:d},{x:e,y:f}]},addLabelLines:function(a){var b=a.svg.insert("g","."+a.cssPrefix+"pieChart").attr("class",a.cssPrefix+"lineGroups").style("opacity",0),c=b.selectAll("."+a.cssPrefix+"lineGroup").data(a.lineCoordGroups).enter().append("g").attr("class",a.cssPrefix+"lineGroup"),d=d3.svg.line().interpolate("basis").x(function(a){return a.x}).y(function(a){return a.y});c.append("path").attr("d",d).attr("stroke",function(b,c){return"segment"===a.options.labels.lines.color?a.options.colors[c]:a.options.labels.lines.color}).attr("stroke-width",1).attr("fill","none").style("opacity",function(b,c){var d=a.options.labels.outer.hideWhenLessThanPercentage,e=j.getPercentage(a,c,a.options.labels.percentage.decimalPlaces),f=null!==d&&d>e||""===a.options.data.content[c].label;return f?0:1})},positionLabelGroups:function(a,b){"none"!==a.options.labels[b].format&&d3.selectAll("."+a.cssPrefix+"labelGroup-"+b).style("opacity",0).attr("transform",function(c,d){var e,i;if("outer"===b)e=a.outerLabelGroupData[d].x,i=a.outerLabelGroupData[d].y;else{var k=g(!0,{},a.pieCenter);if(a.innerRadius>0){var l=j.getSegmentAngle(d,a.options.data.content,a.totalSize,{midpoint:!0}),m=h.translate(a.pieCenter.x,a.pieCenter.y,a.innerRadius,l);k.x=m.x,k.y=m.y}var n=f.getDimensions(a.cssPrefix+"labelGroup"+d+"-inner"),o=n.w/2,p=n.h/4;e=k.x+(a.lineCoordGroups[d][0].x-k.x)/1.8,i=k.y+(a.lineCoordGroups[d][0].y-k.y)/1.8,e-=o,i+=p}return"translate("+e+","+i+")"})},fadeInLabelsAndLines:function(a){var b="default"===a.options.effects.load.effect?a.options.effects.load.speed:1;setTimeout(function(){var b="default"===a.options.effects.load.effect?400:1;d3.selectAll("."+a.cssPrefix+"labelGroup-outer").transition().duration(b).style("opacity",function(b,c){var d=a.options.labels.outer.hideWhenLessThanPercentage,e=j.getPercentage(a,c,a.options.labels.percentage.decimalPlaces);return null!==d&&d>e?0:1}),d3.selectAll("."+a.cssPrefix+"labelGroup-inner").transition().duration(b).style("opacity",function(b,c){var d=a.options.labels.inner.hideWhenLessThanPercentage,e=j.getPercentage(a,c,a.options.labels.percentage.decimalPlaces);return null!==d&&d>e?0:1}),d3.selectAll("g."+a.cssPrefix+"lineGroups").transition().duration(b).style("opacity",1),f.isFunction(a.options.callbacks.onload)&&setTimeout(function(){try{a.options.callbacks.onload()}catch(b){}},b)},b)},getIncludes:function(a){var b=!1,c=!1,d=!1;switch(a){case"label":b=!0;break;case"value":c=!0;break;case"percentage":d=!0;break;case"label-value1":case"label-value2":b=!0,c=!0;break;case"label-percentage1":case"label-percentage2":b=!0,d=!0}return{mainLabel:b,value:c,percentage:d}},computeOuterLabelCoords:function(a){a.svg.selectAll("."+a.cssPrefix+"labelGroup-outer").each(function(b,c){return i.getIdealOuterLabelPositions(a,c)}),i.resolveOuterLabelCollisions(a)},resolveOuterLabelCollisions:function(a){if("none"!==a.options.labels.outer.format){var b=a.options.data.content.length;i.checkConflict(a,0,"clockwise",b),i.checkConflict(a,b-1,"anticlockwise",b)}},checkConflict:function(a,b,c,d){var e,g;if(!(1>=d)){var h=a.outerLabelGroupData[b].hs;if(!("clockwise"===c&&"right"!==h||"anticlockwise"===c&&"left"!==h)){var j="clockwise"===c?b+1:b-1,k=a.outerLabelGroupData[b],l=a.outerLabelGroupData[j],m={labelHeights:a.outerLabelGroupData[0].h,center:a.pieCenter,lineLength:a.outerRadius+a.options.labels.outer.pieDistance,heightChange:a.outerLabelGroupData[0].h+1};if("clockwise"===c){for(e=0;b>=e;e++)if(g=a.outerLabelGroupData[e],f.rectIntersect(g,l)){i.adjustLabelPos(a,j,k,m);break}}else for(e=d-1;e>=b;e--)if(g=a.outerLabelGroupData[e],f.rectIntersect(g,l)){i.adjustLabelPos(a,j,k,m);break}i.checkConflict(a,j,c,d)}}},adjustLabelPos:function(a,b,c,d){var e,f,g,h;h=c.y+d.heightChange,f=d.center.y-h,e=Math.sqrt(Math.abs(d.lineLength)>Math.abs(f)?d.lineLength*d.lineLength-f*f:f*f-d.lineLength*d.lineLength),g="right"===c.hs?d.center.x+e:d.center.x-e-a.outerLabelGroupData[b].w,a.outerLabelGroupData[b].x=g,a.outerLabelGroupData[b].y=h},getIdealOuterLabelPositions:function(a,b){var c=d3.select("#"+a.cssPrefix+"labelGroup"+b+"-outer").node();if(c){var d=c.getBBox(),e=j.getSegmentAngle(b,a.options.data.content,a.totalSize,{midpoint:!0}),f=a.pieCenter.x,g=a.pieCenter.y-(a.outerRadius+a.options.labels.outer.pieDistance),i=h.rotate(f,g,a.pieCenter.x,a.pieCenter.y,e),k="right";e>180?(i.x-=d.width+8,k="left"):i.x+=8,a.outerLabelGroupData[b]={x:i.x,y:i.y,w:d.width,h:d.height,hs:k}}}},j={create:function(a){var b=a.pieCenter,c=a.options.colors,d=a.options.effects.load,e=a.options.misc.colors.segmentStroke,f=a.svg.insert("g","#"+a.cssPrefix+"title").attr("transform",function(){return h.getPieTranslateCenter(b)}).attr("class",a.cssPrefix+"pieChart"),g=d3.svg.arc().innerRadius(a.innerRadius).outerRadius(a.outerRadius).startAngle(0).endAngle(function(b){return b.value/a.totalSize*2*Math.PI}),i=f.selectAll("."+a.cssPrefix+"arc").data(a.options.data.content).enter().append("g").attr("class",a.cssPrefix+"arc"),k=d.speed;"none"===d.effect&&(k=0),i.append("path").attr("id",function(b,c){return a.cssPrefix+"segment"+c}).attr("fill",function(b,d){var e=c[d];return a.options.misc.gradient.enabled&&(e="url(#"+a.cssPrefix+"grad"+d+")"),e}).style("stroke",e).style("stroke-width",1).transition().ease("cubic-in-out").duration(k).attr("data-index",function(a,b){return b}).attrTween("d",function(b){var c=d3.interpolate({value:0},b);return function(b){return a.arc(c(b))}}),a.svg.selectAll("g."+a.cssPrefix+"arc").attr("transform",function(b,c){var d=0;return c>0&&(d=j.getSegmentAngle(c-1,a.options.data.content,a.totalSize)),"rotate("+d+")"}),a.arc=g},addGradients:function(a){var b=a.svg.append("defs").selectAll("radialGradient").data(a.options.data.content).enter().append("radialGradient").attr("gradientUnits","userSpaceOnUse").attr("cx",0).attr("cy",0).attr("r","120%").attr("id",function(b,c){return a.cssPrefix+"grad"+c});b.append("stop").attr("offset","0%").style("stop-color",function(b,c){return a.options.colors[c]}),b.append("stop").attr("offset",a.options.misc.gradient.percentage+"%").style("stop-color",a.options.misc.gradient.color)},addSegmentEventHandlers:function(a){var b=d3.selectAll("."+a.cssPrefix+"arc,."+a.cssPrefix+"labelGroup-inner,."+a.cssPrefix+"labelGroup-outer");b.on("click",function(){var b,c=d3.select(this);if(c.attr("class")===a.cssPrefix+"arc")b=c.select("path");else{var d=c.attr("data-index");b=d3.select("#"+a.cssPrefix+"segment"+d)}var e=b.attr("class")===a.cssPrefix+"expanded";j.onSegmentEvent(a,a.options.callbacks.onClickSegment,b,e),"none"!==a.options.effects.pullOutSegmentOnClick.effect&&(e?j.closeSegment(a,b.node()):j.openSegment(a,b.node()))}),b.on("mouseover",function(){var b,c,d=d3.select(this);if(d.attr("class")===a.cssPrefix+"arc"?b=d.select("path"):(c=d.attr("data-index"),b=d3.select("#"+a.cssPrefix+"segment"+c)),a.options.effects.highlightSegmentOnMouseover){c=b.attr("data-index");var e=a.options.colors[c];b.style("fill",f.getColorShade(e,a.options.effects.highlightLuminosity))}a.options.tooltips.enabled&&(c=b.attr("data-index"),l.showTooltip(a,c));var g=b.attr("class")===a.cssPrefix+"expanded";j.onSegmentEvent(a,a.options.callbacks.onMouseoverSegment,b,g)}),b.on("mousemove",function(){l.moveTooltip(a)}),b.on("mouseout",function(){var b,c,d=d3.select(this);if(d.attr("class")===a.cssPrefix+"arc"?b=d.select("path"):(c=d.attr("data-index"),b=d3.select("#"+a.cssPrefix+"segment"+c)),a.options.effects.highlightSegmentOnMouseover){c=b.attr("data-index");var e=a.options.colors[c];a.options.misc.gradient.enabled&&(e="url(#"+a.cssPrefix+"grad"+c+")"),b.style("fill",e)}a.options.tooltips.enabled&&(c=b.attr("data-index"),l.hideTooltip(a,c));var f=b.attr("class")===a.cssPrefix+"expanded";j.onSegmentEvent(a,a.options.callbacks.onMouseoutSegment,b,f)})},onSegmentEvent:function(a,b,c,d){if(f.isFunction(b)){var e=parseInt(c.attr("data-index"),10);b({segment:c.node(),index:e,expanded:d,data:a.options.data.content[e]})}},openSegment:function(a,b){a.isOpeningSegment||(a.isOpeningSegment=!0,d3.selectAll("."+a.cssPrefix+"expanded").length>0&&j.closeSegment(a,d3.select("."+a.cssPrefix+"expanded").node()),d3.select(b).transition().ease(a.options.effects.pullOutSegmentOnClick.effect).duration(a.options.effects.pullOutSegmentOnClick.speed).attr("transform",function(b,c){var d=a.arc.centroid(b),e=d[0],f=d[1],g=Math.sqrt(e*e+f*f),h=parseInt(a.options.effects.pullOutSegmentOnClick.size,10);return"translate("+e/g*h+","+f/g*h+")"}).each("end",function(c,d){a.currentlyOpenSegment=b,a.isOpeningSegment=!1,d3.select(this).attr("class",a.cssPrefix+"expanded")}))},closeSegment:function(a,b){d3.select(b).transition().duration(400).attr("transform","translate(0,0)").each("end",function(b,c){d3.select(this).attr("class",""),a.currentlyOpenSegment=null})},getCentroid:function(a){var b=a.getBBox();return{x:b.x+b.width/2,y:b.y+b.height/2}},getSegmentAngle:function(a,b,c,d){var e,f=g({compounded:!0,midpoint:!1},d),h=b[a].value;if(f.compounded){e=0;for(var i=0;a>=i;i++)e+=b[i].value}"undefined"==typeof e&&(e=h);var j=e/c*360;if(f.midpoint){var k=h/c*360;j-=k/2}return j},getPercentage:function(a,b,c){var d=a.options.data.content[b].value/a.totalSize;return 0>=c?Math.round(100*d):(100*d).toFixed(c)}},k={offscreenCoord:-1e4,addTitle:function(a){a.svg.selectAll("."+a.cssPrefix+"title").data([a.options.header.title]).enter().append("text").text(function(a){return a.text}).attr({id:a.cssPrefix+"title","class":a.cssPrefix+"title",x:k.offscreenCoord,y:k.offscreenCoord}).attr("text-anchor",function(){var b;return b="top-center"===a.options.header.location||"pie-center"===a.options.header.location?"middle":"left"}).attr("fill",function(a){return a.color}).style("font-size",function(a){return a.fontSize+"px"}).style("font-family",function(a){return a.font})},positionTitle:function(a){var b,c=a.textComponents,d=a.options.header.location,e=a.options.misc.canvasPadding,f=a.options.size.canvasWidth,g=a.options.header.titleSubtitlePadding;b="top-left"===d?e.left:(f-e.right)/2+e.left,b+=a.options.misc.pieCenterOffset.x;var h=e.top+c.title.h;if("pie-center"===d)if(h=a.pieCenter.y,c.subtitle.exists){var i=c.title.h+g+c.subtitle.h;h=h-i/2+c.title.h}else h+=c.title.h/4;a.svg.select("#"+a.cssPrefix+"title").attr("x",b).attr("y",h)},addSubtitle:function(a){var b=a.options.header.location;a.svg.selectAll("."+a.cssPrefix+"subtitle").data([a.options.header.subtitle]).enter().append("text").text(function(a){return a.text}).attr("x",k.offscreenCoord).attr("y",k.offscreenCoord).attr("id",a.cssPrefix+"subtitle").attr("class",a.cssPrefix+"subtitle").attr("text-anchor",function(){var a;return a="top-center"===b||"pie-center"===b?"middle":"left"}).attr("fill",function(a){return a.color}).style("font-size",function(a){return a.fontSize+"px"}).style("font-family",function(a){return a.font})},positionSubtitle:function(a){var b,c=a.options.misc.canvasPadding,d=a.options.size.canvasWidth;b="top-left"===a.options.header.location?c.left:(d-c.right)/2+c.left,b+=a.options.misc.pieCenterOffset.x;var e=k.getHeaderHeight(a);a.svg.select("#"+a.cssPrefix+"subtitle").attr("x",b).attr("y",e)},addFooter:function(a){a.svg.selectAll("."+a.cssPrefix+"footer").data([a.options.footer]).enter().append("text").text(function(a){return a.text}).attr("x",k.offscreenCoord).attr("y",k.offscreenCoord).attr("id",a.cssPrefix+"footer").attr("class",a.cssPrefix+"footer").attr("text-anchor",function(){var b="left";return"bottom-center"===a.options.footer.location?b="middle":"bottom-right"===a.options.footer.location&&(b="left"),b}).attr("fill",function(a){return a.color}).style("font-size",function(a){return a.fontSize+"px"}).style("font-family",function(a){return a.font})},positionFooter:function(a){var b,c=a.options.footer.location,d=a.textComponents.footer.w,e=a.options.size.canvasWidth,f=a.options.size.canvasHeight,g=a.options.misc.canvasPadding;b="bottom-left"===c?g.left:"bottom-right"===c?e-d-g.right:e/2,a.svg.select("#"+a.cssPrefix+"footer").attr("x",b).attr("y",f-g.bottom)},getHeaderHeight:function(a){var b;if(a.textComponents.title.exists){var c=a.textComponents.title.h+a.options.header.titleSubtitlePadding+a.textComponents.subtitle.h;b="pie-center"===a.options.header.location?a.pieCenter.y-c/2+c:c+a.options.misc.canvasPadding.top}else if("pie-center"===a.options.header.location){var d=a.options.misc.canvasPadding.bottom+a.textComponents.footer.h;b=(a.options.size.canvasHeight-d)/2+a.options.misc.canvasPadding.top+a.textComponents.subtitle.h/2}else b=a.options.misc.canvasPadding.top+a.textComponents.subtitle.h;return b}},l={addTooltips:function(a){var b=a.svg.insert("g").attr("class",a.cssPrefix+"tooltips");b.selectAll("."+a.cssPrefix+"tooltip").data(a.options.data.content).enter().append("g").attr("class",a.cssPrefix+"tooltip").attr("id",function(b,c){return a.cssPrefix+"tooltip"+c}).style("opacity",0).append("rect").attr({rx:a.options.tooltips.styles.borderRadius,ry:a.options.tooltips.styles.borderRadius,x:-a.options.tooltips.styles.padding,opacity:a.options.tooltips.styles.backgroundOpacity}).style("fill",a.options.tooltips.styles.backgroundColor),b.selectAll("."+a.cssPrefix+"tooltip").data(a.options.data.content).append("text").attr("fill",function(b){return a.options.tooltips.styles.color}).style("font-size",function(b){return a.options.tooltips.styles.fontSize}).style("font-family",function(b){return a.options.tooltips.styles.font}).text(function(b,c){var d=a.options.tooltips.string;return"caption"===a.options.tooltips.type&&(d=b.caption),l.replacePlaceholders(a,d,c,{label:b.label,value:b.value,percentage:j.getPercentage(a,c,a.options.labels.percentage.decimalPlaces)})}),b.selectAll("."+a.cssPrefix+"tooltip rect").attr({width:function(b,c){var d=f.getDimensions(a.cssPrefix+"tooltip"+c);return d.w+2*a.options.tooltips.styles.padding},height:function(b,c){var d=f.getDimensions(a.cssPrefix+"tooltip"+c);return d.h+2*a.options.tooltips.styles.padding},y:function(b,c){var d=f.getDimensions(a.cssPrefix+"tooltip"+c);return-(d.h/2)+1}})},showTooltip:function(a,b){var c=a.options.tooltips.styles.fadeInSpeed;l.currentTooltip===b&&(c=1),l.currentTooltip=b,d3.select("#"+a.cssPrefix+"tooltip"+b).transition().duration(c).style("opacity",function(){return 1}),l.moveTooltip(a)},moveTooltip:function(a){d3.selectAll("#"+a.cssPrefix+"tooltip"+l.currentTooltip).attr("transform",function(b){var c=d3.mouse(this.parentNode),d=c[0]+a.options.tooltips.styles.padding+2,e=c[1]-2*a.options.tooltips.styles.padding-2;return"translate("+d+","+e+")"})},hideTooltip:function(a,b){d3.select("#"+a.cssPrefix+"tooltip"+b).style("opacity",function(){return 0}),d3.select("#"+a.cssPrefix+"tooltip"+l.currentTooltip).attr("transform",function(b,c){var d=a.options.size.canvasWidth+1e3,e=a.options.size.canvasHeight+1e3;return"translate("+d+","+e+")"})},replacePlaceholders:function(a,b,c,d){f.isFunction(a.options.tooltips.placeholderParser)&&a.options.tooltips.placeholderParser(c,d);var e=function(){return function(a){var b=arguments[1];return d.hasOwnProperty(b)?d[arguments[1]]:arguments[0]}};return b.replace(/\{(\w+)\}/g,e(d))}},m=function(i,j){if(this.element=i,"string"==typeof i){var k=i.replace(/^#/,"");this.element=document.getElementById(k)}var l={};g(!0,l,d,j),this.options=l,null!==this.options.misc.cssPrefix?this.cssPrefix=this.options.misc.cssPrefix:(this.cssPrefix="p"+c+"_",c++),e.initialCheck(this)&&(d3.select(this.element).attr(a,b),this.options.data.content=h.sortPieData(this),this.options.data.smallSegmentGrouping.enabled&&(this.options.data.content=f.applySmallSegmentGrouping(this.options.data.content,this.options.data.smallSegmentGrouping)),this.options.colors=f.initSegmentColors(this),this.totalSize=h.getTotalPieSize(this.options.data.content),n.call(this))};m.prototype.recreate=function(){e.initialCheck(this)&&(this.options.data.content=h.sortPieData(this),this.options.data.smallSegmentGrouping.enabled&&(this.options.data.content=f.applySmallSegmentGrouping(this.options.data.content,this.options.data.smallSegmentGrouping)),this.options.colors=f.initSegmentColors(this),this.totalSize=h.getTotalPieSize(this.options.data.content),n.call(this))},m.prototype.redraw=function(){this.element.innerHTML="",n.call(this)},m.prototype.destroy=function(){this.element.innerHTML="",d3.select(this.element).attr(a,null)},m.prototype.getOpenSegment=function(){var a=this.currentlyOpenSegment;if(null!==a&&"undefined"!=typeof a){var b=parseInt(d3.select(a).attr("data-index"),10);return{element:a,index:b,data:this.options.data.content[b]}}return null},m.prototype.openSegment=function(a){a=parseInt(a,10),0>a||a>this.options.data.content.length-1||j.openSegment(this,d3.select("#"+this.cssPrefix+"segment"+a).node())},m.prototype.closeSegment=function(){var a=this.currentlyOpenSegment;a&&j.closeSegment(this,a)},m.prototype.updateProp=function(a,b){switch(a){case"header.title.text":var c=f.processObj(this.options,a);f.processObj(this.options,a,b),d3.select("#"+this.cssPrefix+"title").html(b),(""===c&&""!==b||""!==c&&""===b)&&this.redraw();break;case"header.subtitle.text":var d=f.processObj(this.options,a);f.processObj(this.options,a,b),d3.select("#"+this.cssPrefix+"subtitle").html(b),(""===d&&""!==b||""!==d&&""===b)&&this.redraw();break;case"callbacks.onload":case"callbacks.onMouseoverSegment":case"callbacks.onMouseoutSegment":case"callbacks.onClickSegment":case"effects.pullOutSegmentOnClick.effect":case"effects.pullOutSegmentOnClick.speed":case"effects.pullOutSegmentOnClick.size":case"effects.highlightSegmentOnMouseover":case"effects.highlightLuminosity":f.processObj(this.options,a,b);break;default:f.processObj(this.options,a,b),this.destroy(),this.recreate()}};var n=function(){this.svg=f.addSVGSpace(this),this.textComponents={headerHeight:0,title:{exists:""!==this.options.header.title.text,h:0,w:0},subtitle:{exists:""!==this.options.header.subtitle.text,h:0,w:0},footer:{exists:""!==this.options.footer.text,h:0,w:0}},this.outerLabelGroupData=[], +this.textComponents.title.exists&&k.addTitle(this),this.textComponents.subtitle.exists&&k.addSubtitle(this),k.addFooter(this);var a=this;f.whenIdExists(this.cssPrefix+"footer",function(){k.positionFooter(a);var b=f.getDimensions(a.cssPrefix+"footer");a.textComponents.footer.h=b.h,a.textComponents.footer.w=b.w});var b=[];this.textComponents.title.exists&&b.push(this.cssPrefix+"title"),this.textComponents.subtitle.exists&&b.push(this.cssPrefix+"subtitle"),this.textComponents.footer.exists&&b.push(this.cssPrefix+"footer"),f.whenElementsExist(b,function(){if(a.textComponents.title.exists){var b=f.getDimensions(a.cssPrefix+"title");a.textComponents.title.h=b.h,a.textComponents.title.w=b.w}if(a.textComponents.subtitle.exists){var c=f.getDimensions(a.cssPrefix+"subtitle");a.textComponents.subtitle.h=c.h,a.textComponents.subtitle.w=c.w}if(a.textComponents.title.exists||a.textComponents.subtitle.exists){var d=0;a.textComponents.title.exists&&(d+=a.textComponents.title.h,a.textComponents.subtitle.exists&&(d+=a.options.header.titleSubtitlePadding)),a.textComponents.subtitle.exists&&(d+=a.textComponents.subtitle.h),a.textComponents.headerHeight=d}h.computePieRadius(a),h.calculatePieCenter(a),k.positionTitle(a),k.positionSubtitle(a),a.options.misc.gradient.enabled&&j.addGradients(a),j.create(a),i.add(a,"inner",a.options.labels.inner.format),i.add(a,"outer",a.options.labels.outer.format),i.positionLabelElements(a,"inner",a.options.labels.inner.format),i.positionLabelElements(a,"outer",a.options.labels.outer.format),i.computeOuterLabelCoords(a),i.positionLabelGroups(a,"outer"),i.computeLabelLinePositions(a),a.options.labels.lines.enabled&&"none"!==a.options.labels.outer.format&&i.addLabelLines(a),i.positionLabelGroups(a,"inner"),i.fadeInLabelsAndLines(a),a.options.tooltips.enabled&&l.addTooltips(a),j.addSegmentEventHandlers(a)})};return m});
\ No newline at end of file diff --git a/src/usr/local/www/diag_arp.php b/src/usr/local/www/diag_arp.php new file mode 100644 index 0000000..01d1ef0 --- /dev/null +++ b/src/usr/local/www/diag_arp.php @@ -0,0 +1,382 @@ +<?php +/* + diag_arp.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /bin/cat /usr/sbin/arp + pfSense_MODULE: arp +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-arptable +##|*NAME=Diagnostics: ARP Table page +##|*DESCR=Allow access to the 'Diagnostics: ARP Table' page. +##|*MATCH=diag_arp.php* +##|-PRIV + +@ini_set('zlib.output_compression', 0); +@ini_set('implicit_flush', 1); + +require("guiconfig.inc"); + +function leasecmp($a, $b) { + return strcmp($a[$_GET['order']], $b[$_GET['order']]); +} + +function adjust_gmt($dt) { + $ts = strtotime($dt . " GMT"); + return strftime("%Y/%m/%d %H:%M:%S", $ts); +} + +function remove_duplicate($array, $field) { + foreach ($array as $sub) { + $cmp[] = $sub[$field]; + } + $unique = array_unique($cmp); + foreach ($unique as $k => $rien) { + $new[] = $array[$k]; + } + return $new; +} + +// Define path to AWK +$awk = "/usr/bin/awk"; + +// Read in leases file +$leasesfile = "{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases"; + +/* this pattern sticks comments into a single array item */ +$cleanpattern = "'{ gsub(\"#.*\", \"\");} { gsub(\";\", \"\"); print;}'"; + +/* We then split the leases file by } */ +$splitpattern = "'BEGIN { RS=\"}\";} {for (i=1; i<=NF; i++) printf \"%s \", \$i; printf \"}\\n\";}'"; + +/* stuff the leases file in a proper format into an array by line */ +exec("cat {$leasesfile} | {$awk} {$cleanpattern} | {$awk} {$splitpattern}", $leases_content); +$leases_count = count($leases_content); + +$pools = array(); +$leases = array(); +$i = 0; +$l = 0; +$p = 0; +// Put everything together again +while ($i < $leases_count) { + /* split the line by space */ + $data = explode(" ", $leases_content[$i]); + /* walk the fields */ + $f = 0; + $fcount = count($data); + /* with less then 20 fields there is nothing useful */ + if ($fcount < 20) { + $i++; + continue; + } + while ($f < $fcount) { + switch ($data[$f]) { + case "failover": + $pools[$p]['name'] = $data[$f+2]; + $pools[$p]['mystate'] = $data[$f+7]; + $pools[$p]['peerstate'] = $data[$f+14]; + $pools[$p]['mydate'] = $data[$f+10]; + $pools[$p]['mydate'] .= " " . $data[$f+11]; + $pools[$p]['peerdate'] = $data[$f+17]; + $pools[$p]['peerdate'] .= " " . $data[$f+18]; + $p++; + $i++; + continue 3; + case "lease": + $leases[$l]['ip'] = $data[$f+1]; + $leases[$l]['type'] = "dynamic"; + $f = $f+2; + break; + case "starts": + $leases[$l]['start'] = $data[$f+2]; + $leases[$l]['start'] .= " " . $data[$f+3]; + $f = $f+3; + break; + case "ends": + $leases[$l]['end'] = $data[$f+2]; + $leases[$l]['end'] .= " " . $data[$f+3]; + $f = $f+3; + break; + case "tstp": + $f = $f+3; + break; + case "tsfp": + $f = $f+3; + break; + case "atsfp": + $f = $f+3; + break; + case "cltt": + $f = $f+3; + break; + case "binding": + switch ($data[$f+2]) { + case "active": + $leases[$l]['act'] = "active"; + break; + case "free": + $leases[$l]['act'] = "expired"; + $leases[$l]['online'] = "offline"; + break; + case "backup": + $leases[$l]['act'] = "reserved"; + $leases[$l]['online'] = "offline"; + break; + } + $f = $f+1; + break; + case "next": + /* skip the next binding statement */ + $f = $f+3; + break; + case "rewind": + /* skip the rewind binding statement */ + $f = $f+3; + break; + case "hardware": + $leases[$l]['mac'] = $data[$f+2]; + /* check if it's online and the lease is active */ + if ($leases[$l]['act'] == "active") { + $online = exec("/usr/sbin/arp -an |/usr/bin/awk '/{$leases[$l]['ip']}/ {print}'|wc -l"); + if ($online == 1) { + $leases[$l]['online'] = 'online'; + } else { + $leases[$l]['online'] = 'offline'; + } + } + $f = $f+2; + break; + case "client-hostname": + if ($data[$f+1] <> "") { + $leases[$l]['hostname'] = preg_replace('/"/', '', $data[$f+1]); + } else { + $hostname = gethostbyaddr($leases[$l]['ip']); + if ($hostname <> "") { + $leases[$l]['hostname'] = $hostname; + } + } + $f = $f+1; + break; + case "uid": + $f = $f+1; + break; + } + $f++; + } + $l++; + $i++; +} + +/* remove duplicate items by mac address */ +if (count($leases) > 0) { + $leases = remove_duplicate($leases, "ip"); +} + +if (count($pools) > 0) { + $pools = remove_duplicate($pools, "name"); + asort($pools); +} + +// Put this in an easy to use form +$dhcpmac = array(); +$dhcpip = array(); + +foreach ($leases as $value) { + $dhcpmac[$value['mac']] = $value['hostname']; + $dhcpip[$value['ip']] = $value['hostname']; +} + +exec("/usr/sbin/arp -an", $rawdata); + +$i = 0; + +/* if list */ +$ifdescrs = get_configured_interface_with_descr(); + +foreach ($ifdescrs as $key => $interface) { + $thisif = convert_friendly_interface_to_real_interface_name($key); + if (!empty($thisif)) { + $hwif[$thisif] = $interface; + } +} + +$data = array(); +foreach ($rawdata as $line) { + $elements = explode(' ', $line); + + if ($elements[3] != "(incomplete)") { + $arpent = array(); + $arpent['ip'] = trim(str_replace(array('(', ')'), '', $elements[1])); + $arpent['mac'] = trim($elements[3]); + $arpent['interface'] = trim($elements[5]); + $data[] = $arpent; + } +} + +function _getHostName($mac, $ip) { + global $dhcpmac, $dhcpip; + + if ($dhcpmac[$mac]) { + return $dhcpmac[$mac]; + } else if ($dhcpip[$ip]) { + return $dhcpip[$ip]; + } else { + exec("host -W 1 " . escapeshellarg($ip), $output); + if (preg_match('/.*pointer ([A-Za-z_0-9.-]+)\..*/', $output[0], $matches)) { + if ($matches[1] <> $ip) { + return $matches[1]; + } + } + } + return ""; +} + +$pgtitle = array(gettext("Diagnostics"), gettext("ARP Table")); +include("head.inc"); + +?> + +<!-- On modern hardware the table will load so fast you may never see this! --> +<div id="loading"> + <?= gettext(" Loading, please wait...")?> +</div> + +<?php + +// Flush buffers out to client so that they see Loading, please wait.... +for ($i = 0; $i < ob_get_level(); $i++) { + ob_end_flush(); +} +ob_implicit_flush(1); + +// Resolve hostnames and replace Z_ with "". The intention +// is to sort the list by hostnames, alpha and then the non +// resolvable addresses will appear last in the list. +$dnsavailable=1; +$dns = trim(_getHostName("", "8.8.8.8")); +if ($dns == "") { + $dns = trim(_getHostName("", "8.8.4.4")); + if ($dns == "") { + $dnsavailable = 0; + } +} + +foreach ($data as &$entry) { + if ($dnsavailable) { + $dns = trim(_getHostName($entry['mac'], $entry['ip'])); + } else { + $dns=""; + } + if (trim($dns)) { + $entry['dnsresolve'] = "$dns"; + } else { + $entry['dnsresolve'] = "Z_ "; + } +} + +// Sort the data alpha first +$data = msort($data, "dnsresolve"); + +// Load MAC-Manufacturer table +$mac_man = load_mac_manufacturer_table(); +?> +<div class="table-responsive"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th><?= gettext("Interface")?></th> + <th><?= gettext("IP address")?></th> + <th><?= gettext("MAC address")?></th> + <th><?= gettext("Hostname")?></th> + </tr> + </thead> + <tbody> + <?php foreach ($data as $entry): ?> + <tr> + <td><?=$hwif[$entry['interface']]?></td> + <td><?=$entry['ip']?></td> + <td> + <?=trim($entry['mac'])?> + <?php + $mac = trim($entry['mac']); + $mac_hi = strtoupper($mac[0] . $mac[1] . $mac[3] . $mac[4] . $mac[6] . $mac[7]); + + if (isset($mac_man[$mac_hi])) + print '<small>('. $mac_man[$mac_hi] .')</small>'; + ?> + </td> + <td><?=trim(str_replace("Z_ ", "", $entry['dnsresolve']))?></td> + </tr> + <?php endforeach?> + </tbody> + </table> +</div> + +<script> +//<![CDATA[ +// Clear the "loading" div once the page has loaded" +events.push(function(){ + $('#loading').empty(); +}); +//]]> +</script> + +<?php +print_info_box(gettext("Local IPv6 peers use ") . '<a href="diag_ndp.php">' . gettext("NDP") . '</a>' . gettext(" instead of ARP"), 'info'); + +include("foot.inc")?> diff --git a/src/usr/local/www/diag_authentication.php b/src/usr/local/www/diag_authentication.php new file mode 100644 index 0000000..503f5a3 --- /dev/null +++ b/src/usr/local/www/diag_authentication.php @@ -0,0 +1,148 @@ +<?php +/* + diag_authentication.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2010 Ermal Luçi + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_MODULE: auth +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-authentication +##|*NAME=Diagnostics: Authentication page +##|*DESCR=Allow access to the 'Diagnostics: Authentication' page. +##|*MATCH=diag_authentication.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("PEAR.inc"); +require_once("radius.inc"); + +if ($_POST) { + $pconfig = $_POST; + unset($input_errors); + + $authcfg = auth_get_authserver($_POST['authmode']); + if (!$authcfg) { + $input_errors[] = $_POST['authmode'] . " " . gettext("is not a valid authentication server"); + } + + if (empty($_POST['username']) || empty($_POST['password'])) { + $input_errors[] = gettext("A username and password must be specified."); + } + + if (!$input_errors) { + $attributes = array(); + if (authenticate_user($_POST['username'], $_POST['password'], $authcfg, $attributes)) { + $savemsg = gettext("User") . ": " . $_POST['username'] . " " . gettext("authenticated successfully."); + $groups = getUserGroups($_POST['username'], $authcfg, $attributes); + $savemsg .= " " . gettext("This user is a member of groups") . ": <br />"; + $savemsg .= "<ul>"; + foreach ($groups as $group) + $savemsg .= "<li>" . "{$group} " . "</li>"; + $savemsg .= "</ul>"; + + } else { + $input_errors[] = gettext("Authentication failed."); + } + } +} +$pgtitle = array(gettext("Diagnostics"), gettext("Authentication")); +$shortcut_section = "authentication"; +include("head.inc"); + +?> +<?php +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print('<div class="alert alert-success" role="alert">'. $savemsg.'</div>'); + +require('classes/Form.class.php'); + +$form = new Form('Test'); + +$section = new Form_Section('Authentication Test'); + +foreach (auth_get_authserver_list() as $auth_server) + $serverlist[$auth_server['name']] = $auth_server['name']; + +$section->addInput(new Form_Select( + 'authmode', + 'Authentication Server', + $pconfig['authmode'], + $serverlist +))->setHelp('Select the authentication server to test against'); + +$section->addInput(new Form_Input( + 'username', + 'Username', + 'text', + $pconfig['username'], + ['placeholder' => 'Username'] +)); + +$section->addInput(new Form_Input( + 'password', + 'Password', + 'password', + $pconfig['password'], + ['placeholder' => 'Password'] +)); + +$form->add($section); +print $form; + +include("foot.inc"); diff --git a/src/usr/local/www/diag_backup.php b/src/usr/local/www/diag_backup.php new file mode 100644 index 0000000..39bcc70 --- /dev/null +++ b/src/usr/local/www/diag_backup.php @@ -0,0 +1,780 @@ +<?php +/* $Id$ */ +/* + diag_backup.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /sbin/shutdown + pfSense_MODULE: backup +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-backup/restore +##|*NAME=Diagnostics: Backup/restore page +##|*DESCR=Allow access to the 'Diagnostics: Backup/restore' page. +##|*MATCH=diag_backup.php* +##|-PRIV + +/* Allow additional execution time 0 = no limit. */ +ini_set('max_execution_time', '0'); +ini_set('max_input_time', '0'); + +/* omit no-cache headers because it confuses IE with file downloads */ +$omit_nocacheheaders = true; +$nocsrf = true; +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +$rrddbpath = "/var/db/rrd"; +$rrdtool = "/usr/bin/nice -n20 /usr/local/bin/rrdtool"; + +function rrd_data_xml() { + global $rrddbpath; + global $rrdtool; + + $result = "\t<rrddata>\n"; + $rrd_files = glob("{$rrddbpath}/*.rrd"); + $xml_files = array(); + foreach ($rrd_files as $rrd_file) { + $basename = basename($rrd_file); + $xml_file = preg_replace('/\.rrd$/', ".xml", $rrd_file); + exec("$rrdtool dump '{$rrd_file}' '{$xml_file}'"); + $xml_data = file_get_contents($xml_file); + unlink($xml_file); + if ($xml_data !== false) { + $result .= "\t\t<rrddatafile>\n"; + $result .= "\t\t\t<filename>{$basename}</filename>\n"; + $result .= "\t\t\t<xmldata>" . base64_encode(gzdeflate($xml_data)) . "</xmldata>\n"; + $result .= "\t\t</rrddatafile>\n"; + } + } + $result .= "\t</rrddata>\n"; + return $result; +} + +function restore_rrddata() { + global $config, $g, $rrdtool, $input_errors; + foreach ($config['rrddata']['rrddatafile'] as $rrd) { + if ($rrd['xmldata']) { + $rrd_file = "{$g['vardb_path']}/rrd/{$rrd['filename']}"; + $xml_file = preg_replace('/\.rrd$/', ".xml", $rrd_file); + if (file_put_contents($xml_file, gzinflate(base64_decode($rrd['xmldata']))) === false) { + log_error("Cannot write $xml_file"); + continue; + } + $output = array(); + $status = null; + exec("$rrdtool restore -f '{$xml_file}' '{$rrd_file}'", $output, $status); + if ($status) { + log_error("rrdtool restore -f '{$xml_file}' '{$rrd_file}' failed returning {$status}."); + continue; + } + unlink($xml_file); + } else if ($rrd['data']) { + $rrd_file = "{$g['vardb_path']}/rrd/{$rrd['filename']}"; + $rrd_fd = fopen($rrd_file, "w"); + if (!$rrd_fd) { + log_error("Cannot write $rrd_file"); + continue; + } + $data = base64_decode($rrd['data']); + /* Try to decompress the data. */ + $dcomp = @gzinflate($data); + if ($dcomp) { + /* If the decompression worked, write the decompressed data */ + if (fwrite($rrd_fd, $dcomp) === false) { + log_error("fwrite $rrd_file failed"); + continue; + } + } else { + /* If the decompression failed, it wasn't compressed, so write raw data */ + if (fwrite($rrd_fd, $data) === false) { + log_error("fwrite $rrd_file failed"); + continue; + } + } + if (fclose($rrd_fd) === false) { + log_error("fclose $rrd_file failed"); + continue; + } + } + } +} + +function add_base_packages_menu_items() { + global $g, $config; + $base_packages = explode(",", $g['base_packages']); + $modified_config = false; + foreach ($base_packages as $bp) { + $basepkg_path = "/usr/local/pkg/{$bp}"; + $tmpinfo = pathinfo($basepkg_path, PATHINFO_EXTENSION); + if ($tmpinfo['extension'] == "xml" && file_exists($basepkg_path)) { + $pkg_config = parse_xml_config_pkg($basepkg_path, "packagegui"); + if ($pkg_config['menu'] != "") { + if (is_array($pkg_config['menu'])) { + foreach ($pkg_config['menu'] as $menu) { + if (is_array($config['installedpackages']['menu'])) { + foreach ($config['installedpackages']['menu'] as $amenu) { + if ($amenu['name'] == $menu['name']) { + continue; + } + } + } + $config['installedpackages']['menu'][] = $menu; + $modified_config = true; + } + } + $static_output .= "done.\n"; + update_output_window($static_output); + } + } + } + if ($modified_config) { + write_config(gettext("Restored base_package menus after configuration restore.")); + $config = parse_config(true); + } +} + +function remove_bad_chars($string) { + return preg_replace('/[^a-z_0-9]/i', '', $string); +} + +function check_and_returnif_section_exists($section) { + global $config; + if (is_array($config[$section])) { + return true; + } + return false; +} + +if ($_POST['apply']) { + ob_flush(); + flush(); + conf_mount_rw(); + clear_subsystem_dirty("restore"); + conf_mount_ro(); + exit; +} + +if ($_POST) { + unset($input_errors); + if (stristr($_POST['Submit'], gettext("Restore configuration"))) { + $mode = "restore"; + } else if (stristr($_POST['Submit'], gettext("Reinstall"))) { + $mode = "reinstallpackages"; + } else if (stristr($_POST['Submit'], gettext("Clear Package Lock"))) { + $mode = "clearpackagelock"; + } else if (stristr($_POST['Submit'], gettext("Download"))) { + $mode = "download"; + } else if (stristr($_POST['Submit'], gettext("Restore version"))) { + $mode = "restore_ver"; + } + if ($_POST["nopackages"] <> "") { + $options = "nopackages"; + } + if ($_POST["ver"] <> "") { + $ver2restore = $_POST["ver"]; + } + if ($mode) { + if ($mode == "download") { + if ($_POST['encrypt']) { + if (!$_POST['encrypt_password'] || !$_POST['encrypt_passconf']) { + $input_errors[] = gettext("You must supply and confirm the password for encryption."); + } + if ($_POST['encrypt_password'] != $_POST['encrypt_passconf']) { + $input_errors[] = gettext("The supplied 'Password' and 'Confirm' field values must match."); + } + } + + if (!$input_errors) { + + //$lockbckp = lock('config'); + + $host = "{$config['system']['hostname']}.{$config['system']['domain']}"; + $name = "config-{$host}-".date("YmdHis").".xml"; + $data = ""; + + if ($options == "nopackages") { + if (!$_POST['backuparea']) { + /* backup entire configuration */ + $data = file_get_contents("{$g['conf_path']}/config.xml"); + } else { + /* backup specific area of configuration */ + $data = backup_config_section($_POST['backuparea']); + $name = "{$_POST['backuparea']}-{$name}"; + } + $sfn = "{$g['tmp_path']}/config.xml.nopkg"; + file_put_contents($sfn, $data); + exec("sed '/<installedpackages>/,/<\/installedpackages>/d' {$sfn} > {$sfn}-new"); + $data = file_get_contents($sfn . "-new"); + } else { + if (!$_POST['backuparea']) { + /* backup entire configuration */ + $data = file_get_contents("{$g['conf_path']}/config.xml"); + } else if ($_POST['backuparea'] === "rrddata") { + $data = rrd_data_xml(); + $name = "{$_POST['backuparea']}-{$name}"; + } else { + /* backup specific area of configuration */ + $data = backup_config_section($_POST['backuparea']); + $name = "{$_POST['backuparea']}-{$name}"; + } + } + + //unlock($lockbckp); + + /* + * Backup RRD Data + */ + if ($_POST['backuparea'] !== "rrddata" && !$_POST['donotbackuprrd']) { + $rrd_data_xml = rrd_data_xml(); + $closing_tag = "</" . $g['xml_rootobj'] . ">"; + $data = str_replace($closing_tag, $rrd_data_xml . $closing_tag, $data); + } + + if ($_POST['encrypt']) { + $data = encrypt_data($data, $_POST['encrypt_password']); + tagfile_reformat($data, $data, "config.xml"); + } + + $size = strlen($data); + header("Content-Type: application/octet-stream"); + header("Content-Disposition: attachment; filename={$name}"); + header("Content-Length: $size"); + if (isset($_SERVER['HTTPS'])) { + header('Pragma: '); + header('Cache-Control: '); + } else { + header("Pragma: private"); + header("Cache-Control: private, must-revalidate"); + } + echo $data; + + exit; + } + } + + if ($mode == "restore") { + if ($_POST['decrypt']) { + if (!$_POST['decrypt_password'] || !$_POST['decrypt_passconf']) { + $input_errors[] = gettext("You must supply and confirm the password for decryption."); + } + if ($_POST['decrypt_password'] != $_POST['decrypt_passconf']) { + $input_errors[] = gettext("The supplied 'Password' and 'Confirm' field values must match."); + } + } + + if (!$input_errors) { + if (is_uploaded_file($_FILES['conffile']['tmp_name'])) { + + /* read the file contents */ + $data = file_get_contents($_FILES['conffile']['tmp_name']); + if (!$data) { + log_error(sprintf(gettext("Warning, could not read file %s"), $_FILES['conffile']['tmp_name'])); + return 1; + } + + if ($_POST['decrypt']) { + if (!tagfile_deformat($data, $data, "config.xml")) { + $input_errors[] = gettext("The uploaded file does not appear to contain an encrypted pfsense configuration."); + return 1; + } + $data = decrypt_data($data, $_POST['decrypt_password']); + } + + if (stristr($data, "<m0n0wall>")) { + log_error(gettext("Upgrading m0n0wall configuration to pfsense.")); + /* m0n0wall was found in config. convert it. */ + $data = str_replace("m0n0wall", "pfsense", $data); + $m0n0wall_upgrade = true; + } + if ($_POST['restorearea']) { + /* restore a specific area of the configuration */ + if (!stristr($data, "<" . $_POST['restorearea'] . ">")) { + $input_errors[] = gettext("You have selected to restore an area but we could not locate the correct xml tag."); + } else { + if (!restore_config_section($_POST['restorearea'], $data)) { + $input_errors[] = gettext("You have selected to restore an area but we could not locate the correct xml tag."); + } else { + if ($config['rrddata']) { + restore_rrddata(); + unset($config['rrddata']); + unlink_if_exists("{$g['tmp_path']}/config.cache"); + write_config(); + add_base_packages_menu_items(); + convert_config(); + conf_mount_ro(); + } + filter_configure(); + $savemsg = gettext("The configuration area has been restored. You may need to reboot the firewall."); + } + } + } else { + if (!stristr($data, "<" . $g['xml_rootobj'] . ">")) { + $input_errors[] = sprintf(gettext("You have selected to restore the full configuration but we could not locate a %s tag."), $g['xml_rootobj']); + } else { + /* restore the entire configuration */ + file_put_contents($_FILES['conffile']['tmp_name'], $data); + if (config_install($_FILES['conffile']['tmp_name']) == 0) { + /* this will be picked up by /index.php */ + conf_mount_rw(); + mark_subsystem_dirty("restore"); + touch("/conf/needs_package_sync"); + /* remove cache, we will force a config reboot */ + if (file_exists("{$g['tmp_path']}/config.cache")) { + unlink("{$g['tmp_path']}/config.cache"); + } + $config = parse_config(true); + if (file_exists("/boot/loader.conf")) { + $loaderconf = file_get_contents("/boot/loader.conf"); + if (strpos($loaderconf, "console=\"comconsole")) { + $config['system']['enableserial'] = true; + write_config("Restore serial console enabling in configuration."); + } + unset($loaderconf); + } + /* extract out rrd items, unset from $config when done */ + if ($config['rrddata']) { + restore_rrddata(); + unset($config['rrddata']); + unlink_if_exists("{$g['tmp_path']}/config.cache"); + write_config(); + add_base_packages_menu_items(); + convert_config(); + conf_mount_ro(); + } + if ($m0n0wall_upgrade == true) { + if ($config['system']['gateway'] <> "") { + $config['interfaces']['wan']['gateway'] = $config['system']['gateway']; + } + unset($config['shaper']); + /* optional if list */ + $ifdescrs = get_configured_interface_list(true, true); + /* remove special characters from interface descriptions */ + if (is_array($ifdescrs)) { + foreach ($ifdescrs as $iface) { + $config['interfaces'][$iface]['descr'] = remove_bad_chars($config['interfaces'][$iface]['descr']); + } + } + /* check for interface names with an alias */ + if (is_array($ifdescrs)) { + foreach ($ifdescrs as $iface) { + if (is_alias($config['interfaces'][$iface]['descr'])) { + // Firewall rules + $origname = $config['interfaces'][$iface]['descr']; + $newname = $config['interfaces'][$iface]['descr'] . "Alias"; + update_alias_names_upon_change(array('filter', 'rule'), array('source', 'address'), $newname, $origname); + update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'address'), $newname, $origname); + // NAT Rules + update_alias_names_upon_change(array('nat', 'rule'), array('source', 'address'), $newname, $origname); + update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'address'), $newname, $origname); + update_alias_names_upon_change(array('nat', 'rule'), array('target'), $newname, $origname); + // Alias in an alias + update_alias_names_upon_change(array('aliases', 'alias'), array('address'), $newname, $origname); + } + } + } + unlink_if_exists("{$g['tmp_path']}/config.cache"); + // Reset configuration version to something low + // in order to force the config upgrade code to + // run through with all steps that are required. + $config['system']['version'] = "1.0"; + // Deal with descriptions longer than 63 characters + for ($i = 0; isset($config["filter"]["rule"][$i]); $i++) { + if (count($config['filter']['rule'][$i]['descr']) > 63) { + $config['filter']['rule'][$i]['descr'] = substr($config['filter']['rule'][$i]['descr'], 0, 63); + } + } + // Move interface from ipsec to enc0 + for ($i = 0; isset($config["filter"]["rule"][$i]); $i++) { + if ($config['filter']['rule'][$i]['interface'] == "ipsec") { + $config['filter']['rule'][$i]['interface'] = "enc0"; + } + } + // Convert icmp types + // http://www.openbsd.org/cgi-bin/man.cgi?query=icmp&sektion=4&arch=i386&apropos=0&manpath=OpenBSD+Current + for ($i = 0; isset($config["filter"]["rule"][$i]); $i++) { + if ($config["filter"]["rule"][$i]['icmptype']) { + switch ($config["filter"]["rule"][$i]['icmptype']) { + case "echo": + $config["filter"]["rule"][$i]['icmptype'] = "echoreq"; + break; + case "unreach": + $config["filter"]["rule"][$i]['icmptype'] = "unreach"; + break; + case "echorep": + $config["filter"]["rule"][$i]['icmptype'] = "echorep"; + break; + case "squench": + $config["filter"]["rule"][$i]['icmptype'] = "squench"; + break; + case "redir": + $config["filter"]["rule"][$i]['icmptype'] = "redir"; + break; + case "timex": + $config["filter"]["rule"][$i]['icmptype'] = "timex"; + break; + case "paramprob": + $config["filter"]["rule"][$i]['icmptype'] = "paramprob"; + break; + case "timest": + $config["filter"]["rule"][$i]['icmptype'] = "timereq"; + break; + case "timestrep": + $config["filter"]["rule"][$i]['icmptype'] = "timerep"; + break; + case "inforeq": + $config["filter"]["rule"][$i]['icmptype'] = "inforeq"; + break; + case "inforep": + $config["filter"]["rule"][$i]['icmptype'] = "inforep"; + break; + case "maskreq": + $config["filter"]["rule"][$i]['icmptype'] = "maskreq"; + break; + case "maskrep": + $config["filter"]["rule"][$i]['icmptype'] = "maskrep"; + break; + } + } + } + $config['diag']['ipv6nat'] = true; + write_config(); + add_base_packages_menu_items(); + convert_config(); + conf_mount_ro(); + $savemsg = gettext("The m0n0wall configuration has been restored and upgraded to pfSense."); + mark_subsystem_dirty("restore"); + } + if (is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $cp) { + if (isset($cp['enable'])) { + /* for some reason ipfw doesn't init correctly except on bootup sequence */ + mark_subsystem_dirty("restore"); + break; + } + } + } + setup_serial_port(); + if (is_interface_mismatch() == true) { + touch("/var/run/interface_mismatch_reboot_needed"); + clear_subsystem_dirty("restore"); + convert_config(); + header("Location: interfaces_assign.php"); + exit; + } + if (is_interface_vlan_mismatch() == true) { + touch("/var/run/interface_mismatch_reboot_needed"); + clear_subsystem_dirty("restore"); + convert_config(); + header("Location: interfaces_assign.php"); + exit; + } + } else { + $input_errors[] = gettext("The configuration could not be restored."); + } + } + } + } else { + $input_errors[] = gettext("The configuration could not be restored (file upload error)."); + } + } + } + + if ($mode == "reinstallpackages") { + header("Location: pkg_mgr_install.php?mode=reinstallall"); + exit; + } else if ($mode == "clearpackagelock") { + clear_subsystem_dirty('packagelock'); + $savemsg = "Package Lock Cleared"; + } else if ($mode == "restore_ver") { + $input_errors[] = gettext("XXX - this feature may hose your config (do NOT backrev configs!) - billm"); + if ($ver2restore <> "") { + $conf_file = "{$g['cf_conf_path']}/bak/config-" . strtotime($ver2restore) . ".xml"; + if (config_install($conf_file) == 0) { + mark_subsystem_dirty("restore"); + } else { + $input_errors[] = gettext("The configuration could not be restored."); + } + } else { + $input_errors[] = gettext("No version selected."); + } + } + } +} + +$id = rand() . '.' . time(); + +$mth = ini_get('upload_progress_meter.store_method'); +$dir = ini_get('upload_progress_meter.file.filename_template'); + +function build_area_list($showall) { + global $config; + + $areas = array( + "aliases" => gettext("Aliases"), + "captiveportal" => gettext("Captive Portal"), + "voucher" => gettext("Captive Portal Vouchers"), + "dnsmasq" => gettext("DNS Forwarder"), + "unbound" => gettext("DNS Resolver"), + "dhcpd" => gettext("DHCP Server"), + "dhcpdv6" => gettext("DHCPv6 Server"), + "filter" => gettext("Firewall Rules"), + "interfaces" => gettext("Interfaces"), + "ipsec" => gettext("IPSEC"), + "nat" => gettext("NAT"), + "openvpn" => gettext("OpenVPN"), + "installedpackages" => gettext("Package Manager"), + "pptpd" => gettext("PPTP Server"), + "rrddata" => gettext("RRD Data"), + "cron" => gettext("Scheduled Tasks"), + "syslog" => gettext("Syslog"), + "system" => gettext("System"), + "staticroutes" => gettext("Static routes"), + "sysctl" => gettext("System tunables"), + "snmpd" => gettext("SNMP Server"), + "shaper" => gettext("Traffic Shaper"), + "vlans" => gettext("VLANS"), + "wol" => gettext("Wake on LAN") + ); + + $list = array("" => gettext("All")); + + if($showall) + return($list + $areas); + else { + foreach ($areas as $area => $areaname) { + if ($area === "rrddata" || check_and_returnif_section_exists($area) == true) { + $list[$area] = $areaname; + } + } + + return($list); + } +} + +$pgtitle = array(gettext("Diagnostics"), gettext("Backup/restore")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('restore')): +?> + <br/> + <form action="reboot.php" method="post"> + <input name="Submit" type="hidden" value="Yes" /> + <?=print_info_box(gettext("The firewall configuration has been changed.") . "<br />" . gettext("The firewall is now rebooting."))?> + <br /> + </form> +<?php +endif; + +$tab_array = array(); +$tab_array[] = array(gettext("Config History"), false, "diag_confbak.php"); +$tab_array[] = array(gettext("Backup/Restore"), true, "diag_backup.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form(false); + +$section = new Form_Section('Backup configuration'); + +$section->addInput(new Form_Select( + 'backuparea', + 'Backup area', + '', + build_area_list(false) +)); + +$section->addInput(new Form_Checkbox( + 'nopackages', + 'Skip packages', + 'Do not backup package information.', + false +)); + +$section->addInput(new Form_Checkbox( + 'donotbackuprrd', + 'Skip RRD data', + 'Do not backup RRD data (NOTE: RRD Data can consume 4+ megabytes of config.xml space!)', + true +)); + +$section->addInput(new Form_Checkbox( + 'encrypt', + 'Encryption', + 'Encrypt this configuration file.', + false +))->toggles('.toggle-passwords');; + +$section->addInput(new Form_Input( + 'encrypt_password', + null, + 'password', + null, + ['placeholder' => 'Password'] +))->addClass('toggle-passwords'); + +$section->addInput(new Form_Input( + 'encrypt_passconf', + null, + 'password', + null, + ['placeholder' => 'Confirm password'] +))->addClass('toggle-passwords'); + +$group = new Form_Group(''); +$group->add(new Form_Button( + 'Submit', + 'Download configuration as XML' +)); + +$section->add($group); +$form->add($section); + +$section = new Form_Section('Restore backup'); + +$section->addInput(new Form_StaticText( + null, + gettext("Open a ") . $g['[product_name'] . gettext(" configuration XML file and click the button below to restore the configuration.") +)); + +$section->addInput(new Form_Select( + 'restorearea', + 'Restore area', + '', + build_area_list(false) +)); + +$section->addInput(new Form_Input( + 'conffile', + 'Configuration file', + 'file', + null +)); + +$section->addInput(new Form_Checkbox( + 'encrypt', + 'Encryption', + 'Encrypt this configuration file.', + false +))->toggles('.toggle-dpasswords');; + +$section->addInput(new Form_Input( + 'decrypt_password', + null, + 'password', + null, + ['placeholder' => 'Password'] +))->addClass('toggle-dpasswords'); + +$section->addInput(new Form_Input( + 'decrypt_passconf', + null, + 'password', + null, + ['placeholder' => 'Confirm password'] +))->addClass('toggle-dpasswords'); + +$group = new Form_Group(''); +$group->add(new Form_Button( + 'Submit', + 'Restore configuration' +))->setHelp('The firewall will reboot after restoring the configuration.')->removeClass('btn-primary')->addClass('btn-danger'); + +$section->add($group); + +$form->add($section); + +if (($config['installedpackages']['package'] != "") || (is_subsystem_dirty("packagelock"))) { + $section = new Form_Section('Package functions'); + + if ($config['installedpackages']['package'] != "") { + $group = new Form_Group(''); + $group->add(new Form_Button( + 'Submit', + 'Reinstall packages' + ))->setHelp('Click this button to reinstall all system packages. This may take a while.')->removeClass('btn-primary')->addClass('btn-warning'); + + $section->add($group); + } + + if (is_subsystem_dirty("packagelock")) { + $group = new Form_Group(''); + $group->add(new Form_Button( + 'Submit', + 'Clear Package Lock' + ))->setHelp('Click this button to clear the package lock if a package fails to reinstall properly after an upgrade.')->removeClass('btn-primary')->addClass('btn-warning'); + + $section->add($group); + } + + $form->add($section); +} + +print($form); + +include("foot.inc"); + +if (is_subsystem_dirty('restore')) { + system_reboot(); +}
\ No newline at end of file diff --git a/src/usr/local/www/diag_confbak.php b/src/usr/local/www/diag_confbak.php new file mode 100644 index 0000000..bc9cc51 --- /dev/null +++ b/src/usr/local/www/diag_confbak.php @@ -0,0 +1,367 @@ +<?php +/* $Id$ */ +/* + diag_confbak.php + Copyright (C) 2005 Colin Smith + Copyright (C) 2010 Jim Pingle + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + pfSense_MODULE: config +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-configurationhistory +##|*NAME=Diagnostics: Configuration History page +##|*DESCR=Allow access to the 'Diagnostics: Configuration History' page. +##|*MATCH=diag_confbak.php* +##|-PRIV + +require("guiconfig.inc"); + +if (isset($_POST['backupcount'])) { + if (is_numeric($_POST['backupcount']) && ($_POST['backupcount'] >= 0)) { + $config['system']['backupcount'] = $_POST['backupcount']; + $changedescr = $config['system']['backupcount']; + } else { + unset($config['system']['backupcount']); + $changedescr = "(platform default)"; + } + write_config("Changed backup revision count to {$changedescr}"); +} elseif ($_POST) { + if (!isset($_POST['confirm']) || ($_POST['confirm'] != gettext("Confirm")) || (!isset($_POST['newver']) && !isset($_POST['rmver']))) { + header("Location: diag_confbak.php"); + return; + } + + conf_mount_rw(); + $confvers = unserialize(file_get_contents($g['cf_conf_path'] . '/backup/backup.cache')); + if ($_POST['newver'] != "") { + if (config_restore($g['conf_path'] . '/backup/config-' . $_POST['newver'] . '.xml') == 0) { + $savemsg = sprintf(gettext('Successfully reverted to timestamp %1$s with description "%2$s".'), date(gettext("n/j/y H:i:s"), $_POST['newver']), htmlspecialchars($confvers[$_POST['newver']]['description'])); + } else { + $savemsg = gettext("Unable to revert to the selected configuration."); + } + } + if ($_POST['rmver'] != "") { + unlink_if_exists($g['conf_path'] . '/backup/config-' . $_POST['rmver'] . '.xml'); + $savemsg = sprintf(gettext('Deleted backup with timestamp %1$s and description "%2$s".'), date(gettext("n/j/y H:i:s"), $_POST['rmver']), htmlspecialchars($confvers[$_POST['rmver']]['description'])); + } + conf_mount_ro(); +} + +if ($_GET['getcfg'] != "") { + $file = $g['conf_path'] . '/backup/config-' . $_GET['getcfg'] . '.xml'; + + $exp_name = urlencode("config-{$config['system']['hostname']}.{$config['system']['domain']}-{$_GET['getcfg']}.xml"); + $exp_data = file_get_contents($file); + $exp_size = strlen($exp_data); + + header("Content-Type: application/octet-stream"); + header("Content-Disposition: attachment; filename={$exp_name}"); + header("Content-Length: $exp_size"); + echo $exp_data; + exit; +} + +if (($_GET['diff'] == 'Diff') && isset($_GET['oldtime']) && isset($_GET['newtime']) && + (is_numeric($_GET['oldtime'])) && + (is_numeric($_GET['newtime']) || ($_GET['newtime'] == 'current'))) { + $diff = ""; + $oldfile = $g['conf_path'] . '/backup/config-' . $_GET['oldtime'] . '.xml'; + $oldtime = $_GET['oldtime']; + if ($_GET['newtime'] == 'current') { + $newfile = $g['conf_path'] . '/config.xml'; + $newtime = $config['revision']['time']; + } else { + $newfile = $g['conf_path'] . '/backup/config-' . $_GET['newtime'] . '.xml'; + $newtime = $_GET['newtime']; + } + if (file_exists($oldfile) && file_exists($newfile)) { + exec("/usr/bin/diff -u " . escapeshellarg($oldfile) . " " . escapeshellarg($newfile), $diff); + } +} + +cleanup_backupcache(false); +$confvers = get_backups(); +unset($confvers['versions']); + +$pgtitle = array(gettext("Diagnostics"), gettext("Configuration History")); +include("head.inc"); + +if($savemsg) + print_info_box($savemsg); +?> + <?php if ($diff):?> + <h3><?=gettext("Configuration diff from")?><?=date(gettext("n/j/y H:i:s"), $oldtime)?><?=gettext("to")?><?=date(gettext("n/j/y H:i:s"), $newtime)?></h3> + <pre><?php foreach ($diff as $line) { + switch (substr($line, 0, 1)) { + case "+": + $color = "#caffd3"; + break; + case "-": + $color = "#ffe8e8"; + break; + case "@": + $color = "#a0a0a0"; + break; + default: + $color = "#ffffff"; + } + + print '<span style="background-color: '.$color .'">'. htmlentities($line) .'</span><br/>'; + } + ?></pre> +<?php endif?> +<?PHP if ($_GET["newver"] || $_GET["rmver"]):?> + <h2><?=gettext("Confirm Action")?></h2> + <form action="diag_confbak.php" method="post"> + <div class="alert alert-danger"> + <p><?=gettext("Please confirm you wish to ")?> + <?PHP + if (!empty($_GET["newver"])) { + echo gettext("restore from Configuration Backup"); + $target_config = $_GET["newver"]?> + <input type="hidden" name="newver" value="<?PHP echo htmlspecialchars($_GET["newver"])?>" /> + <?PHP + } elseif (!empty($_GET["rmver"])) { + echo gettext("remove Configuration Backup"); + $target_config = $_GET["rmver"]?> + <input type="hidden" name="rmver" value="<?PHP echo htmlspecialchars($_GET["rmver"])?>" /> + <?PHP + } ?> + <?PHP echo gettext("revert to configuration from ")?> <?=date(gettext("n/j/y H:i:s"), $target_config)?> + <br /> + <input type="submit" name="confirm" value="<?PHP echo gettext("Confirm")?>" /> + </p> + </div> + </form> +<?PHP else:?> +<?php + $tab_array = array(); + $tab_array[0] = array(gettext("Config History"), true, "diag_confbak.php"); + $tab_array[1] = array(gettext("Backup/Restore"), false, "diag_backup.php"); + display_top_tabs($tab_array); +?> + <form action="diag_confbak.php" method="post"> + <div class="form-group"> + <label for="backupcount" class="col-sm-2 control-label"><?=gettext("Backup Count")?></label> + <div class="col-sm-10"> + <input name="backupcount" type="number" class="form-control" size="5" value="<?=htmlspecialchars($config['system']['backupcount'])?>" /> + <?=gettext("Maximum number of old configurations to keep. By default this is 30 for a full install or 5 on NanoBSD.")?> + </div> + </div> + + <div class="form-group"> + <div class="col-sm-offset-2 col-sm-10"> + <input name="Submit" type="submit" class="btn btn-primary" value="<?=gettext("Save")?>" /> + <p><?=gettext("Current space used by backups: ")?><?=exec("/usr/bin/du -sh /conf/backup | /usr/bin/awk '{print $1;}'")?></p> + </div> + </div> + </form> +<?php if (!is_array($confvers)): ?> + <?php print_info_box(gettext("No backups found."))?> +<?php else: ?> + <form action="diag_confbak.php" method="get"> + <div class="table-responsive"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th><input type="submit" name="diff" class="btn btn-default" value="<?=gettext("Diff")?>" /></th> + <th><?=gettext("Date")?></th> + <th><?=gettext("Version")?></th> + <th><?=gettext("Size")?></th> + <th><?=gettext("Configuration Change")?></th> + <th></th> + </tr> + </thead> + + <tbody> + <tr> + <td> + <input type="radio" name="oldtime" disabled="disabled" /> + <input type="radio" name="newtime" value="current" <?=($_GET['newtime']==$version['time'] ? ' checked="checked"' : '')?>/> + </td> + <td><?=date(gettext("n/j/y H:i:s"), $config['revision']['time'])?></td> + <td><?=$config['version']?></td> + <td><?=format_bytes(filesize("/conf/config.xml"))?></td> + <td><?=$config['revision']['description']?></td> + <td><i><?=gettext("Current")?></i></td> + </tr> + <?php + foreach($confvers as $version): + if($version['time'] != 0) + $date = date(gettext("n/j/y H:i:s"), $version['time']); + else + $date = gettext("Unknown"); + ?> + <tr> + <td> + <div id="mainarea"> + <form action="diag_confbak.php" method="post"> + <table class="tabcont" align="center" width="100%" border="0" cellpadding="6" cellspacing="0" summary="tabcont"> + +<?php if ($_GET["newver"] || $_GET["rmver"]): ?> + <tr> + <td colspan="2" valign="top" class="listtopic"><?php echo gettext("Confirm Action"); ?></td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"> </td> + <td width="78%" class="vtable"> + + <strong><?php echo gettext("Please confirm the selected action"); ?></strong>: + <br /> + <br /><strong><?php echo gettext("Action"); ?>:</strong> + <?php if (!empty($_GET["newver"])) { + echo gettext("Restore from Configuration Backup"); + $target_config = $_GET["newver"]; ?> + <input type="hidden" name="newver" value="<?php echo htmlspecialchars($_GET["newver"]); ?>" /> + <?php } elseif (!empty($_GET["rmver"])) { + echo gettext("Remove Configuration Backup"); + $target_config = $_GET["rmver"]; ?> + <input type="hidden" name="rmver" value="<?php echo htmlspecialchars($_GET["rmver"]); ?>" /> + <?php } ?> + <br /><strong><?php echo gettext("Target Configuration"); ?>:</strong> + <?php echo sprintf(gettext('Timestamp %1$s'), date(gettext("n/j/y H:i:s"), $target_config)); ?> + <br /><input type="submit" name="confirm" value="<?php echo gettext("Confirm"); ?>" /> + </td> + </tr> +<?php else: ?> + + <tr> + <td width="10%"> </td> + <td width="15%" valign="top"><?=gettext("Backup Count");?></td> + <td width="10%"> + <input name="backupcount" type="text" class="formfld unknown" size="5" value="<?=htmlspecialchars($config['system']['backupcount']);?>"/> + </td> + <td width="60%"> + <?= gettext("Enter the number of older configurations to keep in the local backup cache. By default this is 30 for a full install or 5 on NanoBSD."); ?> + </td> + <td width= "5%"><input name="save" type="submit" class="formbtn" value="<?=gettext("Save"); ?>" /></td> + </tr> + <tr> + <td class="vncell"> </td> + <td colspan="4" class="vncell"> + <?= gettext("NOTE: Be aware of how much space is consumed by backups before adjusting this value. Current space used by backups: "); ?> <?= exec("/usr/bin/du -sh /conf/backup | /usr/bin/awk '{print $1;}'") ?> + </td> + </tr> + </table> + </form> + <form action="diag_confbak.php" method="get"> + <table class="tabcont" align="center" width="100%" border="0" cellpadding="6" cellspacing="0" summary="difference"> + <?php if (is_array($confvers)): ?> + <tr> + <td colspan="7" class="list"> + <?= gettext("To view the differences between an older configuration and a newer configuration, select the older configuration using the left column of radio options and select the newer configuration in the right column, then press the Diff button."); ?> + <br /><br /> + </td> + </tr> + <tr> + <td width="5%" colspan="2" valign="middle" align="center" class="list nowrap"><input type="submit" name="diff" value="<?=gettext("Diff"); ?>" /></td> + <td width="20%" class="listhdrr"><?=gettext("Date");?></td> + <td width="5%" class="listhdrr"><?=gettext("Version");?></td> + <td width="5%" class="listhdrr"><?=gettext("Size");?></td> + <td width="60%" class="listhdrr"><?=gettext("Configuration Change");?></td> + <td width="5%" class="list"> </td> + </tr> + <tr valign="top"> + <td valign="middle" class="list nowrap"></td> + <td class="list"> + <input type="radio" name="newtime" value="current" /> + </td> + <td class="listlr"> <?= date(gettext("n/j/y H:i:s"), $config['revision']['time']) ?></td> + <td class="listr"> <?= $config['version'] ?></td> + <td class="listr"> <?= format_bytes(filesize("/conf/config.xml")) ?></td> + <td class="listr"> <?= htmlspecialchars($config['revision']['description']) ?></td> + <td valign="middle" class="list nowrap"><b><?=gettext("Current");?></b></td> + </tr> + <?php + $c = 0; + foreach ($confvers as $version): + if ($version['time'] != 0) { + $date = date(gettext("n/j/y H:i:s"), $version['time']); + } else { + $date = gettext("Unknown"); + } + ?> + <tr valign="top"> + <td class="list"> + <input type="radio" name="oldtime" value="<?php echo $version['time'];?>" /> + </td> + <td class="list"> + <?php if ($c < (count($confvers) - 1)) { ?> + <input type="radio" name="newtime" value="<?php echo $version['time'];?>" /> + <?php } else { ?> + + <?php } + $c++; ?> + </td> + <td class="listlr"> <?= $date ?></td> + <td class="listr"> <?= $version['version'] ?></td> + <td class="listr"> <?= format_bytes($version['filesize']) ?></td> + <td class="listr"> <?= htmlspecialchars($version['description']) ?></td> + <td valign="middle" class="list nowrap"> + <a href="diag_confbak.php?newver=<?=$version['time'];?>"> + <img src="/themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" width="17" height="17" border="0" alt="<?=gettext("Revert to this configuration");?>" title="<?=gettext("Revert to this configuration");?>" /> + </a> + <a href="diag_confbak.php?rmver=<?=$version['time'];?>"> + <img src="/themes/<?= $g['theme']; ?>/images/icons/icon_x.gif" width="17" height="17" border="0" alt="<?=gettext("Remove this backup");?>" title="<?=gettext("Remove this backup");?>" /> + </a> + <a href="diag_confbak.php?getcfg=<?=$version['time'];?>"> + <img src="/themes/<?= $g['theme']; ?>/images/icons/icon_down.gif" width="17" height="17" border="0" alt="<?=gettext("Download this backup");?>" title="<?=gettext("Download this backup");?>" /> + </a> + </td> + </tr> + <?php endforeach; ?> + <tr> + <td colspan="2"><input type="submit" name="diff" value="<?=gettext("Diff"); ?>" /></td> + <td colspan="5"></td> + </tr> + <?php else: ?> + <tr> + <td> + <?php print_info_box(gettext("No backups found.")); ?> + </td> + </tr> + <?php endif; ?> +<?php endif; ?> + </table> + </form> + </div> + </td> + </tr> + <?php endforeach?> + </tbody> + <tfoot> + <tr> + <td colspan="6"><input type="submit" name="diff" class="btn btn-default" value="<?=gettext("Compare selected")?>" /></td> + </tr> + <?php endif; ?> +<?php endif?> + </table> + </div> + </form> +<?php include("foot.inc")?>
\ No newline at end of file diff --git a/src/usr/local/www/diag_defaults.php b/src/usr/local/www/diag_defaults.php new file mode 100755 index 0000000..79786ad --- /dev/null +++ b/src/usr/local/www/diag_defaults.php @@ -0,0 +1,107 @@ +<?php +/* $Id$ */ +/* + diag_defaults.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_MODULE: config +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-factorydefaults +##|*NAME=Diagnostics: Factory defaults page +##|*DESCR=Allow access to the 'Diagnostics: Factory defaults' page. +##|*MATCH=diag_defaults.php* +##|-PRIV + +require("guiconfig.inc"); + +if ($_POST['Submit'] == " " . gettext("No") . " ") { + header("Location: index.php"); + exit; +} + +$pgtitle = array(gettext("Diagnostics"), gettext("Factory defaults")); +include("head.inc"); +?> + +<?php if ($_POST['Submit'] == " " . gettext("Yes") . " "): + print_info_box(gettext("The system has been reset to factory defaults and is now rebooting. This may take a few minutes, depending on your hardware."))?> +<pre> +<?php + reset_factory_defaults(); + system_reboot(); +?> +</pre> +<?php else:?> +<form action="diag_defaults.php" method="post"> + <p><strong><?=gettext("If you click") . " "" . gettext("Yes") . "", " . gettext("the firewall will:")?></strong></p> + <ul> + <li><?=gettext("Reset to factory defaults")?></li> + <li><?=gettext("LAN IP address will be reset to 192.168.1.1")?></li> + <li><?=gettext("System will be configured as a DHCP server on the default LAN interface")?></li> + <li><?=gettext("Reboot after changes are installed")?></li> + <li><?=gettext("WAN interface will be set to obtain an address automatically from a DHCP server")?></li> + <li><?=gettext("webConfigurator admin username will be reset to 'admin'")?></li> + <li><?=gettext("webConfigurator admin password will be reset to")?> '<?=$g['factory_shipped_password']?>'</li> + </ul> + <p><strong><?=gettext("Are you sure you want to proceed?")?></strong></p> + <p> + <input name="Submit" type="submit" class="btn btn-sm btn-success" value=" <?=gettext("Yes")?> " /> + <input name="Submit" type="submit" class="btn btn-sm btn-default" value=" <?=gettext("No")?> " /> + </p> +</form> +<?php endif?> +<?php include("foot.inc")?>
\ No newline at end of file diff --git a/src/usr/local/www/diag_dns.php b/src/usr/local/www/diag_dns.php new file mode 100755 index 0000000..a1267e2 --- /dev/null +++ b/src/usr/local/www/diag_dns.php @@ -0,0 +1,301 @@ +<?php +/* + diag_dns.php +*/ + /* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2009 Jim Pingle (jpingle@gmail.com) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: dns +*/ + +$pgtitle = array(gettext("Diagnostics"), gettext("DNS Lookup")); +require("guiconfig.inc"); + +$host = trim($_REQUEST['host'], " \t\n\r\0\x0B[];\"'"); +$host_esc = escapeshellarg($host); + +/* If this section of config.xml has not been populated yet we need to set it up +*/ +if (!is_array($config['aliases']['alias'])) { + $config['aliases']['alias'] = array(); +} +$a_aliases = &$config['aliases']['alias']; + +$aliasname = str_replace(array(".", "-"), "_", $host); +$alias_exists = false; +$counter = 0; +foreach ($a_aliases as $a) { + if ($a['name'] == $aliasname) { + $alias_exists = true; + $id = $counter; + } + $counter++; +} + +if (isset($_POST['create_alias']) && (is_hostname($host) || is_ipaddr($host))) { + if ($_POST['override']) { + $override = true; + } + $resolved = gethostbyname($host); + $type = "hostname"; + if ($resolved) { + $resolved = array(); + exec("/usr/bin/drill {$host_esc} A | /usr/bin/grep {$host_esc} | /usr/bin/grep -v ';' | /usr/bin/awk '{ print $5 }'", $resolved); + $isfirst = true; + foreach($resolved as $re) { + if($re != "") { + if(!$isfirst) + $addresses .= " "; + $addresses .= rtrim($re) . "/32"; + $isfirst = false; + } + } + $newalias = array(); + if ($override) { + $alias_exists = false; + } + if ($alias_exists == false) { + $newalias['name'] = $aliasname; + $newalias['type'] = "network"; + $newalias['address'] = $addresses; + $newalias['descr'] = "Created from Diagnostics-> DNS Lookup"; + if ($override) { + $a_aliases[$id] = $newalias; + } else { + $a_aliases[] = $newalias; + } + write_config(); + $createdalias = true; + } + } +} + +if ($_POST) { + unset($input_errors); + + $reqdfields = explode(" ", "host"); + $reqdfieldsn = explode(",", "Host"); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (!is_hostname($host) && !is_ipaddr($host)) { + $input_errors[] = gettext("Host must be a valid hostname or IP address."); + } else { + // Test resolution speed of each DNS server. + $dns_speeds = array(); + $dns_servers = array(); + exec("/usr/bin/grep nameserver /etc/resolv.conf | /usr/bin/cut -f2 -d' '", $dns_servers); + foreach ($dns_servers as $dns_server) { + $query_time = exec("/usr/bin/drill {$host_esc} " . escapeshellarg("@" . trim($dns_server)) . " | /usr/bin/grep Query | /usr/bin/cut -d':' -f2"); + if ($query_time == "") { + $query_time = gettext("No response"); + } + $new_qt = array(); + $new_qt['dns_server'] = $dns_server; + $new_qt['query_time'] = $query_time; + $dns_speeds[] = $new_qt; + unset($new_qt); + } + } + + $type = "unknown"; + $resolved = ""; + $ipaddr = ""; + $hostname = ""; + if (!$input_errors) { + if (is_ipaddr($host)) { + $type = "ip"; + $resolved = gethostbyaddr($host); + $ipaddr = $host; + if ($host != $resolved) { + $hostname = $resolved; + } + } elseif (is_hostname($host)) { + $type = "hostname"; + $resolved = gethostbyname($host); + if ($resolved) { + $resolved = array(); + exec("/usr/bin/drill {$host_esc} A | /usr/bin/grep {$host_esc} | /usr/bin/grep -v ';' | /usr/bin/awk '{ print $5 }'", $resolved); + } + $hostname = $host; + if ($host != $resolved) { + $ipaddr = $resolved[0]; + } + } + + if ($host == $resolved) { + $resolved = gettext("No record found"); + } + } +} + +if (($_POST['host']) && ($_POST['dialog_output'])) { + display_host_results ($host, $resolved, $dns_speeds); + exit; +} + +function display_host_results ($address, $hostname, $dns_speeds) { + $map_lengths = function($element) { return strlen($element[0]); }; + + echo gettext("IP Address") . ": {$address} \n"; + echo gettext("Host Name") . ": {$hostname} \n"; + echo "\n"; + $text_table = array(); + $text_table[] = array(gettext("Server"), gettext("Query Time")); + if (is_array($dns_speeds)) { + foreach ($dns_speeds as $qt) { + $text_table[] = array(trim($qt['dns_server']), trim($qt['query_time'])); + } + } + $col0_padlength = max(array_map($map_lengths, $text_table)) + 4; + foreach ($text_table as $text_row) { + echo str_pad($text_row[0], $col0_padlength) . $text_row[1] . "\n"; + } +} + +include("head.inc"); + +/* Display any error messages resulting from user input */ +if ($input_errors) + print_input_errors($input_errors); +else if (!$resolved && $type) + print('<div class="alert alert-warning" role="alert">' . gettext("Host") .' "'. $host .'" '. gettext("could not be resolved") . '</div>'); + +if ($createdalias) + print('<div class="alert alert-success" role="alert">'.gettext("Alias was created/updated successfully").'</div>'); + +require('classes/Form.class.php'); + +$form = new Form('Lookup'); +$section = new Form_Section('DNS Lookup'); + +$section->addInput(new Form_Input( + 'host', + 'Hostname', + 'text', + $host, + ['placeholder' => 'Hostname to look up.'] +)); + +if (!empty($resolved)) { + $form->addGlobal(new Form_Button( + 'create_alias', + 'Add alias' + ))->removeClass('btn-primary')->addClass('btn-success'); +} + +$form->add($section); +print $form; + +if (!$input_errors && $type) { + if ($resolved): +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">Results</h2></div> + <div class="panel-body"> + <ul class="list-group"> +<? + foreach ((array)$resolved as $hostitem) { +?> + <li class="list-group-item"><?=$hostitem?></li> +<? + if ($hostitem != "") { + $found++; + } + } +?> + </ul> + </div> +</div> +<? endif?> + +<!-- Second table displays the server resolution times --> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">Timings</h2></div> + <div class="panel-body"> + <table class="table"> + <thead> + <tr> + <th>Name server</th> + <th>Query time</th> + </tr> + </thead> + + <tbody> +<? foreach ((array)$dns_speeds as $qt):?> + <tr> + <td><?=$qt['dns_server']?></td><td><?=$qt['query_time']?></td> + </tr> +<? endforeach?> + </tbody> + </table> + </div> +</div> + +<!-- Third table displays "More information" --> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">More information</h2></div> + <div class="panel-body"> + <ul class="list-group"> + <li class="list-group-item"><a href="/diag_ping.php?host=<?=htmlspecialchars($host)?>&interface=wan&count=3"><?=gettext("Ping")?></a></li> + <li class="list-group-item"><a href="/diag_traceroute.php?host=<?=htmlspecialchars($host)?>&ttl=18"><?=gettext("Traceroute")?></a></li> + </ul> + <p><?=gettext("NOTE: The following links are to external services, so their reliability cannot be guaranteed.");?></p> + <ul class="list-group"> + <li class="list-group-item"><a target="_blank" href="http://private.dnsstuff.com/tools/whois.ch?ip=<?php echo $ipaddr; ?>"><?=gettext("IP WHOIS @ DNS Stuff");?></a></li> + <li class="list-group-item"><a target="_blank" href="http://private.dnsstuff.com/tools/ipall.ch?ip=<?php echo $ipaddr; ?>"><?=gettext("IP Info @ DNS Stuff");?></a></li> + </ul> + </div> +</div> +<?php +} +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_dump_states.php b/src/usr/local/www/diag_dump_states.php new file mode 100755 index 0000000..9bd63f1 --- /dev/null +++ b/src/usr/local/www/diag_dump_states.php @@ -0,0 +1,230 @@ +<?php +/* + diag_dump_states.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2005 Colin Smith + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /sbin/pfctl + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-showstates +##|*NAME=Diagnostics: Show States page +##|*DESCR=Allow access to the 'Diagnostics: Show States' page. +##|*MATCH=diag_dump_states.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("interfaces.inc"); + +/* handle AJAX operations */ +if (isset($_POST['action']) && $_POST['action'] == "remove") { + if (isset($_POST['srcip']) && isset($_POST['dstip']) && is_ipaddr($_POST['srcip']) && is_ipaddr($_POST['dstip'])) { + $retval = pfSense_kill_states($_POST['srcip'], $_POST['dstip']); + echo htmlentities("|{$_POST['srcip']}|{$_POST['dstip']}|0|"); + } else { + echo gettext("invalid input"); + } + return; +} + +if (isset($_POST['filter']) && isset($_POST['killfilter'])) { + if (is_ipaddr($_POST['filter'])) { + $tokill = $_POST['filter'] . "/32"; + } elseif (is_subnet($_POST['filter'])) { + $tokill = $_POST['filter']; + } else { + // Invalid filter + $tokill = ""; + } + if (!empty($tokill)) { + $retval = pfSense_kill_states($tokill); + $retval = pfSense_kill_states("0.0.0.0/0", $tokill); + } +} + +$pgtitle = array(gettext("Diagnostics"), gettext("Show States")); +include("head.inc"); +?> + +<script> +events.push(function(){ + $('a[data-entry]').on('click', function(){ + var el = $(this); + var data = $(this).data('entry').split('|'); + + $.ajax( + '/diag_dump_states.php', + { + type: 'post', + data: { + action: 'remove', + srcip: data[0], + dstip: data[1] + }, + success: function(){ + el.parents('tr').remove(); + }, + }); + }); +}); +</script> + +<?php +$tab_array = array(); +$tab_array[] = array(gettext("States"), true, "diag_dump_states.php"); +if (isset($config['system']['lb_use_sticky'])) + $tab_array[] = array(gettext("Source Tracking"), false, "diag_dump_states_sources.php"); +$tab_array[] = array(gettext("Reset States"), false, "diag_resetstate.php"); +display_top_tabs($tab_array); + +// Start of tab content +$current_statecount=`pfctl -si | grep "current entries" | awk '{ print $3 }'`; + +require('classes/Form.class.php'); + +$form = new Form(false); + +$section = new Form_Section('State filter'); + +$section->addInput(new Form_Input( + 'filter', + 'Filter expression', + 'text', + $_POST['filter'], + ['placeholder' => 'Simple filter such as 192.168, v6, icmp or ESTABLISHED'] +)); + +$filterbtn = new Form_Button('filterbtn', 'Filter', null); +$filterbtn->removeClass('btn-primary')->addClass('btn-default btn-sm'); +$section->addInput(new Form_StaticText( + '', + $filterbtn +)); + +if (isset($_POST['filter']) && (is_ipaddr($_POST['filter']) || is_subnet($_POST['filter']))) { + $killbtn = new Form_Button('killfilter', 'Kill States'); + $killbtn->removeClass('btn-primary')->addClass('btn-danger btn-sm'); + $section->addInput(new Form_StaticText( + 'Kill filtered states', + $killbtn + ))->setHelp('Remove all states to and from the filtered address'); +} + +$form->add($section); +print $form; +?> +<table class="table table-striped"> + <thead> + <tr> + <th><?=gettext("Int")?></th> + <th><?=gettext("Proto")?></th> + <th><?=gettext("Source -> Router -> Destination")?></th> + <th><?=gettext("State")?></th> + <th></th> <!-- For the optional "Remove" button --> + </tr> + </thead> + <tbody> +<?php + $row = 0; + /* get our states */ + $grepline = (isset($_POST['filter'])) ? "| /usr/bin/egrep " . escapeshellarg(htmlspecialchars($_POST['filter'])) : ""; + $fd = popen("/sbin/pfctl -s state {$grepline}", "r" ); + while ($line = chop(fgets($fd))) { + if($row >= 10000) + break; + + $line_split = preg_split("/\s+/", $line); + + $iface = array_shift($line_split); + $proto = array_shift($line_split); + $state = array_pop($line_split); + $info = implode(" ", $line_split); + + // We may want to make this optional, with a large state table, this could get to be expensive. + $iface = convert_real_interface_to_friendly_descr($iface); + + /* break up info and extract $srcip and $dstip */ + $ends = preg_split("/\<?-\>?/", $info); + $parts = explode(":", $ends[0]); + $srcip = trim($parts[0]); + $parts = explode(":", $ends[count($ends) - 1]); + $dstip = trim($parts[0]); +?> + <tr> + <td><?= $iface ?></td> + <td><?= $proto ?></td> + <td><?= $info ?></td> + <td><?= $state ?></td> + + <td> + <a class="btn btn-xs btn-danger" data-entry="<?=$srcip?>|<?=$dstip?>" + title="<?=sprintf(gettext('Remove all state entries from %s to %s'), $srcip, $dstip);?>">Remove</a> + </td> + </tr> +<?php $row++; } ?> + </tbody> +</table> +<?php + +if ($row == 0) { + if (isset($_POST['filter']) && !empty($_POST['filter'])) + $errmsg = gettext('No states were found that match the current filter'); + else + $errmsg = gettext('No states were found'); + + print('<p class="alert alert-warning">' . $errmsg . '</p>'); +} + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_dump_states_sources.php b/src/usr/local/www/diag_dump_states_sources.php new file mode 100644 index 0000000..04c8c03 --- /dev/null +++ b/src/usr/local/www/diag_dump_states_sources.php @@ -0,0 +1,202 @@ +<?php +/* + diag_dump_states_sources.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2005 Colin Smith + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /sbin/pfctl + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-showstates +##|*NAME=Diagnostics: Show States page +##|*DESCR=Allow access to the 'Diagnostics: Show States' page. +##|*MATCH=diag_dump_states.php* +##|-PRIV + +require_once("guiconfig.inc"); + +/* handle AJAX operations */ +if($_POST['action']) { + if($_POST['action'] == "remove") { + if (is_ipaddr($_POST['srcip']) && is_ipaddr($_POST['dstip'])) { + $retval = mwexec("/sbin/pfctl -K " . escapeshellarg($_POST['srcip']) . " -K " . escapeshellarg($_POST['dstip'])); + echo htmlentities("|{$_GET['srcip']}|{$_POST['dstip']}|{$retval}|"); + } else { + echo gettext("invalid input"); + } + exit; + } +} + +/* get our states */ +if($_POST['filter']) { + exec("/sbin/pfctl -s Sources | grep " . escapeshellarg(htmlspecialchars($_GET['filter'])), $sources); +} +else { + exec("/sbin/pfctl -s Sources", $sources); +} + + +$pgtitle = array(gettext("Diagnostics"),gettext("Show Source Tracking")); +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("States"), false, "diag_dump_states.php"); +$tab_array[] = array(gettext("Source Tracking"), true, "diag_dump_states_sources.php"); +$tab_array[] = array(gettext("Reset States"), false, "diag_resetstate.php"); +display_top_tabs($tab_array); + +?> + +<script> +events.push(function(){ + $('a[data-entry]').on('click', function(){ + var el = $(this); + var data = $(this).data('entry').split('|'); + + $.ajax( + '/diag_dump_states_sources.php', + { + type: 'post', + data: { + action: 'remove', + srcip: data[0], + dstip: data[1] + }, + success: function(){ + el.parents('tr').remove(); + }, + }); + }); +}); +</script> + +<?php + +require('classes/Form.class.php'); + +$form = new Form; +$section = new Form_Section('Filters'); + +$section->addInput(new Form_Input( + 'filter', + 'Filter expression', + 'text', + $_POST['filter'] +)); + +$form->add($section); +print $form; + +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">Current source tracking entries</h2></div> + <div class="panel-body"> + <table class="table table-striped"> + <thead> + <tr> + <th><?=gettext("Source -> Destination")?></th> + <th><?=gettext("# States")?></th> + <th><?=gettext("# Connections")?></th> + <th><?=gettext("Rate")?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php +$row = 0; +if (count($sources) > 0) { + foreach ($sources as $line) { + if ($row >= 1000) { + break; + } + + // 192.168.20.2 -> 216.252.56.1 ( states 10, connections 0, rate 0.0/0s ) + + $source_split = ""; + preg_match("/(.*)\s\(\sstates\s(.*),\sconnections\s(.*),\srate\s(.*)\s\)/", $line, $source_split); + list($all, $info, $numstates, $numconnections, $rate) = $source_split; + + $source_split = ""; + preg_match("/(.*)\s\<?-\>?\s(.*)/", $info, $source_split); + list($all, $srcip, $dstip) = $source_split; +?> + <tr> + <td><?= $info ?></td> + <td><?= $numstates ?></td> + <td><?= $numconnections ?></td> + <td><?= $rate ?></td> + + <td> + <a class="btn btn-xs btn-danger" data-entry="<?=$srcip?>|<?=$dstip?>" + title="<?=sprintf(gettext('Remove all source tracking entries from %s to %s'), $srcip, $dstip);?>">Remove</a> + </td> + </tr> +<?php + $row++; + } +} +?> + </tbody> + </table> + </div> +</div> +<?php +if ($row == 0) { + print('<p class="alert alert-warning">' . gettext('No source tracking entries were found.') . '</p>'); +} + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_gmirror.php b/src/usr/local/www/diag_gmirror.php new file mode 100644 index 0000000..17fbed8 --- /dev/null +++ b/src/usr/local/www/diag_gmirror.php @@ -0,0 +1,405 @@ +<?php +/* + diag_gmirror.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2014 Jim Pingle + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /sbin/gmirror /sbin/geom /usr/bin/grep /usr/bin/egrep /usr/bin/cut /usr/bin/head + pfSense_BUILDER_BINARIES: /sbin/mount /usr/bin/awk /usr/bin/sed + pfSense_MODULE: gmirror +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-gmirror +##|*NAME=Diagnostics: GEOM Mirrors +##|*DESCR=Allow access to the 'Diagnostics: GEOM Mirrors' page. +##|*MATCH=diag_gmirror.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("config.inc"); +require_once("gmirror.inc"); + +$pgtitle = array(gettext("Diagnostics"), gettext("GEOM Mirrors")); + +include("head.inc"); + +$action_list = array( + "forget" => gettext("Forget all formerly connected consumers"), + "clear" => gettext("Remove metadata from disk"), + "insert" => gettext("Insert consumer into mirror"), + "remove" => gettext("Remove consumer from mirror"), + "activate" => gettext("Reactivate consumer on mirror"), + "deactivate" => gettext("Deactivate consumer from mirror"), + "rebuild" => gettext("Force rebuild of mirror consumer"), +); + +/* User tried to pass a bogus action */ +if (!empty($_REQUEST['action']) && !array_key_exists($_REQUEST['action'], $action_list)) { + header("Location: diag_gmirror.php"); + return; +} + +if ($_POST) { + if (!isset($_POST['confirm']) || ($_POST['confirm'] != gettext("Confirm"))) { + header("Location: diag_gmirror.php"); + return; + } + + $input_errors = ""; + + if (($_POST['action'] != "clear") && !is_valid_mirror($_POST['mirror'])) { + $input_errors[] = gettext("You must supply a valid mirror name."); + } + + if (!empty($_POST['consumer']) && !is_valid_consumer($_POST['consumer'])) { + $input_errors[] = gettext("You must supply a valid consumer name"); + } + + /* Additional action-specific validation that hasn't already been tested */ + switch ($_POST['action']) { + case "insert": + if (!is_consumer_unused($_POST['consumer'])) { + $input_errors[] = gettext("Consumer is already in use and cannot be inserted. Remove consumer from existing mirror first."); + } + if (gmirror_consumer_has_metadata($_POST['consumer'])) { + $input_errors[] = gettext("Consumer has metadata from an existing mirror. Clear metadata before inserting consumer."); + } + $mstat = gmirror_get_status_single($_POST['mirror']); + if (strtoupper($mstat) != "COMPLETE") { + $input_errors[] = gettext("Mirror is not in a COMPLETE state, cannot insert consumer. Forget disconnected disks or wait for rebuild to finish."); + } + break; + + case "clear": + if (!is_consumer_unused($_POST['consumer'])) { + $input_errors[] = gettext("Consumer is in use and cannot be cleared. Deactivate disk first."); + } + if (!gmirror_consumer_has_metadata($_POST['consumer'])) { + $input_errors[] = gettext("Consumer has no metadata to clear."); + } + break; + + case "activate": + if (is_consumer_in_mirror($_POST['consumer'], $_POST['mirror'])) { + $input_errors[] = gettext("Consumer is already present on specified mirror."); + } + if (!gmirror_consumer_has_metadata($_POST['consumer'])) { + $input_errors[] = gettext("Consumer has no metadata and cannot be reactivated."); + } + + break; + + case "remove": + case "deactivate": + case "rebuild": + if (!is_consumer_in_mirror($_POST['consumer'], $_POST['mirror'])) { + $input_errors[] = gettext("Consumer must be present on the specified mirror."); + } + break; + } + + $result = 0; + if (empty($input_errors)) { + switch ($_POST['action']) { + case "forget": + $result = gmirror_forget_disconnected($_POST['mirror']); + break; + case "clear": + $result = gmirror_clear_consumer($_POST['consumer']); + break; + case "insert": + $result = gmirror_insert_consumer($_POST['mirror'], $_POST['consumer']); + break; + case "remove": + $result = gmirror_remove_consumer($_POST['mirror'], $_POST['consumer']); + break; + case "activate": + $result = gmirror_activate_consumer($_POST['mirror'], $_POST['consumer']); + break; + case "deactivate": + $result = gmirror_deactivate_consumer($_POST['mirror'], $_POST['consumer']); + break; + case "rebuild": + $result = gmirror_force_rebuild($_POST['mirror'], $_POST['consumer']); + break; + } + + $redir = "Location: diag_gmirror.php"; + + if ($result != 0) { + $redir .= "?error=" . urlencode($result); + } + + /* If we reload the page too fast, the gmirror information may be missing or not up-to-date. */ + sleep(3); + header($redir); + return; + } +} + +$mirror_status = gmirror_get_status(); +$mirror_list = gmirror_get_mirrors(); +$unused_disks = gmirror_get_disks(); +$unused_consumers = array(); + +foreach ($unused_disks as $disk) { + if (is_consumer_unused($disk)) { + $unused_consumers = array_merge($unused_consumers, gmirror_get_all_unused_consumer_sizes_on_disk($disk)); + } +} + +if ($input_errors) { + print_input_errors($input_errors); +} +if ($_GET["error"] && ($_GET["error"] != 0)) { + print_info_box(gettext("There was an error performing the chosen mirror operation. Check the System Log for details.")); +} + +?> +<form action="diag_gmirror.php" method="POST" id="gmirror_form" name="gmirror_form"> + +<!-- Confirmation screen --> +<?php +if ($_GET["action"]): ?> + <div class="panel panel-default"> + <div class="panel-heading">Confirm Action</div> + <div class="panel-body"> + <strong><?=gettext('Please confirm the selected action: '); ?></strong> + <span style="color:green"><?=$action_list[$_GET["action"]]; ?></span> + <input type="hidden" name="action" value="<?=htmlspecialchars($_GET['action']); ?>" /> +<?php + if (!empty($_GET["mirror"])): ?> + <br /><strong><?=gettext("Mirror: "); ?></strong> + <?=htmlspecialchars($_GET['mirror']); ?> + <input type="hidden" name="mirror" value="<?=htmlspecialchars($_GET['mirror']); ?>" /> +<?php + endif; ?> + +<?php + if (!empty($_GET["consumer"])): ?> + <br /><strong><?=gettext("Consumer"); ?>:</strong> + <?=htmlspecialchars($_GET["consumer"]); ?> + <input type="hidden" name="consumer" value="<?=htmlspecialchars($_GET["consumer"]); ?>" /> +<?php + endif; ?> + <br /> + <br /><input type="submit" name="confirm" class="btn btn-sm btn-primary" value="<?=gettext("Confirm"); ?>" /> + </div> + </div> +<?php +else: + // Status/display page + print_info_box(gettext("The options on this page are intended for use by advanced users only. This page is for managing existing mirrors, not creating new mirrors.")); +?> + + <!-- GEOM mirror table --> + <div class="panel panel-default"> + <div class="panel-heading">GEOM Mirror information - Mirror Status</div> + <div class="panel-body table-responsive"> + +<?php + if (count($mirror_status) > 0): ?> + + <table class="table table-striped stable-hover table-condensed"> + <thead> + <tr> + <th="><?=gettext("Name"); ?></th> + <th=><?=gettext("Status"); ?></th> + <th=><?=gettext("Component"); ?></th> + </tr> + </thead> + <tbody> +<?php + foreach ($mirror_status as $mirror => $name): + $components = count($name["components"]); ?> + <tr> + <td rowspan="<?=$components; ?>"> + <?=htmlspecialchars($name['name']); ?><br />Size: <?=gmirror_get_mirror_size($name['name']); ?> + </td> + <td rowspan="<?=$components; ?>"> + <?=htmlspecialchars($name['status']); ?> +<?php + if (strtoupper($name['status']) == "DEGRADED"): ?> + <br /><a href="diag_gmirror.php?action=forget&mirror=<?=htmlspecialchars($name['name']); ?>">[<?=gettext("Forget Disconnected Disks"); ?>]</a> +<?php + endif; ?> + </td> + <td> + <?=$name['components'][0]; ?> + <?php list($cname, $cstatus) = explode(" ", $name['components'][0], 2); ?><br /> +<?php + if ((strtoupper($name['status']) == "COMPLETE") && (count($name["components"]) > 1)): ?> + <a class="btn btn-xs btn-info" href="diag_gmirror.php?action=rebuild&consumer=<?=htmlspecialchars($cname); ?>&mirror=<?=htmlspecialchars($name['name']); ?>">[<?=gettext("Rebuild"); ?>]</a> + <a class="btn btn-xs btn-info" href="diag_gmirror.php?action=deactivate&consumer=<?=htmlspecialchars($cname); ?>&mirror=<?=htmlspecialchars($name['name']); ?>">[<?=gettext("Deactivate"); ?>]</a> + <a class="btn btn-xs btn-danger" href="diag_gmirror.php?action=remove&consumer=<?=htmlspecialchars($cname); ?>&mirror=<?=htmlspecialchars($name['name']); ?>">[<?=gettext("Remove"); ?>]</a> +<?php + endif; ?> + </td> + </tr> +<?php + if (count($name["components"]) > 1): + $morecomponents = array_slice($name["components"], 1); ?> +<?php + foreach ($morecomponents as $component): ?> + <tr> + <td> + <?=$component; ?> + <?php list($cname, $cstatus) = explode(" ", $component, 2); ?><br /> +<?php + if ((strtoupper($name['status']) == "COMPLETE") && (count($name["components"]) > 1)): ?> + <a class="btn btn-xs btn-info" href="diag_gmirror.php?action=rebuild&consumer=<?=htmlspecialchars($cname); ?>&mirror=<?=htmlspecialchars($name['name']); ?>">[<?=gettext("Rebuild"); ?>]</a> + <a class="btn btn-xs btn-info" href="diag_gmirror.php?action=deactivate&consumer=<?=htmlspecialchars($cname); ?>&mirror=<?=htmlspecialchars($name['name']); ?>">[<?=gettext("Deactivate"); ?>]</a> + <a class="btn btn-xs btn-danger" href="diag_gmirror.php?action=remove&consumer=<?=htmlspecialchars($cname); ?>&mirror=<?=htmlspecialchars($name['name']); ?>">[<?=gettext("Remove"); ?>]</a> +<?php + endif; ?> + </td> + </tr> +<?php + endforeach; ?> +<?php + endif; ?> +<?php + endforeach; ?> + </tbody> + </table> +<?php + else: ?> + <?=gettext("No Mirrors Found"); ?> + +<?php + endif; ?> + + </div> + </div> + +<?php print_info_box(gettext("Some disk operations may only be performed when there are multiple consumers present in a mirror."), 'default'); ?> + + <!-- Consumer information table --> + <div class="panel panel-default"> + <div class="panel-heading">Consumer information - Available consumers</div> + <div class="panel-body table-responsive"> +<?php + if (count($unused_consumers) > 0): ?> + <table class="table table-striped stable-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Name"); ?></th> + <th><?=gettext("Size"); ?></th> + <th><?=gettext("Add to Mirror"); ?></th> + </tr> + </thead> + + <tbody> +<?php + foreach ($unused_consumers as $consumer): ?> + <tr> + <td> + <?=htmlspecialchars($consumer['name']); ?> + </td> + <td> + <?=htmlspecialchars($consumer['size']); ?> + <?=htmlspecialchars($consumer['humansize']); ?> + </td> + <td> +<?php + $oldmirror = gmirror_get_consumer_metadata_mirror($consumer['name']); + + if ($oldmirror): ?> + <a class="btn btn-xs btn-success" href="diag_gmirror.php?action=activate&consumer=<?=htmlspecialchars($consumer['name']); ?>&mirror=<?=htmlspecialchars($oldmirror); ?>"> + <?=gettext("Reactivate on:") . ' ' . htmlspecialchars($oldmirror); ?> + </a> + <br /> + <a class="btn btn-xs btn-danger" href="diag_gmirror.php?action=clear&consumer=<?=htmlspecialchars($consumer['name']); ?>"> + <?=gettext("Remove metadata from disk"); ?> + </a> +<?php + else: ?> +<?php + foreach ($mirror_list as $mirror): + $mirror_size = gmirror_get_mirror_size($mirror); + $consumer_size = gmirror_get_unused_consumer_size($consumer['name']); + + if ($consumer_size > $mirror_size): ?> + <a class="btn btn-xs btn-info" href="diag_gmirror.php?action=insert&consumer=<?=htmlspecialchars($consumer['name']); ?>&mirror=<?=htmlspecialchars($mirror); ?>"> + <?=htmlspecialchars($mirror); ?> + </a> +<?php + endif; ?> +<?php + endforeach; ?> + +<?php + endif; ?> + </td> + </tr> +<?php + endforeach; ?> + </tbody> + </table> +<?php + else: ?> + <?=gettext("No unused consumers found"); ?> +<?php + endif; ?> + </div> + </div> +<?php + print_info_box(gettext("Consumers may only be added to a mirror if they are larger than the size of the mirror.") . '<br />' . + gettext("To repair a failed mirror, first perform a 'Forget' command on the mirror, followed by an 'insert' action on the new consumer."), 'default'); +endif; ?> +</form> + +<?php +require("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_ipsec.php b/src/usr/local/www/diag_ipsec.php new file mode 100644 index 0000000..f5a83c8 --- /dev/null +++ b/src/usr/local/www/diag_ipsec.php @@ -0,0 +1,518 @@ +<?php +/* $Id$ */ +/* + diag_ipsec.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_MODULE: ipsec +*/ + +##|+PRIV +##|*IDENT=page-status-ipsec +##|*NAME=Status: IPsec page +##|*DESCR=Allow access to the 'Status: IPsec' page. +##|*MATCH=diag_ipsec.php* +##|-PRIV + + +global $g; + +$pgtitle = array(gettext("Status"), gettext("IPsec")); +$shortcut_section = "ipsec"; + +require("guiconfig.inc"); +include("head.inc"); +require("ipsec.inc"); + +if ($_GET['act'] == 'connect') { + if (ctype_digit($_GET['ikeid'])) { + $ph1ent = ipsec_get_phase1($_GET['ikeid']); + if (!empty($ph1ent)) { + if (empty($ph1ent['iketype']) || $ph1ent['iketype'] == 'ikev1') { + $ph2entries = ipsec_get_number_of_phase2($_GET['ikeid']); + for ($i = 0; $i < $ph2entries; $i++) { + $connid = escapeshellarg("con{$_GET['ikeid']}00{$i}"); + mwexec("/usr/local/sbin/ipsec down {$connid}"); + mwexec("/usr/local/sbin/ipsec up {$connid}"); + } + } else { + mwexec("/usr/local/sbin/ipsec down con" . escapeshellarg($_GET['ikeid'])); + mwexec("/usr/local/sbin/ipsec up con" . escapeshellarg($_GET['ikeid'])); + } + } + } +} else if ($_GET['act'] == 'ikedisconnect') { + if (ctype_digit($_GET['ikeid'])) { + if (!empty($_GET['ikesaid']) && ctype_digit($_GET['ikesaid'])) { + mwexec("/usr/local/sbin/ipsec down con" . escapeshellarg($_GET['ikeid']) . "[" . escapeshellarg($_GET['ikesaid']) . "]"); + } else { + mwexec("/usr/local/sbin/ipsec down con" . escapeshellarg($_GET['ikeid'])); + } + } +} else if ($_GET['act'] == 'childdisconnect') { + if (ctype_digit($_GET['ikeid'])) { + if (!empty($_GET['ikesaid']) && ctype_digit($_GET['ikesaid'])) { + mwexec("/usr/local/sbin/ipsec down con" . escapeshellarg($_GET['ikeid']) . "{" . escapeshellarg($_GET['ikesaid']) . "}"); + } + } +} + +if (!is_array($config['ipsec']['phase1'])) { + $config['ipsec']['phase1'] = array(); +} + +$a_phase1 = &$config['ipsec']['phase1']; + +$status = ipsec_smp_dump_status(); + +$tab_array = array(); +$tab_array[] = array(gettext("Overview"), true, "diag_ipsec.php"); +$tab_array[] = array(gettext("Leases"), false, "diag_ipsec_leases.php"); +$tab_array[] = array(gettext("SAD"), false, "diag_ipsec_sad.php"); +$tab_array[] = array(gettext("SPD"), false, "diag_ipsec_spd.php"); +$tab_array[] = array(gettext("Logs"), false, "diag_logs_ipsec.php"); +display_top_tabs($tab_array); +?> + +<div class="panel panel-default"> + <div class="panel-heading">IPSec status</div> + <div class="panel-body table responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Description")?></th> + <th><?=gettext("Local ID")?></th> + <th><?=gettext("Local IP")?></th> + <th><?=gettext("Remote ID")?></th> + <th><?=gettext("Remote IP")?></th> + <th><?=gettext("Role")?></th> + <th><?=gettext("Reauth")?></th> + <th><?=gettext("Algo")?></th> + <th><?=gettext("Status")?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php +$ipsecconnected = array(); + +if (is_array($status['query']) && is_array($status['query']['ikesalist']) && is_array($status['query']['ikesalist']['ikesa'])): + foreach ($status['query']['ikesalist']['ikesa'] as $ikeid => $ikesa): + $con_id = substr($ikesa['peerconfig'], 3); + + if ($ikesa['version'] == 1) { + $ph1idx = substr($con_id, 0, strrpos(substr($con_id, 0, -1), '00')); + $ipsecconnected[$ph1idx] = $ph1idx; + } else { + $ipsecconnected[$con_id] = $ph1idx = $con_id; + } + + if (ipsec_phase1_status($status['query']['ikesalist']['ikesa'], $ikesa['id'])) + $icon = "pass"; + elseif (!isset($config['ipsec']['enable'])) + $icon = "block"; + else + $icon = "reject"; +?> + <tr> + <td> + <?=htmlspecialchars(ipsec_get_descr($ph1idx))?> + </td> + <td> +<?php + if (!is_array($ikesa['local'])) + echo gettext("Unknown"); + else { + if (!empty($ikesa['local']['identification'])) { + if ($ikesa['local']['identification'] == '%any') + print(gettext('Any identifier')); + else + print(htmlspecialchars($ikesa['local']['identification'])); + } else + print(gettext("Unknown")); + } + + if (ipsec_phase1_status($status['query']['ikesalist']['ikesa'], $ikesa['id'])) { + $icon = "pass"; + } elseif (!isset($config['ipsec']['enable'])) { + $icon = "block"; + } else { + $icon = "reject"; + } +?> + </td> + <td> +<?php + if (!is_array($ikesa['local'])) + print(gettext("Unknown")); + else { + if (!empty($ikesa['local']['address'])) + print(htmlspecialchars($ikesa['local']['address']) . '<br/>' . gettext('Port: ') . htmlspecialchars($ikesa['local']['port'])); + else + print(gettext("Unknown")); + if ($ikesa['local']['port'] == '4500') + print(" NAT-T"); + } +?> + </td> + <td> +<?php + if (!is_array($ikesa['remote'])) + print(gettext("Unknown")); + else { + $identity = ""; + if (!empty($ikesa['remote']['identification'])) { + if ($ikesa['remote']['identification'] == '%any') + $identity = 'Any identifier'; + else + $identity = htmlspecialchars($ikesa['remote']['identification']); + } + + if (is_array($ikesa['remote']['auth']) && !empty($ikesa['remote']['auth'][0]['identity'])) { + print(htmlspecialchars($ikesa['remote']['auth'][0]['identity'])); + print('<br/>' . $identity); + } else { + if (empty($identity)) + print(ettext("Unknown")); + else + print($identity); + } + } +?> + </td> + <td> +<?php + if (!is_array($ikesa['remote'])) + print(gettext("Unknown")); + else { + if (!empty($ikesa['remote']['address'])) + print(htmlspecialchars($ikesa['remote']['address']) . '<br/>' . gettext('Port: ') . htmlspecialchars($ikesa['remote']['port'])); + else + print(gettext("Unknown")); + if ($ikesa['remote']['port'] == '4500') + print(" NAT-T"); + } +?> + </td> + <td> + IKEv<?=htmlspecialchars($ikesa['version'])?> + <br/> + <?=htmlspecialchars($ikesa['role'])?> + </td> + <td> + <?=htmlspecialchars($ikesa['reauth']);?> + </td> + <td> + <?=htmlspecialchars($ikesa['encalg'])?> + <br/> + <?=htmlspecialchars($ikesa['intalg'])?> + <br/> + <?=htmlspecialchars($ikesa['prfalg'])?> + <br/> + <?=htmlspecialchars($ikesa['dhgroup'])?> + </td> + <td> +<?php + if($ikesa['status'] == 'established') + print('<span style="color:green">'); + else + print('<span>'); +?> + <?=ucfirst(htmlspecialchars($ikesa['status']))?> + <br/><?=htmlspecialchars($ikesa['established'])?> + </span> + </td> + <td > +<?php + if ($icon != "pass"): +?> + <a href="diag_ipsec.php?act=connect&ikeid=<?=$con_id; ?>" class="btn btn-xs btn-success" data-toggle="tooltip" title="Connect VPN" > + <?=gettext("Connect VPN")?> + </a> +<?php + else: +?> + <a href="diag_ipsec.php?act=ikedisconnect&ikeid=<?=$con_id; ?>" class="btn btn-xs btn-danger" data-toggle="tooltip" title="Disconnect VPN"> + <?=gettext("Disconnect")?> + </a><br /> + <a href="diag_ipsec.php?act=ikedisconnect&ikeid=<?=$con_id; ?>&ikesaid=<?=$ikesa['id']; ?>" class="btn btn-xs btn-warning" data-toggle="tooltip" title="Disconnect VPN connection"> + <?=gettext("Disconnect")?> + </a> +<?php + endif; +?> + </td> + </tr> + <tr> + <td colspan = 10> +<?php + if (is_array($ikesa['childsalist'])): +?> + <div id="btnchildsa-<?=$ikeid?>"> + <a type="button" onclick="show_childsa('childsa-<?=$ikeid?>','btnchildsa-<?=$ikeid?>');" class="btn btn-sm btn-default" /> + <?=gettext('Show child SA entries')?> + </a> + </div> + + <table class="table table-hover table-condensed" id="childsa-<?=$ikeid?>" style="display:none"> + <thead> + <tr class="info"> + <th><?=gettext("Local subnets")?></th> + <th><?=gettext("Local SPI(s)")?></th> + <th><?=gettext("Remote subnets")?></th> + <th><?=gettext("Times")?></th> + <th><?=gettext("Algo")?></th> + <th><?=gettext("Stats")?></th> + <th><!-- Buttons --></th> + </tr> + </thead> + <tbody> +<?php + if (is_array($ikesa['childsalist']['childsa'])): + foreach ($ikesa['childsalist']['childsa'] as $childsa): +?> + <tr> + <td> +<?php + if (is_array($childsa['local']) && + is_array($childsa['local']['networks']) && + is_array($childsa['local']['networks']['network'])) + foreach ($childsa['local']['networks']['network'] as $lnets) + print(htmlspecialchars(ipsec_fixup_network($lnets)) . "<br />"); + else + print(gettext("Unknown")); +?> + </td> + <td> +<?php + if (is_array($childsa['local'])) + print(gettext("Local: ") . htmlspecialchars($childsa['local']['spi'])); + + if (is_array($childsa['remote'])) + print('<br/>' . gettext('Remote: ') . htmlspecialchars($childsa['remote']['spi'])); +?> + </td> + <td> +<?php + if (is_array($childsa['remote']) && + is_array($childsa['remote']['networks']) && + is_array($childsa['remote']['networks']['network'])) + foreach ($childsa['remote']['networks']['network'] as $rnets) + print(htmlspecialchars(ipsec_fixup_network($rnets)) . '<br />'); + else + print(gettext("Unknown")); +?> + </td> + <td> +<?php + print(gettext("Rekey: ") . htmlspecialchars($childsa['rekey'])); + print('<br/>' . gettext('Life: ') . htmlspecialchars($childsa['lifetime'])); + print('<br/>' . gettext('Install: ') .htmlspecialchars($childsa['installtime'])); + +?> + </td> + <td> +<?php + print(htmlspecialchars($childsa['encalg']) . '<br/>'); + print(htmlspecialchars($childsa['intalg']) . '<br/>'); + + if (!empty($childsa['prfalg'])) + print(htmlspecialchars($childsa['prfalg']) . '<br/>'); + + if (!empty($childsa['dhgroup'])) + print(htmlspecialchars($childsa['dhgroup']) . '<br/>'); + + if (!empty($childsa['esn'])) + + print(gettext("IPComp: ") . htmlspecialchars($childsa['ipcomp'])); +?> + </td> + <td> +<?php + print(gettext("Bytes-In: ") . htmlspecialchars($childsa['bytesin']) . '<br/>'); + print(gettext("Packets-In: ") . htmlspecialchars($childsa['packetsin']) . '<br/>'); + print(gettext("Bytes-Out: ") . htmlspecialchars($childsa['bytesout']) . '<br/>'); + print(gettext("Packets-Out: ") . htmlspecialchars($childsa['packetsout']) . '<br/>'); +?> + </td> + <td> + <a href="diag_ipsec.php?act=childdisconnect&ikeid=<?=$con_id; ?>&ikesaid=<?=$childsa['reqid']; ?>" class="btn btn-xs btn-warning" data-toggle="tooltip" title="<?=gettext('Disconnect Child SA')?>"> + <?=gettext("Disconnect")?> + </a> + </td> + </tr> +<?php + endforeach; + endif; +?> + + </tbody> + </table> + </td> + </tr> +<?php + endif; + + unset($con_id); + endforeach; +endif; + +$rgmap = array(); +foreach ($a_phase1 as $ph1ent): + if (isset($ph1ent['disabled'])) + continue; + + $rgmap[$ph1ent['remote-gateway']] = $ph1ent['remote-gateway']; + + if ($ipsecconnected[$ph1ent['ikeid']]) + continue; +?> + <tr> + <td> +<?php + print(htmlspecialchars($ph1ent['descr'])); +?> + </td> + <td> +<?php + list ($myid_type, $myid_data) = ipsec_find_id($ph1ent, "local"); + if (empty($myid_data)) + print(gettext("Unknown")); + else + print(htmlspecialchars($myid_data)); +?> + </td> + <td> +<?php + $ph1src = ipsec_get_phase1_src($ph1ent); + + if (empty($ph1src)) + print(gettext("Unknown")); + else + print(htmlspecialchars($ph1src)); +?> + </td> + <td> +<?php + list ($peerid_type, $peerid_data) = ipsec_find_id($ph1ent, "peer", $rgmap); + if (empty($peerid_data)) + print(gettext("Unknown")); + else + print(htmlspecialchars($peerid_data)); +?> + </td> + <td> +<?php + $ph1src = ipsec_get_phase1_dst($ph1ent); + if (empty($ph1src)) + print(gettext("Unknown")); + else + print(htmlspecialchars($ph1src)); +?> + </td> + <td> + </td> + <td> + </td> + <td> + </td> +<?php + if (isset($ph1ent['mobile'])): +?> + <td> + <?=gettext("Awaiting connections")?> + </td> + <td> + </td> +<?php + else: +?> + <td> + <?=gettext("Disconnected")?> + </td> + <td > + <a href="diag_ipsec.php?act=connect&ikeid=<?=$ph1ent['ikeid']; ?>" class="btn btn-xs btn-success"> + <?=gettext("Connect VPN")?> + </a> + </td> +<?php + endif; +?> + <td>> + </td> + </tr> +<?php +endforeach; +unset($ipsecconnected, $phase1, $rgmap); +?> + </tbody> + </table> + </div> +</div> + +<script type="text/javascript"> +//<![CDATA[ +function show_childsa(id, buttonid) { + document.getElementById(buttonid).innerHTML=''; + aodiv = document.getElementById(id); + aodiv.style.display = "block"; +} +//]]> +</script> + +<?php +unset($status); +print_info_box(gettext("You can configure IPsec ") . '<a href="vpn_ipsec.php">Here</a>'); +include("foot.inc"); ?> diff --git a/src/usr/local/www/diag_ipsec_leases.php b/src/usr/local/www/diag_ipsec_leases.php new file mode 100644 index 0000000..0bf5012 --- /dev/null +++ b/src/usr/local/www/diag_ipsec_leases.php @@ -0,0 +1,166 @@ +<?php +/* $Id$ */ +/* + diag_ipsec_leases.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2014 Ermal Luçi + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /usr/local/sbin/ipsec + pfSense_MODULE: ipsec +*/ + +##|+PRIV +##|*IDENT=page-status-ipsec-leases +##|*NAME=Status: IPsec: Leases page +##|*DESCR=Allow access to the 'Status: IPsec: Leases' page. +##|*MATCH=diag_ipsec_leases.php* +##|-PRIV + +define(DEBUG, true); // Force dummy data for testing. Setting up a pFSense box to get real data is far too hard! + +require("guiconfig.inc"); +require("ipsec.inc"); + +$pgtitle = array(gettext("Status"),gettext("IPsec"),gettext("Leases")); +$shortcut_section = "ipsec"; +include("head.inc"); + +$mobile = ipsec_dump_mobile(); + +$tab_array = array(); +$tab_array[] = array(gettext("Overview"), false, "diag_ipsec.php"); +$tab_array[] = array(gettext("Leases"), true, "diag_ipsec_leases.php"); +$tab_array[] = array(gettext("SAD"), false, "diag_ipsec_sad.php"); +$tab_array[] = array(gettext("SPD"), false, "diag_ipsec_spd.php"); +$tab_array[] = array(gettext("Logs"), false, "diag_logs.php?logfile=ipsec"); +display_top_tabs($tab_array); + +if (isset($mobile['pool']) && is_array($mobile['pool'])) { +?> + <div class="table-responsive"> + <table class="table table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Pool")?></th> + <th><?=gettext("Usage")?></th> + <th><?=gettext("Online")?></th> + <th><?=gettext("ID")?></th> + <th><?=gettext("Host")?></th> + <th><?=gettext("Status")?></th> + </tr> + </thead> + <tbody> +<?php + foreach($mobile['pool'] as $pool) { + // The first row of each pool includes the pool information +?> + <tr> + <td> + <?=$pool['name']?> + </td> + <td> + <?=$pool['usage']?> + </td> + <td> + <?=$pool['online']?> + </td> + +<?php + $leaserow = true; + if (is_array($pool['lease']) && count($pool['lease']) > 0) { + foreach ($pool['lease'] as $lease) { + if(!$leaserow) { + // On subsequent rows the first three columns are blank +?> + <tr> + <td></td> + <td></td> + <td></td> +<?php + } + $leaserow = false; +?> + <td> + <?=htmlspecialchars($lease['id'])?> + </td> + <td> + <?=htmlspecialchars($lease['host'])?> + </td> + <td> + <?=htmlspecialchars($lease['status'])?> + </td> + </tr> +<?php + + } + } + else { +?> + <td colspan="3" class="warning"><?=gettext('No leases from this pool yet.')?></td> + </tr> +<?php + } + } +?> + </tbody> + </table> + </div> +<?php +} +else + print_info_box(gettext('No IPsec pools.')); + +print_info_box(gettext('You can configure your IPsec subsystem by clicking ') . '<a href="vpn_ipsec.php">' . gettext("here.") . '</a>'); + +include("foot.inc"); diff --git a/src/usr/local/www/diag_ipsec_sad.php b/src/usr/local/www/diag_ipsec_sad.php new file mode 100644 index 0000000..2a9579f --- /dev/null +++ b/src/usr/local/www/diag_ipsec_sad.php @@ -0,0 +1,159 @@ +<?php +/* $Id$ */ +/* + diag_ipsec_sad.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004-2009 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /sbin/setkey + pfSense_MODULE: ipsec +*/ + +##|+PRIV +##|*IDENT=page-status-ipsec-sad +##|*NAME=Status: IPsec: SAD page +##|*DESCR=Allow access to the 'Status: IPsec: SAD' page. +##|*MATCH=diag_ipsec_sad.php* +##|-PRIV + +require("guiconfig.inc"); +require("ipsec.inc"); + +$pgtitle = array(gettext("Status"),gettext("IPsec"),gettext("SAD")); +$shortcut_section = "ipsec"; +include("head.inc"); + +$sad = ipsec_dump_sad(); + +/* delete any SA? */ +if ($_GET['act'] == "del") { + $fd = @popen("/sbin/setkey -c > /dev/null 2>&1", "w"); + if ($fd) { + fwrite($fd, "delete {$_GET['src']} {$_GET['dst']} {$_GET['proto']} {$_GET['spi']} ;\n"); + pclose($fd); + sleep(1); + } +} + +$tab_array = array(); +$tab_array[] = array(gettext("Overview"), false, "diag_ipsec.php"); +$tab_array[] = array(gettext("Leases"), false, "diag_ipsec_leases.php"); +$tab_array[] = array(gettext("SAD"), true, "diag_ipsec_sad.php"); +$tab_array[] = array(gettext("SPD"), false, "diag_ipsec_spd.php"); +$tab_array[] = array(gettext("Logs"), false, "diag_logs.php?logfile=ipsec"); +display_top_tabs($tab_array); + +if (count($sad)) { +?> + <div table-responsive> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Source")?></th> + <th><?=gettext("Destination")?></th> + <th><?=gettext("Protocol")?></th> + <th><?=gettext("SPI")?></th> + <th><?=gettext("Enc. alg.")?></th> + <th><?=gettext("Auth. alg.")?></th> + <th><?=gettext("Data")?></th> + <th></th> + </tr> + </thead> + <tbody> + <?php foreach ($sad as $sa) { ?> + <tr> + <td> + <?=htmlspecialchars($sa['src'])?> + </td> + <td> + <?=htmlspecialchars($sa['dst'])?> + </td> + <td> + <?=htmlspecialchars(strtoupper($sa['proto']))?> + </td> + <td> + <?=htmlspecialchars($sa['spi'])?> + </td> + <td> + <?=htmlspecialchars($sa['ealgo'])?> + </td> + <td> + <?=htmlspecialchars($sa['aalgo'])?> + </td> + <td> + <?=htmlspecialchars($sa['data'])?></td> + <td> + <?php + $args = "src=" . rawurlencode($sa['src']); + $args .= "&dst=" . rawurlencode($sa['dst']); + $args .= "&proto=" . rawurlencode($sa['proto']); + $args .= "&spi=" . rawurlencode("0x" . $sa['spi']); + ?> + <a class="btn btn-xs btn-danger" href="diag_ipsec_sad.php?act=del&<?=$args?>">Delete</a> + </td> + </tr> + + <?php + } ?> + </tbody> + </table> + </div> +<?php + } +else + print_info_box(gettext('No IPsec security associations.')); + +print_info_box(gettext('You can configure your IPsec subsystem by clicking ') . '<a href="vpn_ipsec.php">' . gettext("here.") . '</a>'); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_ipsec_spd.php b/src/usr/local/www/diag_ipsec_spd.php new file mode 100644 index 0000000..c952188 --- /dev/null +++ b/src/usr/local/www/diag_ipsec_spd.php @@ -0,0 +1,149 @@ +<?php +/* $Id$ */ +/* + diag_ipsec_spd.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004-2009 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /sbin/setkey + pfSense_MODULE: ipsec +*/ + +##|+PRIV +##|*IDENT=page-status-ipsec-spd +##|*NAME=Status: IPsec: SPD page +##|*DESCR=Allow access to the 'Status: IPsec: SPD' page. +##|*MATCH=diag_ipsec_spd.php* +##|-PRIV + +define(DEBUG, true); +define(RIGHTARROW, '►'); +define(LEFTARROW, '◀'); + +require("guiconfig.inc"); +require("ipsec.inc"); + +$pgtitle = array(gettext("Status"),gettext("IPsec"),gettext("SPD")); +$shortcut_section = "ipsec"; +include("head.inc"); + +if(DEBUG) { // Dummy data for testing. REMOVE for production + $spd = array ( 0 => array ( 'srcid' => '172.27.0.0/16', 'dstid' => '172.21.2.0/24', 'dir' => 'in' , 'proto' => 'esp', 'dst' => '184.57.8.247', 'src' => '208.123.73.7', 'reqid' => 'nique:1' ), + 1 => array ( 'srcid' => '172.21.2.0/24', 'dstid' => '172.27.0.0/16', 'dir' => 'out', 'proto' => 'esp', 'dst' => '208.123.73.7', 'src' => '184.57.8.247', 'reqid' => 'nique:1' ) ); +} +else + $spd = ipsec_dump_spd(); + +$tab_array = array(); +$tab_array[0] = array(gettext("Overview"), false, "diag_ipsec.php"); +$tab_array[1] = array(gettext("Leases"), false, "diag_ipsec_leases.php"); +$tab_array[2] = array(gettext("SAD"), false, "diag_ipsec_sad.php"); +$tab_array[3] = array(gettext("SPD"), true, "diag_ipsec_spd.php"); +$tab_array[4] = array(gettext("Logs"), false, "diag_logs.php?logfile=ipsec"); +display_top_tabs($tab_array); + +if (count($spd)){ +?> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?= gettext("Source"); ?></th> + <th><?= gettext("Destination"); ?></th> + <th><?= gettext("Direction"); ?></th> + <th><?= gettext("Protocol"); ?></th> + <th><?= gettext("Tunnel endpoints"); ?></th> + </tr> + </thead> + + <tbody> +<?php + foreach ($spd as $sp) { + if($sp['dir'] == 'in') + $dirstr = LEFTARROW . ' Inbound'; + else + $dirstr = RIGHTARROW . ' Outbound'; +?> + <tr> + <td> + <?=htmlspecialchars($sp['srcid'])?> + </td> + <td> + <?=htmlspecialchars($sp['dstid'])?> + </td> + <td> + <?=$dirstr ?> + </td> + <td> + <?=htmlspecialchars(strtoupper($sp['proto']))?> + </td> + <td> + <?=htmlspecialchars($sp['src'])?> -> <?=htmlspecialchars($sp['dst'])?> + </td> + </tr> +<?php + } +?> + </tbody> + </table> + </div> +<?php + } // e-o-if (count($spd)) +else { + print_info_box(gettext('No IPsec security policies configured.')); +} + +print_info_box(gettext('You can configure your IPsec subsystem by clicking ') . '<a href="vpn_ipsec.php">' . gettext("here.") . '</a>'); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_ipsec_xml.php b/src/usr/local/www/diag_ipsec_xml.php new file mode 100644 index 0000000..e495685 --- /dev/null +++ b/src/usr/local/www/diag_ipsec_xml.php @@ -0,0 +1,107 @@ +<?php +/* $Id$ */ +/* + diag_ipsec_xml.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2010 Seth Mos + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +##|+PRIV +##|*IDENT=page-ipsecxml +##|*NAME=Diag IPsec XML page +##|*DESCR=Allow access to the 'Diag IPsec XML' page. +##|*MATCH=diag_ipsec_xml.php +##|-PRIV + +global $g; + +require("guiconfig.inc"); +require("ipsec.inc"); + +if (!is_array($config['ipsec']['phase2'])) { + $config['ipsec']['phase2'] = array(); +} + +$ipsec_status = array(); + +$a_phase2 = &$config['ipsec']['phase2']; + +$status = ipsec_smp_dump_status(); + +if (is_array($status['query']) && $status['query']['ikesalist'] && $status['query']['ikesalist']['ikesa']) { + foreach ($a_phase2 as $ph2ent) { + ipsec_lookup_phase1($ph2ent, $ph1ent); + $tunnel = array(); + if (!isset($ph2ent['disabled']) && !isset($ph1ent['disabled'])) { + if (ipsec_phase1_status($status['query']['ikesalist']['ikesa'], $ph1ent['ikeid'])) { + $tunnel['state'] = "up"; + } elseif (!isset($config['ipsec']['enable'])) { + $tunnel['state'] = "disabled"; + } else { + $tunnel['state'] = "down"; + } + + $tunnel['src'] = ipsec_get_phase1_src($ph1ent); + $tunnel['endpoint'] = $ph1ent['remote-gateway']; + $tunnel['local'] = ipsec_idinfo_to_text($ph2ent['localid']); + $tunnel['remote'] = ipsec_idinfo_to_text($ph2ent['remoteid']); + $tunnel['name'] = "{$ph2ent['descr']}"; + $ipsec_status['tunnel'][] = $tunnel; + } + } +} + +$listtags = array("tunnel"); +$xml = dump_xml_config($ipsec_status, "ipsec"); + +echo $xml; +?> diff --git a/src/usr/local/www/diag_limiter_info.php b/src/usr/local/www/diag_limiter_info.php new file mode 100644 index 0000000..8c7cd1f --- /dev/null +++ b/src/usr/local/www/diag_limiter_info.php @@ -0,0 +1,124 @@ +<?php +/* $Id$ */ +/* + diag_limiter_info.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2010 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /usr/bin/top + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-limiter-info +##|*NAME=Diagnostics: Limiter Info +##|*DESCR=Allows access to the 'Diagnostics: Limiter Info' page +##|*MATCH=diag_limiter_info.php* +##|-PRIV + +require("guiconfig.inc"); + +$pgtitle = gettext("Diagnostics: Limiter Info"); +$shortcut_section = "trafficshaper-limiters"; + +if ($_REQUEST['getactivity']) { + $text = `/sbin/ipfw pipe show`; + if ($text == "") { + $text = "We could not find any limiters on this system."; + } + echo "Limiters:\n"; + echo $text; + $text = `/sbin/ipfw queue show`; + if ($text != "") { + echo "\n\nQueues:\n"; + echo $text; + } + exit; +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +?> +<script> + function getlimiteractivity() { + $.ajax( + '/diag_limiter_info.php', + { + type: 'post', + data: { + getactivity: 'yes' + }, + success: function (data) { + $('#xhrOutput').html(data); + }, + }); + } + + events.push(function(){ + setInterval('getlimiteractivity()', 2500); + getlimiteractivity(); + }); +</script> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">Limiter Information</h2></div> + <div class="panel-body"> + <pre id="xhrOutput"><?=gettext("Gathering Limiter information, please wait...")?></pre> + </div> +</div> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_logs.php b/src/usr/local/www/diag_logs.php new file mode 100755 index 0000000..69422cc --- /dev/null +++ b/src/usr/local/www/diag_logs.php @@ -0,0 +1,175 @@ +<?php +/* $Id$ */ +/* + diag_logs.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004-2009 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-logs-system +##|*NAME=Diagnostics: Logs: System page +##|*DESCR=Allow access to the 'Diagnostics: Logs: System' page. +##|*MATCH=diag_logs.php* +##|-PRIV + +require("guiconfig.inc"); + +// The logs to display are specified in a GET argument. Default to 'system' logs +if(!$_GET['logfile']) + $logfile = 'system'; +else + $logfile = $_GET['logfile']; + +$system_logfile = "{$g['varlog_path']}/{$logfile}.log"; + +$nentries = $config['syslog']['nentries']; +if (!$nentries) { + $nentries = 50; +} + +if ($_POST['clear']) { + clear_log_file($system_logfile); +} + +if ($_GET['filtertext']) { + $filtertext = htmlspecialchars($_GET['filtertext']); +} + +if ($_POST['filtertext']) { + $filtertext = htmlspecialchars($_POST['filtertext']); +} + +if ($filtertext) { + $filtertextmeta="?filtertext=$filtertext"; +} + +$pgtitle = array(gettext("Status"), gettext("System logs"), gettext("General")); +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("System"), ($logfile == 'system'), "diag_logs.php"); +$tab_array[] = array(gettext("Firewall"), false, "diag_logs_filter.php"); +$tab_array[] = array(gettext("DHCP"), ($logfile == 'dhcpd'), "diag_logs.php?logfile=dhcpd"); +$tab_array[] = array(gettext("Portal Auth"), ($logfile == 'portalauth'), "diag_logs.php?logfile=portalauth"); +$tab_array[] = array(gettext("IPsec"), ($logfile == 'ipsec'), "diag_logs.php?logfile=ipsec"); +$tab_array[] = array(gettext("PPP"), ($logfile == 'ppp'), "diag_logs.php?logfile=ppp"); +$tab_array[] = array(gettext("VPN"), false, "diag_logs_vpn.php"); +$tab_array[] = array(gettext("Load Balancer"), ($logfile == 'relayd'), "diag_logs.php?logfile=relayd"); +$tab_array[] = array(gettext("OpenVPN"), ($logfile == 'openvpn'), "diag_logs.php?logfile=openvpn"); +$tab_array[] = array(gettext("NTP"), ($logfile == 'ntpd'), "diag_logs.php?logfile=ntpd"); +$tab_array[] = array(gettext("Settings"), false, "diag_logs_settings.php"); +display_top_tabs($tab_array); + +$tab_array = array(); +if (in_array($logfile, array('system', 'gateways', 'routing', 'resolver', 'wireless'))) { + $tab_array[] = array(gettext("General"), ($logfile == 'system'), "/diag_logs.php"); + $tab_array[] = array(gettext("Gateways"), ($logfile == 'gateways'), "/diag_logs.php?logfile=gateways"); + $tab_array[] = array(gettext("Routing"), ($logfile == 'routing'), "/diag_logs.php?logfile=routing"); + $tab_array[] = array(gettext("Resolver"), ($logfile == 'resolver'), "/diag_logs.php?logfile=resolver"); + $tab_array[] = array(gettext("Wireless"), ($logfile == 'wireless'), "/diag_logs.php?logfile=wireless"); + display_top_tabs($tab_array, false, 'nav nav-tabs'); +} + +require('classes/Form.class.php'); + +$form = new Form(false); + +$section = new Form_Section('Log file filter'); + +$section->addInput(new Form_Input( + 'filtertext', + 'Filter', + 'text', + $filtertext, + ['placeholder' => 'Filter text'] +)); + +$form->addGlobal(new Form_Button( + 'filtersubmit', + 'Filter' +))->removeClass('btn-primary')->addClass('btn-default')->addClass('btn-sm'); + +$form->addGlobal(new Form_Button( + 'clear', + 'Clear log' +))->removeClass('btn-primary')->addClass('btn-danger')->addClass('btn-sm'); + +$form->add($section); +print $form; + +if($logfile == 'dhcpd') + print_info_box('Warning: Clearing the log file will restart the DHCP daemon.'); + +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Last ")?><?=$nentries?> <?=$logfile?><?=gettext(" log entries")?></h2></div> + <pre> +<?php + if(($logfile == 'resolver') || ($logfile == 'system')) + $inverse = array("ppp"); + else + $inverse = null; + + if($filtertext) + dump_clog_no_table($system_logfile, $nentries, true, array("$filtertext"), $inverse); + else + dump_clog_no_table($system_logfile, $nentries, true, array(), $inverse); +?> + </pre> +</div> + +<?php include("foot.inc"); ?>
\ No newline at end of file diff --git a/src/usr/local/www/diag_logs_filter.php b/src/usr/local/www/diag_logs_filter.php new file mode 100644 index 0000000..fd4df31 --- /dev/null +++ b/src/usr/local/www/diag_logs_filter.php @@ -0,0 +1,551 @@ +<?php +/* $Id$ */ +/* + diag_logs_filter.php +*/ + +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004-2009 Scott Ullrich + * Copyright (c) 2003-2009 Manuel Kasper <mk@neon1.net> + * Jim Pingle jim@pingle.org + * originally based on m0n0wall (http://m0n0.ch/wall) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-logs-firewall +##|*NAME=Diagnostics: Logs: Firewall page +##|*DESCR=Allow access to the 'Diagnostics: Logs: Firewall' page. +##|*MATCH=diag_logs_filter.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("filter_log.inc"); + +# --- AJAX RESOLVE --- +if (isset($_POST['resolve'])) { + $ip = strtolower($_POST['resolve']); + $res = (is_ipaddr($ip) ? gethostbyaddr($ip) : ''); + + if ($res && $res != $ip) { + $response = array('resolve_ip' => $ip, 'resolve_text' => $res); + } else { + $response = array('resolve_ip' => $ip, 'resolve_text' => gettext("Cannot resolve")); + } + + echo json_encode(str_replace("\\", "\\\\", $response)); // single escape chars can break JSON decode + exit; +} + +function getGETPOSTsettingvalue($settingname, $default) { + $settingvalue = $default; + if ($_GET[$settingname]) { + $settingvalue = $_GET[$settingname]; + } + if ($_POST[$settingname]) { + $settingvalue = $_POST[$settingname]; + } + return $settingvalue; +} + +$rulenum = getGETPOSTsettingvalue('getrulenum', null); +if ($rulenum) { + list($rulenum, $tracker, $type) = explode(',', $rulenum); + $rule = find_rule_by_number($rulenum, $tracker, $type); + echo gettext("The rule that triggered this action is") . ":\n\n{$rule}"; + exit; +} + +$filtersubmit = getGETPOSTsettingvalue('filtersubmit', null); + +if ($filtersubmit) { + $interfacefilter = getGETPOSTsettingvalue('interface', null); + $filtertext = getGETPOSTsettingvalue('filtertext', ""); + $filterlogentries_qty = getGETPOSTsettingvalue('filterlogentries_qty', null); +} + +$filterlogentries_submit = getGETPOSTsettingvalue('filterlogentries_submit', null); + +if ($filterlogentries_submit) { + $filterfieldsarray = array(); + + $actpass = getGETPOSTsettingvalue('actpass', null); + $actblock = getGETPOSTsettingvalue('actblock', null); + + $filterfieldsarray['act'] = str_replace(" ", " ", trim($actpass . " " . $actblock)); + $filterfieldsarray['act'] = $filterfieldsarray['act'] != "" ? $filterfieldsarray['act'] : 'All'; + $filterfieldsarray['time'] = getGETPOSTsettingvalue('filterlogentries_time', null); + $filterfieldsarray['interface'] = getGETPOSTsettingvalue('filterlogentries_interfaces', null); + $filterfieldsarray['srcip'] = getGETPOSTsettingvalue('filterlogentries_sourceipaddress', null); + $filterfieldsarray['srcport'] = getGETPOSTsettingvalue('filterlogentries_sourceport', null); + $filterfieldsarray['dstip'] = getGETPOSTsettingvalue('filterlogentries_destinationipaddress', null); + $filterfieldsarray['dstport'] = getGETPOSTsettingvalue('filterlogentries_destinationport', null); + $filterfieldsarray['proto'] = getGETPOSTsettingvalue('filterlogentries_protocol', null); + $filterfieldsarray['tcpflags'] = getGETPOSTsettingvalue('filterlogentries_protocolflags', null); + $filterlogentries_qty = getGETPOSTsettingvalue('filterlogentries_qty', null); +} + +$filter_logfile = "{$g['varlog_path']}/filter.log"; + +$nentries = $config['syslog']['nentries']; + +# Override Display Quantity +if ($filterlogentries_qty) { + $nentries = $filterlogentries_qty; +} + +if (!$nentries || !is_numeric($nentries)) { + $nentries = 50; +} + +if ($_POST['clear']) { + clear_log_file($filter_logfile); +} + +$pgtitle = array(gettext("Status"), gettext("System logs"), gettext("Firewall")); +$shortcut_section = "firewall"; +include("head.inc"); + +/* +🔹 = "Resolve" (Small blue diamond) +⏬ = "Easyrule add to block list" (Double down arrow) +⏫ = "Easyrule add to pass list" (Double up arrow) +► = "out" (simple right arrow) +*/ + +function build_if_list() { + $iflist = get_configured_interface_with_descr(false, true); + //$iflist = get_interface_list(); + // Allow extending of the firewall edit interfaces + pfSense_handle_custom_code("/usr/local/pkg/firewall_nat/pre_interfaces_edit"); + foreach ($iflist as $if => $ifdesc) + $interfaces[$if] = $ifdesc; + + if ($config['l2tp']['mode'] == "server") + $interfaces['l2tp'] = "L2TP VPN"; + + if ($config['pptpd']['mode'] == "server") + $interfaces['pptp'] = "PPTP VPN"; + + if (is_pppoe_server_enabled() && have_ruleint_access("pppoe")) + $interfaces['pppoe'] = "PPPoE Server"; + + /* add ipsec interfaces */ + if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enable'])) + $interfaces["enc0"] = "IPsec"; + + /* add openvpn/tun interfaces */ + if ($config['openvpn']["openvpn-server"] || $config['openvpn']["openvpn-client"]) + $interfaces["openvpn"] = "OpenVPN"; + + return($interfaces); +} + +$tab_array = array(); +$tab_array[] = array(gettext("System"), false, "diag_logs.php"); +$tab_array[] = array(gettext("Firewall"), true, "diag_logs_filter.php"); +$tab_array[] = array(gettext("DHCP"), false, "diag_logs.php?logfile=dhcpd"); +$tab_array[] = array(gettext("Portal Auth"), false, "diag_logs.php?logfile=portalauth"); +$tab_array[] = array(gettext("IPsec"), false, "diag_logs.php?logfile=ipsec"); +$tab_array[] = array(gettext("PPP"), false, "diag_logs.php?logfile=ppp"); +$tab_array[] = array(gettext("VPN"), false, "diag_logs_vpn.php"); +$tab_array[] = array(gettext("Load Balancer"), false, "diag_logs.php?logfile=relayd"); +$tab_array[] = array(gettext("OpenVPN"), false, "diag_logs.php?logfile=openvpn"); +$tab_array[] = array(gettext("NTP"), false, "diag_logs.php?logfile=ntpd"); +$tab_array[] = array(gettext("Settings"), false, "diag_logs_settings.php"); +display_top_tabs($tab_array); + +$tab_array = array(); +$tab_array[] = array(gettext("Normal View"), true, "/diag_logs_filter.php"); +$tab_array[] = array(gettext("Dynamic View"), false, "/diag_logs_filter_dynamic.php"); +$tab_array[] = array(gettext("Summary View"), false, "/diag_logs_filter_summary.php"); +display_top_tabs($tab_array, false, 'nav nav-tabs'); + +$Include_Act = explode(",", str_replace(" ", ",", $filterfieldsarray['act'])); +if ($filterfieldsarray['interface'] == "All") + $interface = ""; + +require('classes/Form.class.php'); + +if(!isset($config['syslog']['rawfilter'])) { // Advanced log filter form + $form = new Form(new Form_Button( + 'filterlogentries_submit', + 'Filter' + )); + + $section = new Form_Section('Advanced Log Filter'); + + $group = new Form_Group(''); + + $group->add(new Form_Input( + 'filterlogentries_sourceipaddress', + null, + 'text', + $filterfieldsarray['srcip'] + ))->setHelp('Source IP Address'); + + $group->add(new Form_Input( + 'filterlogentries_destinationipaddress', + null, + 'text', + $filterfieldsarray['dstip'] + ))->setHelp('Destination IP Address'); + + $section->add($group); + $group = new Form_Group(''); + + $group->add(new Form_Checkbox( + 'actpass', + 'Pass', + 'Pass', + in_arrayi('Pass', $Include_Act) + )); + + $group->add(new Form_Input( + 'filterlogentries_time', + null, + 'text', + $filterfieldsarray['time'] + ))->setHelp('Time'); + + $group->add(new Form_Input( + 'filterlogentries_sourceport', + null, + 'text', + $filterfieldsarray['srcport'] + ))->setHelp('Source Port'); + + $group->add(new Form_Input( + 'filterlogentries_protocol', + null, + 'text', + $filterfieldsarray['proto'] + ))->setHelp('Protocol'); + + $group->add(new Form_Input( + 'filterlogentries_qty', + null, + 'text', + $filterlogentries_qty + ))->setHelp('Quantity'); + + $section->add($group); + + $group = new Form_Group(''); + + $group->add(new Form_Checkbox( + 'actblock', + 'Block', + 'Block', + in_arrayi('Block', $Include_Act) + )); + + $group->add(new Form_Input( + 'filterlogentries_interfaces', + null, + 'text', + $filterfieldsarray['interface'] + ))->setHelp('Interface'); + + $group->add(new Form_Input( + 'filterlogentries_destinationport', + null, + 'text', + $filterfieldsarray['dstport'] + ))->setHelp('Destination Port'); + + $group->add(new Form_Input( + 'filterlogentries_protocolflags', + null, + 'text', + $filterfieldsarray['tcpflags'] + ))->setHelp('Protocol Flags'); +} +else { // Simple log filter form + $form = new Form(new Form_Button( + 'filtersubmit', + 'Filter' + )); + $section = new Form_Section('Log Filter'); + + $section->addInput(new Form_Select( + 'filterdescriptions', + 'Where to show rule descriptions', + $interfacefilter, + build_if_list() + )); + + $group = new Form_Group(''); + + $group->add(new Form_Input( + 'filtertext', + null, + 'text', + $filtertext + ))->setHelp('Filter Expression'); + + $group->add(new Form_Input( + 'filterlogentries_qty', + null, + 'text', + $filterlogentries_qty + ))->setHelp('Quantity'); +} + +$group->setHelp('<a target="_blank" href="http://www.php.net/manual/en/book.pcre.php">' . 'Regular expression reference</a> Precede with exclamation (!) to exclude match.'); +$section->add($group); +$form->add($section); +print($form); + +// Now the forms are complete we can draw the log table and its controls +if (!isset($config['syslog']['rawfilter'])) { + $iflist = get_configured_interface_with_descr(false, true); + + if ($iflist[$interfacefilter]) + $interfacefilter = $iflist[$interfacefilter]; + + if ($filterlogentries_submit) + $filterlog = conv_log_filter($filter_logfile, $nentries, $nentries + 100, $filterfieldsarray); + else + $filterlog = conv_log_filter($filter_logfile, $nentries, $nentries + 100, $filtertext, $interfacefilter); +?> + +<div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title"> +<?php + if ( (!$filtertext) && (!$filterfieldsarray) ) + printf(gettext("Last %s firewall log entries."),count($filterlog)); + else + print(count($filterlog). ' ' . gettext('matched log entries.') . ' '); + + printf(gettext(" (Maximum %s)"),$nentries) +?> + </h2> + </div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table striped table-hover table-compact"> + <tr> + <th><?=gettext("Act")?></th> + <th><?=gettext("Time")?></th> + <th><?=gettext("IF")?></th> +<?php + if ($config['syslog']['filterdescriptions'] === "1") { +?> + <th> + <?=gettext("Rule")?> + </th> +<?php + } +?> + <th><?=gettext("Source")?></th> + <th><?=gettext("Destination")?></th> + <th><?=gettext("Proto")?></th> + </tr> +<?php + if ($config['syslog']['filterdescriptions']) + buffer_rules_load(); + + foreach ($filterlog as $filterent) { +?> + <tr> + <td> + <a class="btn btn-xs btn-info" onclick="javascript:getURL('diag_logs_filter.php?getrulenum=<?="{$filterent['rulenum']},{$filterent['tracker']},{$filterent['act']}"; ?>', outputrule);"> + <?=gettext('Block')?></a> +<?php + if ($filterent['count']) + echo $filterent['count']; +?> + </td> + <td> + <?=htmlspecialchars($filterent['time'])?> + </td> + <td> +<?php + if ($filterent['direction'] == "out") + print('►' . ' '); +?> + <?=htmlspecialchars($filterent['interface'])?> + </td> +<?php + if ($config['syslog']['filterdescriptions'] === "1") { +?> + <td> + <?=find_rule_by_number_buffer($filterent['rulenum'], $filterent['tracker'], $filterent['act'])?> + </td> +<?php + } + + $int = strtolower($filterent['interface']); + $proto = strtolower($filterent['proto']); + + if($filterent['version'] == '6') { + $ipproto = "inet6"; + $filterent['srcip'] = "[{$filterent['srcip']}]"; + $filterent['dstip'] = "[{$filterent['dstip']}]"; + } else { + $ipproto = "inet"; + } + + $srcstr = $filterent['srcip'] . get_port_with_service($filterent['srcport'], $proto); + $src_htmlclass = str_replace(array('.', ':'), '-', $filterent['srcip']); + $dststr = $filterent['dstip'] . get_port_with_service($filterent['dstport'], $proto); + $dst_htmlclass = str_replace(array('.', ':'), '-', $filterent['dstip']); +?> + <td> + <a onclick="javascript:resolve_with_ajax('<?="{$filterent['srcip']}"; ?>');" title="<?=gettext("Click to resolve")?>" alt="Reverse Resolve with DNS"/> + 🔹</a> + <a href="easyrule.php?<?="action=block&int={$int}&src={$filterent['srcip']}&ipproto={$ipproto}"; ?>" alt="Easy Rule: Add to Block List" title="<?=gettext("Easy Rule: Add to Block List")?>" onclick="return confirm('<?=gettext("Do you really want to add this BLOCK rule?")?>')"> + ⏬</a> + <?=$srcstr . '<span class="RESOLVE-' . $src_htmlclass . '"></span>'?> + </td> + <td> + <a onclick="javascript:resolve_with_ajax('<?="{$filterent['dstip']}"; ?>');" title="<?=gettext("Click to resolve")?>" class="ICON-<?= $dst_htmlclass; ?>" alt="Reverse Resolve with DNS"/> + 🔹</a> + <a href="easyrule.php?<?="action=pass&int={$int}&proto={$proto}&src={$filterent['srcip']}&dst={$filterent['dstip']}&dstport={$filterent['dstport']}&ipproto={$ipproto}"; ?>" title="<?=gettext("Easy Rule: Pass this traffic")?>" onclick="return confirm('<?=gettext("Do you really want to add this PASS rule?")?>')"> + ⏫</a> + <?=$dststr . '<span class="RESOLVE-' . $dst_htmlclass . '"></span>'?> + </td> +<?php + if ($filterent['proto'] == "TCP") + $filterent['proto'] .= ":{$filterent['tcpflags']}"; + ?> + <td><?=htmlspecialchars($filterent['proto'])?></td> + </tr> +<?php + if (isset($config['syslog']['filterdescriptions']) && $config['syslog']['filterdescriptions'] === "2") { +?> + <tr> + <td colspan="2" /> + <td colspan="4"><?=find_rule_by_number_buffer($filterent['rulenum'],$filterent['tracker'],$filterent['act'])?></td> + </tr> +<?php + } + } // e-o-foreach + buffer_rules_clear(); +} +else + { +?> + <tr> + <td colspan="2"> + <?php printf(gettext("Last %s firewall log entries"),$nentries)?> + </td> + </tr> +<?php + if($filtertext) + dump_clog($filter_logfile, $nentries, true, array("$filtertext")); + else + dump_clog($filter_logfile, $nentries); +} +?> + </table> + </div> + </div> +</div> + +<p> + <form id="clearform" name="clearform" action="diag_logs_filter.php" method="post" style="margin-top: 14px;"> + <input id="submit" name="clear" type="submit" class="btn btn-danger" value="<?=gettext("Clear log")?>" /> + </form> +</p> + +<?php + +print_info_box('<a href="https://doc.pfsense.org/index.php/What_are_TCP_Flags%3F">' . + gettext("TCP Flags") . '</a>: F - FIN, S - SYN, A or . - ACK, R - RST, P - PSH, U - URG, E - ECE, C - CWR'); + +?> + +<!-- AJAXY STUFF --> +<script type="text/javascript"> +//<![CDATA[ +function resolve_with_ajax(ip_to_resolve) { + var url = "/diag_logs_filter.php"; + + jQuery.ajax( + url, + { + type: 'post', + dataType: 'json', + data: { + resolve: ip_to_resolve, + }, + complete: resolve_ip_callback + }); + +} + +function resolve_ip_callback(transport) { + var response = jQuery.parseJSON(transport.responseText); + var resolve_class = htmlspecialchars(response.resolve_ip.replace(/[.:]/g, '-')); + var resolve_text = '<small><br />' + htmlspecialchars(response.resolve_text) + '<\/small>'; + + jQuery('span.RESOLVE-' + resolve_class).html(resolve_text); + jQuery('img.ICON-' + resolve_class).removeAttr('title'); + jQuery('img.ICON-' + resolve_class).removeAttr('alt'); + jQuery('img.ICON-' + resolve_class).attr('src', '/themes/<?= $g['theme']; ?>/images/icons/icon_log_d.gif'); + jQuery('img.ICON-' + resolve_class).prop('onclick', null); + // jQuery cautions that "removeAttr('onclick')" fails in some versions of IE +} + +// From http://stackoverflow.com/questions/5499078/fastest-method-to-escape-html-tags-as-html-entities +function htmlspecialchars(str) { + return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, '''); +} +//]]> +</script> + +<?php include("foot.inc"); diff --git a/src/usr/local/www/diag_logs_filter_dynamic.php b/src/usr/local/www/diag_logs_filter_dynamic.php new file mode 100755 index 0000000..ca32b46 --- /dev/null +++ b/src/usr/local/www/diag_logs_filter_dynamic.php @@ -0,0 +1,243 @@ +<?php +/* $Id$ */ +/* + diag_logs_filter_dynamic.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-hidden-nolongerincluded +##|*NAME=Hidden: No longer included page +##|*DESCR=Allow access to the 'Hidden: No longer included' page. +##|*MATCH=diag_logs_filter_dynamic.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("filter_log.inc"); + +$filter_logfile = "{$g['varlog_path']}/filter.log"; + +/* Hardcode this. AJAX doesn't do so well with large numbers */ +$nentries = 50; + +/* AJAX related routines */ +handle_ajax($nentries, $nentries + 20); + +if ($_POST['clear']) { + clear_log_file($filter_logfile); +} + +$filterlog = conv_log_filter($filter_logfile, $nentries, $nentries + 100); + +$pgtitle = array(gettext("Status"), gettext("System logs"), gettext("Firewall (Dynamic View)")); +$shortcut_section = "firewall"; +include("head.inc"); + +?> + +<script type="text/javascript"> +//<![CDATA[ + lastsawtime = '<?=time(); ?>;'; + var lines = Array(); + var timer; + var updateDelay = 25500; + var isBusy = false; + var isPaused = false; + var nentries = <?=$nentries; ?>; +<?php + if (isset($config['syslog']['reverse'])) { + echo "var isReverse = true;\n"; + } else { + echo "var isReverse = false;\n"; + } +?> + /* Called by the AJAX updater */ + function format_log_line(row) { + if (row[8] == '6') { + srcIP = '[' + row[3] + ']'; + dstIP = '[' + row[5] + ']'; + } else { + srcIP = row[3]; + dstIP = row[5]; + } + + if (row[4] == '') { + srcPort = ''; + } else { + srcPort = ':' + row[4]; + } + if (row[6] == '') { + dstPort = ''; + } else { + dstPort = ':' + row[6]; + } + + var line = + '<td>' + row[1] + '</td>' + + '<td>' + row[2] + '</td>' + + '<td>' + srcIP + srcPort + '</td>' + + '<td>' + dstIP + dstPort + '</td>' + + '<td>' + row[7] + '</td>' + + '<td>' + row[0] + '</td>'; + + return line; + } +//]]> +</script> +<script src="/javascript/filter_log.js" type="text/javascript"></script> + +<?php +$tab_array = array(); +$tab_array[] = array(gettext("System"), false, "diag_logs.php"); +$tab_array[] = array(gettext("Firewall"), true, "diag_logs_filter.php"); +$tab_array[] = array(gettext("DHCP"), false, "diag_logs.php?logfile=dhcpd"); +$tab_array[] = array(gettext("Portal Auth"), false, "diag_logs.php?logfile=portalauth"); +$tab_array[] = array(gettext("IPsec"), false, "diag_logs.php?logfile=ipsec"); +$tab_array[] = array(gettext("PPP"), false, "diag_logs.php?logfile=ppp"); +$tab_array[] = array(gettext("VPN"), false, "diag_logs_vpn.php"); +$tab_array[] = array(gettext("Load Balancer"), false, "diag_logs.php?logfile=relayd"); +$tab_array[] = array(gettext("OpenVPN"), false, "diag_logs.php?logfile=openvpn"); +$tab_array[] = array(gettext("NTP"), false, "diag_logs.php?logfile=ntpd"); +$tab_array[] = array(gettext("Settings"), false, "diag_logs_settings.php"); +display_top_tabs($tab_array); + +$tab_array = array(); +$tab_array[] = array(gettext("Normal View"), false, "/diag_logs_filter.php"); +$tab_array[] = array(gettext("Dynamic View"), true, "/diag_logs_filter_dynamic.php"); +$tab_array[] = array(gettext("Summary View"), false, "/diag_logs_filter_summary.php"); +display_top_tabs($tab_array, false, 'nav nav-tabs'); +?> + +<div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title"> + <?=gettext('Last ') . $nentries . gettext(' records. ') . gettext('Pause ')?><input type="checkbox" onclick="javascript:toggle_pause();" /> + </h2> + </div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Time")?></th> + <th><?=gettext("IF")?></th> + <th><?=gettext("Source")?></th> + <th><?=gettext("Destination")?></th> + <th><?=gettext("Proto")?></th> + <th></th> <!-- For the "Block" buttons--> + </tr> + </thead> + <tbody id="filter-log-entries"> +<?php + $rowIndex = 0; + $tcpcnt = 0; + + foreach ($filterlog as $filterent) { + $evenRowClass = $rowIndex % 2 ? " listMReven" : " listMRodd"; + $rowIndex++; + if ($filterent['version'] == '6') { + $srcIP = "[" . htmlspecialchars($filterent['srcip']) . "]"; + $dstIP = "[" . htmlspecialchars($filterent['dstip']) . "]"; + } else { + $srcIP = htmlspecialchars($filterent['srcip']); + $dstIP = htmlspecialchars($filterent['dstip']); + } + + if ($filterent['srcport']) + $srcPort = ":" . htmlspecialchars($filterent['srcport']); + else + $srcPort = ""; + + if ($filterent['dstport']) + $dstPort = ":" . htmlspecialchars($filterent['dstport']); + else + $dstPort = ""; +?> + <tr> + <td><?=htmlspecialchars($filterent['time'])?></td> + <td><?=htmlspecialchars($filterent['interface'])?></td> + <td><?=$srcIP . $srcPort?></td> + <td><?=$dstIP . $dstPort?></td> +<?php + if ($filterent['proto'] == "TCP") { + $filterent['proto'] .= ":{$filterent['tcpflags']}"; + $tcpcnt++; + } +?> + <td><?=htmlspecialchars($filterent['proto'])?></td> + <td> + <a href="#" class="btn btn-danger btn-xs" alt="<?=$filterent['act'];?>" title="<?=$filterent['act'];?> onclick="javascript:getURL('diag_logs_filter.php?getrulenum=<?="{$filterent['rulenum']},{$filterent['act']}"; ?>', outputrule);"> + <?=gettext('Block')?> + </a> + </td> + </tr> +<?php + } // e-o-foreach() +?> + </tbody> + </table> + </div> + </div> +</div> +<?php + +if($tcpcnt > 0) + print_info_box('<a href="https://doc.pfsense.org/index.php/What_are_TCP_Flags%3F">' . + gettext("TCP Flags") . '</a>: F - FIN, S - SYN, A or . - ACK, R - RST, P - PSH, U - URG, E - ECE, C - CWR'); +?> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_logs_filter_summary.php b/src/usr/local/www/diag_logs_filter_summary.php new file mode 100644 index 0000000..8d88310 --- /dev/null +++ b/src/usr/local/www/diag_logs_filter_summary.php @@ -0,0 +1,318 @@ +<?php +/* + diag_logs_filter_summary.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2009 Jim Pingle (jpingle@gmail.com) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: + pfSense_MODULE: filter +*/ + +$pgtitle = gettext("Status").": ".gettext("System logs").": ".gettext("Firewall Log Summary"); +$shortcut_section = "firewall"; +require_once("guiconfig.inc"); +include_once("filter_log.inc"); + +$filter_logfile = "{$g['varlog_path']}/filter.log"; +$lines = 5000; +$entriesperblock = 5; + +$filterlog = conv_log_filter($filter_logfile, $lines, $lines); +$gotlines = count($filterlog); +$fields = array( + 'act' => gettext("Actions"), + 'interface' => gettext("Interfaces"), + 'proto' => gettext("Protocols"), + 'srcip' => gettext("Source IPs"), + 'dstip' => gettext("Destination IPs"), + 'srcport' => gettext("Source Ports"), + 'dstport' => gettext("Destination Ports")); + +$segcolors = array("#2484c1", "#65a620", "#7b6888", "#a05d56", "#961a1a", "#d8d23a", "#e98125", "#d0743c", "#635222", "#6ada6a"); +$numcolors = 10; + +$summary = array(); +foreach (array_keys($fields) as $f) { + $summary[$f] = array(); +} + +$totals = array(); + +function cmp($a, $b) { + if ($a == $b) { + return 0; + } + return ($a < $b) ? 1 : -1; +} + +function stat_block($summary, $stat, $num) { + global $g, $gotlines, $fields; + uasort($summary[$stat] , 'cmp'); + print('<div class="table-responsive">'); + print('<table class="table table-striped table-hover table-condensed">'); + print('<tr><th>' . $fields[$stat] . '</th>' . '<th>' . gettext("Data points") . '</th><th></th></tr>'); + $k = array_keys($summary[$stat]); + $total = 0; + $numentries = 0; + for ($i = 0; $i < $num; $i++) { + if ($k[$i]) { + $total += $summary[$stat][$k[$i]]; + $numentries++; + $outstr = $k[$i]; + if (is_ipaddr($outstr)) { + print('<tr><td>' . $outstr . '</td>' . '<td>' . $summary[$stat][$k[$i]] . '</td><td><a href="diag_dns.php?host=' . $outstr . '" class="btn btn-xs btn-success" title="' . gettext("Reverse Resolve with DNS") . '">Lookup</a></td></tr>'); + + } elseif (substr_count($outstr, '/') == 1) { + list($proto, $port) = explode('/', $outstr); + $service = getservbyport($port, strtolower($proto)); + if ($service) { + $outstr .= ": {$service}"; + } + } + + if(!is_ipaddr($outstr)) + print('<tr><td>' . $outstr . '</td><td>' . $summary[$stat][$k[$i]] . '</td><td></td></tr>'); + } + } + $leftover = $gotlines - $total; + if ($leftover > 0) { + print "<tr><td>Other</td><td>{$leftover}</td><td></td>"; + } + print "</table>"; + print('</div>'); +} + +// Create the JSON document for the chart to be displayed +// Todo: Be good to investigate building this with json_encode and friends some time +function pie_block($summary, $stat, $num, $chartnum) { + global $fields, $segcolors, $gotlines, $numcolors; +?> +<script> +var pie = new d3pie("pieChart<?=$chartnum?>", { + "header": { + "title": { + "text": "", + "fontSize": 22, + "font": "verdana" + }, + "subtitle": { + "color": "#999999", + "fontSize": 12, + "font": "open sans" + }, + "titleSubtitlePadding": 12 + }, + "footer": { + "color": "#999999", + "fontSize": 10, + "font": "open sans", + "location": "bottom-left" + }, + "size": { + "canvasHeight": 400, + "canvasWidth": 590, + "pieOuterRadius": "88%" + }, + "data": { + "sortOrder": "value-desc", + "content": [ +<?php + uasort($summary[$stat] , 'cmp'); + $k = array_keys($summary[$stat]); + $total = 0; + $numentries = 0; + + for ($i=0; $i < $num; $i++) { + if ($k[$i]) { + $total += $summary[$stat][$k[$i]]; + $numentries++; + if($i > 0) + print(",\r\n"); + + print("{"); + print('"label": "' . $k[$i] . '", "value": '); + print($summary[$stat][$k[$i]]); + print(', "color": "' . $segcolors[$i % $numcolors] . '"'); + print("}"); + + } + } + + $leftover = $gotlines - $total; + + if ($leftover > 0) { + print(",\r\n"); + print("{"); + print('"label": "Other", "value": '); + print($leftover); + print(', "color": "' . $segcolors[$i % $numcolors] . '"'); + print("}"); + } +?> + ] + }, + "labels": { + "outer": { + "pieDistance": 32 + }, + "inner": { + "hideWhenLessThanPercentage": 3 + }, + "mainLabel": { + "fontSize": 11 + }, + "percentage": { + "color": "#ffffff", + "decimalPlaces": 0 + }, + "value": { + "color": "#adadad", + "fontSize": 11 + }, + "lines": { + "enabled": true + }, + "truncation": { + "enabled": true + } + }, + "effects": { + "pullOutSegmentOnClick": { + "effect": "linear", + "speed": 400, + "size": 8 + } + }, + "misc": { + "gradient": { + "enabled": true, + "percentage": 100 + } + }, + "callbacks": {} +}); +</script> +<?php +} + +foreach ($filterlog as $fe) { + $specialfields = array('srcport', 'dstport'); + foreach (array_keys($fields) as $field) { + if (!in_array($field, $specialfields)) { + $summary[$field][$fe[$field]]++; + } + } + /* Handle some special cases */ + if ($fe['srcport']) { + $summary['srcport'][$fe['proto'].'/'.$fe['srcport']]++; + } else { + $summary['srcport'][$fe['srcport']]++; + } + if ($fe['dstport']) { + $summary['dstport'][$fe['proto'].'/'.$fe['dstport']]++; + } else { + $summary['dstport'][$fe['dstport']]++; + } +} + +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("System"), false, "diag_logs.php"); +$tab_array[] = array(gettext("Firewall"), true, "diag_logs_filter.php"); +$tab_array[] = array(gettext("DHCP"), false, "diag_logs.php?logfile=dhcpd"); +$tab_array[] = array(gettext("Portal Auth"), false, "diag_logs.php?logfile=portalauth"); +$tab_array[] = array(gettext("IPsec"), false, "diag_logs.php?logfile=ipsec"); +$tab_array[] = array(gettext("PPP"), false, "diag_logs.php?logfile=ppp"); +$tab_array[] = array(gettext("VPN"), false, "diag_logs_vpn.php"); +$tab_array[] = array(gettext("Load Balancer"), false, "diag_logs.php?logfile=relayd"); +$tab_array[] = array(gettext("OpenVPN"), false, "diag_logs.php?logfile=openvpn"); +$tab_array[] = array(gettext("NTP"), false, "diag_logs.php?logfile=ntpd"); +$tab_array[] = array(gettext("Settings"), false, "diag_logs_settings.php"); +display_top_tabs($tab_array); + +$tab_array = array(); +$tab_array[] = array(gettext("Normal View"), false, "/diag_logs_filter.php"); +$tab_array[] = array(gettext("Dynamic View"), false, "/diag_logs_filter_dynamic.php"); +$tab_array[] = array(gettext("Summary View"), true, "/diag_logs_filter_summary.php"); +display_top_tabs($tab_array, false, 'nav nav-tabs'); + +$infomsg = sprintf('This is a firewall log summary, of the last %1$s lines of the firewall log (Max %2$s).', $gotlines, $lines); +print_info_box($infomsg); +?> + +<script src="d3pie/d3pie.min.js"></script> +<script src="d3pie/d3.min.js"></script> + +<?php + +$chartnum=0; +foreach(array_keys($fields) as $field) { +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=$fields[$field]?></h2></div> + <div class="panel-body"> + <div id="pieChart<?=$chartnum?>" align="center"> +<?php + pie_block($summary, $field , $entriesperblock, $chartnum); + stat_block($summary, $field , $entriesperblock); + $chartnum++; +?> + </div> + </div> +</div> +<?php +} + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_logs_settings.php b/src/usr/local/www/diag_logs_settings.php new file mode 100644 index 0000000..b195714 --- /dev/null +++ b/src/usr/local/www/diag_logs_settings.php @@ -0,0 +1,463 @@ +<?php +/* $Id$ */ +/* + diag_logs_settings.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004-9 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-logs-settings +##|*NAME=Diagnostics: Logs: Settings page +##|*DESCR=Allow access to the 'Diagnostics: Logs: Settings' page. +##|*MATCH=diag_logs_settings.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +$pconfig['reverse'] = isset($config['syslog']['reverse']); +$pconfig['nentries'] = $config['syslog']['nentries']; +$pconfig['remoteserver'] = $config['syslog']['remoteserver']; +$pconfig['remoteserver2'] = $config['syslog']['remoteserver2']; +$pconfig['remoteserver3'] = $config['syslog']['remoteserver3']; +$pconfig['sourceip'] = $config['syslog']['sourceip']; +$pconfig['ipproto'] = $config['syslog']['ipproto']; +$pconfig['filter'] = isset($config['syslog']['filter']); +$pconfig['dhcp'] = isset($config['syslog']['dhcp']); +$pconfig['portalauth'] = isset($config['syslog']['portalauth']); +$pconfig['vpn'] = isset($config['syslog']['vpn']); +$pconfig['apinger'] = isset($config['syslog']['apinger']); +$pconfig['relayd'] = isset($config['syslog']['relayd']); +$pconfig['hostapd'] = isset($config['syslog']['hostapd']); +$pconfig['logall'] = isset($config['syslog']['logall']); +$pconfig['system'] = isset($config['syslog']['system']); +$pconfig['enable'] = isset($config['syslog']['enable']); +$pconfig['logdefaultblock'] = !isset($config['syslog']['nologdefaultblock']); +$pconfig['logdefaultpass'] = isset($config['syslog']['nologdefaultpass']); +$pconfig['logbogons'] = !isset($config['syslog']['nologbogons']); +$pconfig['logprivatenets'] = !isset($config['syslog']['nologprivatenets']); +$pconfig['loglighttpd'] = !isset($config['syslog']['nologlighttpd']); +$pconfig['rawfilter'] = isset($config['syslog']['rawfilter']); +$pconfig['filterdescriptions'] = $config['syslog']['filterdescriptions']; +$pconfig['disablelocallogging'] = isset($config['syslog']['disablelocallogging']); +$pconfig['logfilesize'] = $config['syslog']['logfilesize']; + +if (!$pconfig['nentries']) { + $pconfig['nentries'] = 50; +} + +function is_valid_syslog_server($target) { + return (is_ipaddr($target) + || is_ipaddrwithport($target) + || is_hostname($target) + || is_hostnamewithport($target)); +} + +if ($_POST['resetlogs'] == gettext("Reset Log Files")) { + clear_all_log_files(); + $savemsg .= gettext("The log files have been reset."); +} elseif ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if ($_POST['enable'] && !is_valid_syslog_server($_POST['remoteserver'])) { + $input_errors[] = gettext("A valid IP address/hostname or IP/hostname:port must be specified for remote syslog server #1."); + } + if ($_POST['enable'] && $_POST['remoteserver2'] && !is_valid_syslog_server($_POST['remoteserver2'])) { + $input_errors[] = gettext("A valid IP address/hostname or IP/hostname:port must be specified for remote syslog server #2."); + } + if ($_POST['enable'] && $_POST['remoteserver3'] && !is_valid_syslog_server($_POST['remoteserver3'])) { + $input_errors[] = gettext("A valid IP address/hostname or IP/hostname:port must be specified for remote syslog server #3."); + } + + if (($_POST['nentries'] < 5) || ($_POST['nentries'] > 2000)) { + $input_errors[] = gettext("Number of log entries to show must be between 5 and 2000."); + } + + if (isset($_POST['logfilesize']) && (strlen($_POST['logfilesize']) > 0)) { + if (!is_numeric($_POST['logfilesize']) || ($_POST['logfilesize'] < 100000)) { + $input_errors[] = gettext("Log file size must be numeric and greater than or equal to 100000."); + } + } + if (!$input_errors) { + $config['syslog']['reverse'] = $_POST['reverse'] ? true : false; + $config['syslog']['nentries'] = (int)$_POST['nentries']; + $pconfig['nentries'] = $config['syslog']['nentries']; + if (isset($_POST['logfilesize']) && (strlen($_POST['logfilesize']) > 0)) { + $config['syslog']['logfilesize'] = (int)$_POST['logfilesize']; + $pconfig['logfilesize'] = $config['syslog']['logfilesize']; + } else { + unset($config['syslog']['logfilesize']); + } + $config['syslog']['remoteserver'] = $_POST['remoteserver']; + $config['syslog']['remoteserver2'] = $_POST['remoteserver2']; + $config['syslog']['remoteserver3'] = $_POST['remoteserver3']; + $config['syslog']['sourceip'] = $_POST['sourceip']; + $config['syslog']['ipproto'] = $_POST['ipproto']; + $config['syslog']['filter'] = $_POST['filter'] ? true : false; + $config['syslog']['dhcp'] = $_POST['dhcp'] ? true : false; + $config['syslog']['portalauth'] = $_POST['portalauth'] ? true : false; + $config['syslog']['vpn'] = $_POST['vpn'] ? true : false; + $config['syslog']['apinger'] = $_POST['apinger'] ? true : false; + $config['syslog']['relayd'] = $_POST['relayd'] ? true : false; + $config['syslog']['hostapd'] = $_POST['hostapd'] ? true : false; + $config['syslog']['logall'] = $_POST['logall'] ? true : false; + $config['syslog']['system'] = $_POST['system'] ? true : false; + $config['syslog']['disablelocallogging'] = $_POST['disablelocallogging'] ? true : false; + $config['syslog']['enable'] = $_POST['enable'] ? true : false; + $oldnologdefaultblock = isset($config['syslog']['nologdefaultblock']); + $oldnologdefaultpass = isset($config['syslog']['nologdefaultpass']); + $oldnologbogons = isset($config['syslog']['nologbogons']); + $oldnologprivatenets = isset($config['syslog']['nologprivatenets']); + $oldnologlighttpd = isset($config['syslog']['nologlighttpd']); + $config['syslog']['nologdefaultblock'] = $_POST['logdefaultblock'] ? false : true; + $config['syslog']['nologdefaultpass'] = $_POST['logdefaultpass'] ? true : false; + $config['syslog']['nologbogons'] = $_POST['logbogons'] ? false : true; + $config['syslog']['nologprivatenets'] = $_POST['logprivatenets'] ? false : true; + $config['syslog']['nologlighttpd'] = $_POST['loglighttpd'] ? false : true; + $config['syslog']['rawfilter'] = $_POST['rawfilter'] ? true : false; + if (is_numeric($_POST['filterdescriptions']) && $_POST['filterdescriptions'] > 0) { + $config['syslog']['filterdescriptions'] = $_POST['filterdescriptions']; + } else { + unset($config['syslog']['filterdescriptions']); + } + if ($config['syslog']['enable'] == false) { + unset($config['syslog']['remoteserver']); + unset($config['syslog']['remoteserver2']); + unset($config['syslog']['remoteserver3']); + } + + write_config(); + + $retval = 0; + $retval = system_syslogd_start(); + if (($oldnologdefaultblock !== isset($config['syslog']['nologdefaultblock'])) || + ($oldnologdefaultpass !== isset($config['syslog']['nologdefaultpass'])) || + ($oldnologbogons !== isset($config['syslog']['nologbogons'])) || + ($oldnologprivatenets !== isset($config['syslog']['nologprivatenets']))) { + $retval |= filter_configure(); + } + + $savemsg = get_std_save_message($retval); + + if ($oldnologlighttpd !== isset($config['syslog']['nologlighttpd'])) { + ob_flush(); + flush(); + log_error(gettext("webConfigurator configuration has changed. Restarting webConfigurator.")); + send_event("service restart webgui"); + $savemsg .= "<br />" . gettext("WebGUI process is restarting."); + } + + filter_pflog_start(true); + } +} + +$pgtitle = array(gettext("Status"), gettext("System logs"), gettext("Settings")); +include("head.inc"); + +$logfilesizeHelp = gettext("Logs are held in constant-size circular log files. This field controls how large each log file is, and thus how many entries may exist inside the log. By default this is approximately 500KB per log file, and there are nearly 20 such log files.") . + '<br /><br />' . + gettext("NOTE: Log sizes are changed the next time a log file is cleared or deleted. To immediately increase the size of the log files, you must first save the options to set the size, then clear all logs using the \"Reset Log Files\" option farther down this page. ") . + gettext("Be aware that increasing this value increases every log file size, so disk usage will increase significantly.") . '<br /><br />' . + gettext("Disk space currently used by log files is: ") . exec("/usr/bin/du -sh /var/log | /usr/bin/awk '{print $1;}'") . + gettext(" Remaining disk space for log files: ") . exec("/bin/df -h /var/log | /usr/bin/awk '{print $4;}'"); + +$remoteloghelp = gettext("This option will allow the logging daemon to bind to a single IP address, rather than all IP addresses.") . + gettext("If you pick a single IP, remote syslog severs must all be of that IP type. If you wish to mix IPv4 and IPv6 remote syslog servers, you must bind to all interfaces.") . + "<br /><br />" . + gettext("NOTE: If an IP address cannot be located on the chosen interface, the daemon will bind to all addresses."); +if ($input_errors) + print_input_errors($input_errors); +else if ($savemsg) + print_info_box($savemsg); + +$tab_array = array(); +$tab_array[] = array(gettext("System"), false, "diag_logs.php"); +$tab_array[] = array(gettext("Firewall"), false, "diag_logs_filter.php"); +$tab_array[] = array(gettext("DHCP"), false, "diag_logs.php?logfile=dhcpd"); +$tab_array[] = array(gettext("Portal Auth"), false, "diag_logs.php?logfile=portalauth"); +$tab_array[] = array(gettext("IPsec"), false, "diag_logs.php?logfile=ipsec"); +$tab_array[] = array(gettext("PPP"), false, "diag_logs.php?logfile=ppp"); +$tab_array[] = array(gettext("VPN"), false, "diag_logs_vpn.php"); +$tab_array[] = array(gettext("Load Balancer"), false, "diag_logs.php?logfile=relayd"); +$tab_array[] = array(gettext("OpenVPN"), false, "diag_logs.php?logfile=openvpn"); +$tab_array[] = array(gettext("NTP"), false, "diag_logs.php?logfile=ntpd"); +$tab_array[] = array(gettext("Settings"), true, "diag_logs_settings.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form(new Form_Button( + 'Submit', + gettext("Save") +)); + +$section = new Form_Section('General Logging Options'); + +$section->addInput(new Form_Checkbox( + 'reverse', + 'Forward/Reverse Display', + 'Show log entries in reverse order (newest entries on top)', + $pconfig['reverse'] +)); + +$section->addInput(new Form_Input( + 'nentries', + 'GUI Log Entries', + 'text', + $pconfig['nentries'], + ['placeholder' => ''] +))->setHelp('This is only the number of log entries displayed in the GUI. It does not affect how many entries are contained in the actual log files.'); + +$section->addInput(new Form_Input( + 'logfilesize', + 'Log file size (Bytes)', + 'text', + $pconfig['logfilesize'], + ['placeholder' => 'Bytes'] +))->setHelp($logfilesizeHelp); + +$section->addInput(new Form_Checkbox( + 'logdefaultblock', + 'Log firewall default blocks', + 'Log packets matched from the default block rules in the ruleset', + $pconfig['logdefaultblock'] +))->setHelp('Packets that are blocked by the implicit default block rule will not be logged if you uncheck this option. Per-rule logging options are still respected.'); + +$section->addInput(new Form_Checkbox( + 'logdefaultpass', + null, + 'Log packets matched from the default pass rules put in the ruleset', + $pconfig['logdefaultpass'] +))->setHelp('Packets that are allowed by the implicit default pass rule will be logged if you check this option. Per-rule logging options are still respected. '); + +$section->addInput(new Form_Checkbox( + 'logbogons', + null, + 'Log packets blocked by \'Block Bogon Networks\' rules', + $pconfig['logbogons'] +)); + +$section->addInput(new Form_Checkbox( + 'logprivatenets', + null, + 'Log packets blocked by \'Block Private Networks\' rules', + $pconfig['logprivatenets'] +)); + +$section->addInput(new Form_Checkbox( + 'loglighttpd', + 'Web Server Log', + 'Log errors from the web server process', + $pconfig['loglighttpd'] +))->setHelp('If this is checked, errors from the lighttpd web server process for the GUI or Captive Portal will appear in the main system log'); + +$section->addInput(new Form_Checkbox( + 'rawfilter', + 'Raw Logs', + 'Show raw filter logs)', + $pconfig['rawfilter'] +))->setHelp(gettext('If this is checked, filter logs are shown as generated by the packet filter, without any formatting. This will reveal more detailed information, but it is more difficult to read')); + +$section->addInput(new Form_Select( + 'filterdescriptions', + 'Where to show rule descriptions', + !isset($pconfig['filterdescriptions']) ? '0':$pconfig['filterdescriptions'], + array( + '0' => 'Dont load descriptions', + '1' => 'Display as column', + '2' => 'Display as second row' + ) +))->setHelp('Show the applied rule description below or in the firewall log rows' . '<br />' . + 'Displaying rule descriptions for all lines in the log might affect performance with large rule sets'); + +$section->addInput(new Form_Checkbox( + 'disablelocallogging', + 'Local Logging', + $g['platform'] == 'pfSense' ? "Disable writing log files to the local disk" : "Disable writing log files to the local RAM disk", + $pconfig['disablelocallogging'] +)); + +$section->addInput(new Form_Button( + 'resetlogs', + 'Reset Log Files' +))->addClass('btn-danger btn-xs')->setHelp('Clears all local log files and reinitializes them as empty logs. This also restarts the DHCP daemon. Use the Save button first if you have made any setting changes.'); + +$form->add($section); +$section = new Form_Section('Remote Logging Options'); +$section->addClass('toggle-remote'); + +$section->addInput(new Form_Checkbox( + 'enable', + 'Enable Remote Logging', + 'Send log messages to remote syslog server', + $pconfig['enable'] +))->toggles('.toggle-remote .panel-body .form-group:not(:first-child)'); + +$section->addInput(new Form_Select( + 'sourceip', + 'Source Address', + link_interface_to_bridge($pconfig['sourceip']) ? null : $pconfig['sourceip'], + get_possible_traffic_source_addresses(false) +))->setHelp($remoteloghelp); + +$section->addInput(new Form_Select( + 'ipproto', + 'IP Protocol', + $ipproto, + array('ipv4' => 'IPv4', 'ipv6' => 'IPv6') +))->setHelp('This option is only used when a non-default address is chosen as the source above. This option only expresses a preference; If an IP address of the selected type is not found on the chosen interface, the other type will be tried.'); + +// Group colapses/appears based on 'enable' checkbox above +$group = new Form_Group('Remote log servers'); + +$group->add(new Form_Input( + 'remoteserver', + 'Server 1', + 'text', + $pconfig['remoteserver'], + ['placeholder' => 'IP[:port]'] +)); + +$group->add(new Form_Input( + 'remoteserver2', + 'Server 2', + 'text', + $pconfig['remoteserver2'], + ['placeholder' => 'IP[:port]'] +)); + +$group->add(new Form_Input( + 'remoteserver3', + 'Server 3', + 'text', + $pconfig['remoteserver3'], + ['placeholder' => 'IP[:port]'] +)); + +$section->add($group); + +$group = new Form_MultiCheckboxGroup('Remote Syslog Contents'); +$group->add(new Form_MultiCheckbox( + 'system', + null, + 'System Events', + $pconfig['system'] +)); + +$group->add(new Form_MultiCheckbox( + 'filter', + null, + 'Firewall Events', + $pconfig['filter'] +)); + +$group->add(new Form_MultiCheckbox( + 'dhcp', + null, + 'DHCP service events', + $pconfig['dhcp'] +)); + +$group->add(new Form_MultiCheckbox( + 'portalauth', + null, + 'Portal Auth events', + $pconfig['portalauth'] +)); + +$group->add(new Form_MultiCheckbox( + 'vpn', + null, + 'VPN (PPTP, IPsec, OpenVPN) events', + $pconfig['vpn'] +)); + +$group->add(new Form_MultiCheckbox( + 'apinger', + null, + 'Gateway Monitor events', + $pconfig['apinger'] +)); + +$group->add(new Form_MultiCheckbox( + 'relayd', + null, + 'Server Load Balancer events', + $pconfig['relayd'] +)); + +$group->add(new Form_MultiCheckbox( + 'hostapd', + null, + 'Wireless events', + $pconfig['hostapd'] +)); + +$group->setHelp('syslog sends UDP datagrams to port 514 on the specified remote '. + 'syslog server, unless another port is specified. Be sure to set syslogd on '. + 'the remote server to accept syslog messages from pfSense.'); + +$section->add($group); + +$form->add($section); +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_logs_vpn.php b/src/usr/local/www/diag_logs_vpn.php new file mode 100755 index 0000000..e5ce76b --- /dev/null +++ b/src/usr/local/www/diag_logs_vpn.php @@ -0,0 +1,242 @@ +#!/usr/local/bin/php +<?php +/* + diag_logs_vpn.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2003-2006 Manuel Kasper <mk@neon1.net> + * part of m0n0wall (http://m0n0.ch/wall) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /usr/sbin/fifolog_reader /usr/local/sbin/clog + pfSense_MODULE: vpn +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-logs-pptpvpn +##|*NAME=Diagnostics: Logs: VPN page +##|*DESCR=Allow access to the 'Diagnostics: Logs: VPN' page. +##|*MATCH=diag_logs_vpn.php* +##|-PRIV + + +$vpns = array('pptp' => 'PPTP', 'poes' => 'PPPoE', 'l2tp' => 'L2TP'); + +$pgtitle = array(gettext("Status"), gettext("System logs"), gettext("VPN")); +require("guiconfig.inc"); +require_once("vpn.inc"); + +$nentries = $config['syslog']['nentries']; +if (!$nentries) { + $nentries = 50; +} + +if (htmlspecialchars($_POST['vpntype'])) { + $vpntype = htmlspecialchars($_POST['vpntype']); +} elseif (htmlspecialchars($_GET['vpntype'])) { + $vpntype = htmlspecialchars($_GET['vpntype']); +} else { + $vpntype = "pptp"; +} +if (htmlspecialchars($_POST['mode'])) { + $mode = htmlspecialchars($_POST['mode']); +} elseif (htmlspecialchars($_GET['mode'])) { + $mode = htmlspecialchars($_GET['mode']); +} else { + $mode = "login"; +} +switch ($vpntype) { + case 'pptp': + $logname = "pptps"; + break; + case 'poes': + $logname = "poes"; + break; + case 'l2tp': + $logname = "l2tps"; + break; +} + +if ($_POST['clear']) { + if ($mode != "raw") { + clear_log_file("/var/log/vpn.log"); + } else { + clear_log_file("/var/log/{$logname}.log"); + } +} + +function dump_clog_vpn($logfile, $tail) { + global $g, $config, $vpntype; + + $sor = isset($config['syslog']['reverse']) ? "-r" : ""; + + $logarr = ""; + + if (isset($config['system']['usefifolog'])) { + exec("/usr/sbin/fifolog_reader " . escapeshellarg($logfile) . " | tail {$sor} -n " . $tail, $logarr); + } else { + exec("/usr/local/sbin/clog " . escapeshellarg($logfile) . " | tail {$sor} -n " . $tail, $logarr); + } + + $rows = 0; + foreach ($logarr as $logent) { + $logent = preg_split("/\s+/", $logent, 6); + $llent = explode(",", $logent[5]); + $iftype = substr($llent[1], 0, 4); + if ($iftype != $vpntype) { + continue; + } + echo "<tr>\n"; + echo "<td>" . htmlspecialchars(join(" ", array_slice($logent, 0, 3))) . "</td>\n"; + + if ($llent[0] == "login") { + echo "<td class=\"listr\"><img src=\"/themes/{$g['theme']}/images/icons/icon_in.gif\" width=\"11\" height=\"11\" title=\"login\" alt=\"in\" /></td>\n"; + } else { + echo "<td class=\"listr\"><img src=\"/themes/{$g['theme']}/images/icons/icon_out.gif\" width=\"11\" height=\"11\" title=\"logout\" alt=\"out\" /></td>\n"; + } + + echo "<td>" . htmlspecialchars($llent[3]) . "</td>\n"; + echo "<td>" . htmlspecialchars($llent[2]) . " </td>\n"; + echo "</tr>\n"; + } + return($rows); +} + +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("System"), false, "diag_logs.php"); +$tab_array[] = array(gettext("Firewall"), false, "diag_logs_filter.php"); +$tab_array[] = array(gettext("DHCP"), false, "diag_logs.php?logfile=dhcpd"); +$tab_array[] = array(gettext("Portal Auth"), false, "diag_logs.php?logfile=portalauth"); +$tab_array[] = array(gettext("IPsec"), false, "diag_logs.php?logfile=ipsec"); +$tab_array[] = array(gettext("PPP"), false, "diag_logs.php?logfile=ppp"); +$tab_array[] = array(gettext("VPN"), true, "diag_logs_vpn.php"); +$tab_array[] = array(gettext("Load Balancer"), false, "diag_logs.php?logfile=relayd"); +$tab_array[] = array(gettext("OpenVPN"), false, "diag_logs.php?logfile=openvpn"); +$tab_array[] = array(gettext("NTP"), false, "diag_logs.php?logfile=ntpd"); +$tab_array[] = array(gettext("Settings"), false, "diag_logs_settings.php"); +display_top_tabs($tab_array); + +$tab_array = array(); +$tab_array[] = array(gettext("PPTP Logins"), + (($vpntype == "pptp") && ($mode != "raw")), + "/diag_logs_vpn.php?vpntype=pptp"); +$tab_array[] = array(gettext("PPTP Raw"), + (($vpntype == "pptp") && ($mode == "raw")), + "/diag_logs_vpn.php?vpntype=pptp&mode=raw"); +$tab_array[] = array(gettext("PPPoE Logins"), + (($vpntype == "poes") && ($mode != "raw")), + "/diag_logs_vpn.php?vpntype=poes"); +$tab_array[] = array(gettext("PPPoE Raw"), + (($vpntype == "poes") && ($mode == "raw")), + "/diag_logs_vpn.php?vpntype=poes&mode=raw"); +$tab_array[] = array(gettext("L2TP Logins"), + (($vpntype == "l2tp") && ($mode != "raw")), + "/diag_logs_vpn.php?vpntype=l2tp"); +$tab_array[] = array(gettext("L2TP Raw"), + (($vpntype == "l2tp") && ($mode == "raw")), + "/diag_logs_vpn.php?vpntype=l2tp&mode=raw"); +display_top_tabs($tab_array, false, 'nav nav-tabs'); +?> + +<!-- Raw logs are displayed as preformatted text. vpn logs are displayed as a table--> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Last ")?><?=$nentries?> <?=$vpns[$vpntype]?><?=gettext(" log entries")?></h2></div> + <div class="panel-body"> +<?php + if ($mode != "raw") { +?> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Time")?></th> + <th><?=gettext("Action")?></th> + <th><?=gettext("User")?></th> + <th><?=gettext("IP address")?></th> + </tr> + </thead> + <tbody> +<?php + $rows = dump_clog_vpn("/var/log/vpn.log", $nentries); // dump_clog_vpn provides all the need <td></td>/<tr></tr> tags +?> + </tbody> + </table> +<?php + if($rows == 0) + print_info_box('No logs to display'); +?> + </div> +<?php + } + else { +?> + <pre> +<?php + if(dump_clog_no_table("/var/log/{$logname}.log", $nentries) == 0) + print('No logs to display'); +?> + </pre> +<?php + } +?> + <p> + <form action="diag_logs_vpn.php" method="post"> + <input type="hidden" name="vpntype" id="vpntype" value="<?=$vpntype?>" /> + <input type="hidden" name="mode" id="mode" value="<?=$mode?>" /> + <input name="clear" type="submit" class="btn btn-danger" value="<?=gettext("Clear log")?>" /> + </form> + </p> + </div> +</div> +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_nanobsd.php b/src/usr/local/www/diag_nanobsd.php new file mode 100644 index 0000000..7479693 --- /dev/null +++ b/src/usr/local/www/diag_nanobsd.php @@ -0,0 +1,256 @@ +<?php +/* + diag_nanobsd.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2009 Scott Ullrich <sullrich@gmail.com> + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /sbin/mount /sbin/glabel /usr/bin/grep /usr/bin/cut /usr/bin/head /bin/cp + pfSense_BUILDER_BINARIES: /usr/sbin/boot0cfg /bin/mkdir /sbin/fsck_ufs /sbin/mount /bin/dd /sbin/tunefs + pfSense_MODULE: nanobsd +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-nanobsd +##|*NAME=Diagnostics: NanoBSD +##|*DESCR=Allow access to the 'Diagnostics: NanoBSD' page. +##|*MATCH=diag_nanobsd.php* +##|-PRIV + +ini_set('zlib.output_compression', 0); +ini_set('implicit_flush', 1); +ini_set('max_input_time', '9999'); + +require_once("guiconfig.inc"); +require_once("config.inc"); + +// Setting DEBUG to true causes the dangerous stuff on this page to be simulated rather than exectued. +// MUST be set to false for production of course +define(DEBUG, true); + +$pgtitle = array(gettext("Diagnostics"), gettext("NanoBSD")); +include("head.inc"); + +// Survey slice info +global $SLICE, $OLDSLICE, $TOFLASH, $COMPLETE_PATH, $COMPLETE_BOOT_PATH; +global $GLABEL_SLICE, $UFS_ID, $OLD_UFS_ID, $BOOTFLASH; +global $BOOT_DEVICE, $REAL_BOOT_DEVICE, $BOOT_DRIVE, $ACTIVE_SLICE; +nanobsd_detect_slice_info(); + +$NANOBSD_SIZE = nanobsd_get_size(); +$class='alert-warning'; + +if($_POST['bootslice']) { + if(!DEBUG) + nanobsd_switch_boot_slice(); + else + sleep(4); + + $savemsg = gettext("The boot slice has been set to") . " " . nanobsd_get_active_slice(); + $class='alert-success'; + // Survey slice info + nanobsd_detect_slice_info(); +} + +if($_POST['destslice'] && $_POST['duplicateslice']) { + $statusmsg = gettext("Duplicating slice. Please wait, this will take a moment..."); + + if(!DEBUG && nanobsd_clone_slice($_POST['destslice'])) { + $savemsg = gettext("The slice has been duplicated.") . "<p/>" . gettext("If you would like to boot from this newly duplicated slice please set it using the bootup information area."); + $class='alert-success'; + } else { + $savemsg = gettext("There was an error while duplicating the slice. Operation aborted."); + $class='alert-danger'; + } + // Re-Survey slice info + nanobsd_detect_slice_info(); +} + +if ($_POST['changero']) { + if (!DEBUG && is_writable("/")) { + conf_mount_ro(); + } else { + conf_mount_rw(); + } +} + +if ($_POST['setrw']) { + if(!DEBUG) { + conf_mount_rw(); + if (isset($_POST['nanobsd_force_rw'])) + $config['system']['nanobsd_force_rw'] = true; + else + unset($config['system']['nanobsd_force_rw']); + + write_config("Changed Permanent Read/Write Setting"); + conf_mount_ro(); + } + else { + $savemsg = 'Saved r/w permanantly'; + $class = 'alert-success'; + } +} + +print_info_box("The options on this page are intended for use by advanced users only."); + +if ($savemsg) + print_info_box($savemsg, $class); + +require('classes/Form.class.php'); + +$form = new Form(false); + +$section = new Form_Section('NanoBSD Option'); + +$section->addInput(new Form_StaticText( + 'Image Size', + $NANOBSD_SIZE +)); + +$slicebtn = new Form_Button('bootslice', 'Switch Slice'); +$slicebtn->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'Bootup slice', + $ACTIVE_SLICE . ' ' . $slicebtn +)); + +if (is_writable("/")) { + $refcount = refcount_read(1000); + /* refcount_read returns -1 when shared memory section does not exist */ + if ($refcount == 1 || $refcount == -1) { + $refdisplay = ""; + } else { + $refdisplay = " (Reference count " . $refcount . ")"; + } + $lbl = gettext("Read/Write") . $refdisplay; + if (!isset($config['system']['nanobsd_force_rw'])) + $btnlbl = gettext("Switch to Read-Only"); +} else { + $lbl = gettext("Read-Only"); + if (!isset($config['system']['nanobsd_force_rw'])) + $btnlbl = gettext("Switch to Read/Write"); +} + +$robtn = new Form_Button('changero', $btnlbl); +$robtn->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'Read/Write status', + $lbl . ' ' . $robtn +)); + +$section->addInput(new Form_Checkbox( + 'nanobsd_force_rw', + 'Permanent Read/Write', + 'Keep media mounted read/write at all times. ', + isset($config['system']['nanobsd_force_rw']) +))->setHelp('This setting is only temporary, and can be switched dynamically in the background.'); + +$permbtn = new Form_Button('setrw', 'Save'); +$permbtn->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + null, + $permbtn +)); + +$section->addInput(new Form_Input( + 'destslice', + null, + 'hidden', + $COMPLETE_PATH +)); + +$dupbtn = new Form_Button('duplicateslice', 'Duplicate ' . $COMPLETE_BOOT_PATH . ' -> ' . $TOFLASH); +$dupbtn->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'Duplicate boot slice', + $dupbtn +))->setHelp('This will duplicate the bootup slice to the alternate slice. Use this if you would like to duplicate the known good working boot partition to the alternate.'); + +$section->addInput(new Form_StaticText( + 'RRD/DHCP Backup', + 'These options have been relocated to the ' . '<a href="system_advanced_misc.php">' . 'System > Advanced, Miscellaneous</a> tab.' +)); + +if(file_exists("/conf/upgrade_log.txt")) { + $viewbtn = new Form_Button('viewupgradelog', 'View log'); + $viewbtn->removeClass('btn-primary')->addClass('btn-default btn-sm'); + + $section->addInput(new Form_StaticText( + 'View previous upgrade log', + $viewbtn + )); +} +$form->add($section); +print($form); + +if(file_exists("/conf/upgrade_log.txt") && $_POST['viewupgradelog']) { +?> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Previous upgrade log")?></h2></div> + <!-- No white space between the <pre> and the first output or it will appear on the page! --> + <pre><?=str_ireplace("pfsense", $g['product_name'], file_get_contents("/conf/upgrade_log.txt"))?> + <br /><?=gettext("File list:")?> + <?=str_ireplace("pfsense", $g['product_name'], file_get_contents("/conf/file_upgrade_log.txt"))?> + <br /><?=gettext("Misc log:")?> + <?=str_ireplace("pfsense", $g['product_name'], file_get_contents("/conf/firmware_update_misc_log.txt"))?> + <br /><?=gettext("fdisk/bsdlabel log:")?> + <?=str_ireplace("pfsense", $g['product_name'], file_get_contents("/conf/fdisk_upgrade_log.txt"))?> + </pre> + </div> +<?php +} +require("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_ndp.php b/src/usr/local/www/diag_ndp.php new file mode 100644 index 0000000..1fe0b24 --- /dev/null +++ b/src/usr/local/www/diag_ndp.php @@ -0,0 +1,179 @@ +<?php +/* + diag_ndp.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (C) 2004-2010 Scott Ullrich <sullrich@gmail.com> + * Copyright (C) 2011 Seth Mos <seth.mos@dds.nl> + * Part of the pfSense project (https://www.pfsense.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /bin/cat /usr/sbin/arp + pfSense_MODULE: arp +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-ndptable +##|*NAME=Diagnostics: NDP Table page +##|*DESCR=Allow access to the 'Diagnostics: NDP Table' page. +##|*MATCH=diag_ndp.php* +##|-PRIV + +@ini_set('zlib.output_compression', 0); +@ini_set('implicit_flush', 1); + +require("guiconfig.inc"); + +exec("/usr/sbin/ndp -na", $rawdata); + +$i = 0; + +/* if list */ +$ifdescrs = get_configured_interface_with_descr(); + +foreach ($ifdescrs as $key =>$interface) { + $hwif[$config['interfaces'][$key]['if']] = $interface; +} + +/* Array ( [0] => Neighbor [1] => Linklayer [2] => Address +[3] => Netif [4] => Expire [5] => S +[6] => Flags ) */ +$data = array(); +array_shift($rawdata); +foreach ($rawdata as $line) { + $elements = preg_split('/[ ]+/', $line); + + $ndpent = array(); + $ndpent['ipv6'] = trim($elements[0]); + $ndpent['mac'] = trim($elements[1]); + $ndpent['interface'] = trim($elements[2]); + $data[] = $ndpent; +} + +/* FIXME: Not ipv6 compatible dns resolving. PHP needs fixing */ +function _getHostName($mac, $ip) { + if (is_ipaddr($ip)) { + list($ip, $scope) = explode("%", $ip); + if (gethostbyaddr($ip) <> "" and gethostbyaddr($ip) <> $ip) { + return gethostbyaddr($ip); + } else { + return ""; + } + } +} + +// Resolve hostnames and replace Z_ with "". The intention +// is to sort the list by hostnames, alpha and then the non +// resolvable addresses will appear last in the list. +foreach ($data as &$entry) { + $dns = trim(_getHostName($entry['mac'], $entry['ipv6'])); + if (trim($dns)) { + $entry['dnsresolve'] = "$dns"; + } else { + $entry['dnsresolve'] = "Z_ "; + } +} + +// Sort the data alpha first +$data = msort($data, "dnsresolve"); + +// Load MAC-Manufacturer table +$mac_man = load_mac_manufacturer_table(); + +$pgtitle = array(gettext("Diagnostics"), gettext("NDP Table")); +include("head.inc"); +?> + +<div class="table-responsive"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th><?= gettext("IPv6 address"); ?></th> + <th><?= gettext("MAC address"); ?></th> + <th><?= gettext("Hostname"); ?></th> + <th><?= gettext("Interface"); ?></th> + </tr> + </thead> + <tbody> + <?php foreach ($data as $entry): ?> + <tr> + <td><?=$entry['ipv6']?></td> + <td> + <?php + $mac=trim($entry['mac']); + $mac_hi = strtoupper($mac[0] . $mac[1] . $mac[3] . $mac[4] . $mac[6] . $mac[7]); + ?> + <?=$mac?> + + <? if(isset($mac_man[$mac_hi])):?> + (<?=$mac_man[$mac_hi]?>) + <?endif?> + + </td> + <td> + <?=htmlspecialchars(str_replace("Z_ ", "", $entry['dnsresolve']))?> + </td> + <td> + <?php + if(isset($hwif[$entry['interface']])) + echo $hwif[$entry['interface']]; + else + echo $entry['interface']; + ?> + </td> + </tr> + <?php endforeach; ?> + </tbody> + </table> +</div> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_packet_capture.php b/src/usr/local/www/diag_packet_capture.php new file mode 100644 index 0000000..d4adee8 --- /dev/null +++ b/src/usr/local/www/diag_packet_capture.php @@ -0,0 +1,505 @@ +<?php +/* + diag_packet_capture.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /bin/ps /usr/bin/grep /usr/sbin/tcpdump + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-packetcapture +##|*NAME=Diagnostics: Packet Capture page +##|*DESCR=Allow access to the 'Diagnostics: Packet Capture' page. +##|*MATCH=diag_packet_capture.php* +##|-PRIV + +$allowautocomplete = true; + +function fixup_host_logic($value) { + return str_replace(array(" ", ",", "+", "|", "!"), array("", "and ", "and ", "or ", "not "), $value); +} + +function strip_host_logic($value) { + return str_replace(array(" ", ",", "+", "|", "!"), array("", "", "", "", ""), $value); +} + +function get_host_boolean($value, $host) { + $value = str_replace(array("!", $host), array("", ""), $value); + $andor = ""; + switch (trim($value)) { + case "|": + $andor = "or "; + break; + case ",": + case "+": + $andor = "and "; + break; + } + + return $andor; +} + +function has_not($value) { + return strpos($value, '!') !== false; +} + +function fixup_not($value) { + return str_replace("!", "not ", $value); +} + +function strip_not($value) { + return ltrim(trim($value), '!'); +} + +function fixup_host($value, $position) { + $host = strip_host_logic($value); + $not = has_not($value) ? "not " : ""; + $andor = ($position > 0) ? get_host_boolean($value, $host) : ""; + if (is_ipaddr($host)) { + return "{$andor}host {$not}" . $host; + } elseif (is_subnet($host)) { + return "{$andor}net {$not}" . $host; + } else { + return ""; + } +} + +if ($_POST['downloadbtn'] == gettext("Download Capture")) { + $nocsrf = true; +} + +$pgtitle = array(gettext("Diagnostics"), gettext("Packet Capture")); +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); + +$fp = "/root/"; +$fn = "packetcapture.cap"; +$snaplen = 0;//default packet length +$count = 100;//default number of packets to capture + +$fams = array('ip', 'ip6'); +$protos = array('icmp', 'icmp6', 'tcp', 'udp', 'arp', 'carp', 'esp', + '!icmp', '!icmp6', '!tcp', '!udp', '!arp', '!carp', '!esp'); + +$input_errors = array(); + +$interfaces = get_configured_interface_with_descr(); +if (isset($config['ipsec']['enable'])) { + $interfaces['ipsec'] = "IPsec"; +} +foreach (array('server', 'client') as $mode) { + if (is_array($config['openvpn']["openvpn-{$mode}"])) { + foreach ($config['openvpn']["openvpn-{$mode}"] as $id => $setting) { + if (!isset($setting['disable'])) { + $interfaces['ovpn' . substr($mode, 0, 1) . $setting['vpnid']] = gettext("OpenVPN") . " ".$mode.": ".htmlspecialchars($setting['description']); + } + } + } +} + +if ($_POST) { + $host = $_POST['host']; + $selectedif = $_POST['interface']; + $count = $_POST['count']; + $snaplen = $_POST['snaplen']; + $port = $_POST['port']; + $detail = $_POST['detail']; + $fam = $_POST['fam']; + $proto = $_POST['proto']; + + if (!array_key_exists($selectedif, $interfaces)) { + $input_errors[] = gettext("Invalid interface."); + } + + if ($fam !== "" && $fam !== "ip" && $fam !== "ip6") { + $input_errors[] = gettext("Invalid address family."); + } + + if ($proto !== "" && !in_array(strip_not($proto), $protos)) { + $input_errors[] = gettext("Invalid protocol."); + } + + if ($host != "") { + $host_string = str_replace(array(" ", "|", ","), array("", "#|", "#+"), $host); + + if (strpos($host_string, '#') === false) { + $hosts = array($host); + } else { + $hosts = explode('#', $host_string); + } + + foreach ($hosts as $h) { + if (!is_subnet(strip_host_logic($h)) && !is_ipaddr(strip_host_logic($h))) { + $input_errors[] = sprintf(gettext("A valid IP address or CIDR block must be specified. [%s]"), $h); + } + } + } + + if ($port != "") { + if (!is_port(strip_not($port))) { + $input_errors[] = gettext("Invalid value specified for port."); + } + } + + if ($snaplen == "") { + $snaplen = 0; + } else { + if (!is_numeric($snaplen) || $snaplen < 0) { + $input_errors[] = gettext("Invalid value specified for packet length."); + } + } + + if ($count == "") { + $count = 0; + } else { + if (!is_numeric($count) || $count < 0) { + $input_errors[] = gettext("Invalid value specified for packet count."); + } + } + + if (!count($input_errors)) { + $do_tcpdump = true; + + conf_mount_rw(); + + if ($_POST['promiscuous']) { + //if promiscuous mode is checked + $disablepromiscuous = ""; + } else { + //if promiscuous mode is unchecked + $disablepromiscuous = "-p"; + } + + if ($_POST['dnsquery']) { + //if dns lookup is checked + $disabledns = ""; + } else { + //if dns lookup is unchecked + $disabledns = "-n"; + } + + if ($_POST['startbtn'] != "") { + $action = gettext("Start"); + + //delete previous packet capture if it exists + if (file_exists($fp.$fn)) { + unlink ($fp.$fn); + } + + } elseif ($_POST['stopbtn'] != "") { + $action = gettext("Stop"); + $processes_running = trim(shell_exec("/bin/ps axw -O pid= | /usr/bin/grep tcpdump | /usr/bin/grep {$fn} | /usr/bin/egrep -v '(pflog|grep)'")); + + //explode processes into an array, (delimiter is new line) + $processes_running_array = explode("\n", $processes_running); + + //kill each of the packetcapture processes + foreach ($processes_running_array as $process) { + $process_id_pos = strpos($process, ' '); + $process_id = substr($process, 0, $process_id_pos); + exec("kill $process_id"); + } + + } elseif ($_POST['downloadbtn'] != "") { + //download file + $fs = filesize($fp.$fn); + header("Content-Type: application/octet-stream"); + header("Content-Disposition: attachment; filename=$fn"); + header("Content-Length: $fs"); + readfile($fp.$fn); + exit; + } + } +} else { + $do_tcpdump = false; +} + +$protocollist = array( + '' => 'Any', + 'icmp' => 'ICMP', + '!icmp' => 'Exclude ICMP', + 'icmp6' => 'ICMPv6', + '!icmp6' => 'Exclude ICMPv6', + 'tcp' => 'TCP', + '!tcp' => 'Exclude TCP', + 'udp' => 'UDP', + '!udp' => 'Exclude UDP', + 'arp' => 'ARP', + '!arp' => 'Exclude ARP', + 'carp' => 'CARP (VRRP)', + '!carp' => 'Exclude CARP (VRRP)', + 'esp' => 'ESP' +); + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(false); // No button yet. We add those later depending on the required action + +$section = new Form_Section('General Logging Options'); + +$section->addInput(new Form_Select( + 'interface', + 'Interface', + $selectedif, + $interfaces +))->setHelp('Select the interface on which to capture traffic. '); + +$section->addInput(new Form_Checkbox( + 'promiscuous', + 'Promiscuous', + 'Packet capture will be performed using promiscuous mode', + $pconfig['promiscuous'] +))->setHelp('Note: Some network adapters do not support or work well in promiscuous mode.'. '<br />' . + 'More: ' . '<a target="_blank" href="http://www.freebsd.org/cgi/man.cgi?query=tcpdump&apropos=0&sektion=0&manpath=FreeBSD+8.3-stable&arch=default&format=html">' . + 'Packet capture' . '</a>'); + +$section->addInput(new Form_Select( + 'fam', + 'Address Family', + $fam, + array('' => 'Any', + 'ip' => 'IPv4 Only', + 'ip6' => 'IPv6 Only' + ) +))->setHelp('Select the type of traffic to be captured'); + +$section->addInput(new Form_Select( + 'proto', + 'Protocol', + $proto, + $protocollist +))->setHelp('Select the protocol to capture, or "Any". '); + +$section->addInput(new Form_Input( + 'host', + 'Host Address', + 'text', + $host +))->setHelp('This value is either the Source or Destination IP address or subnet in CIDR notation. The packet capture will look for this address in either field.' . '<br />' . + 'Matching can be negated by preceding the value with "!". Multiple IP addresses or CIDR subnets may be specified. Comma (",") separated values perform a boolean "AND". ' . + 'Separating with a pipe ("|") performs a boolean "OR".' . '<br />' . + 'If you leave this field blank, all packets on the specified interface will be captured.'); + +$section->addInput(new Form_Input( + 'port', + 'Port', + 'text', + $port +))->setHelp('The port can be either the source or destination port. The packet capture will look for this port in either field. ' . + 'Leave blank if you do not want to filter by port.'); + +$section->addInput(new Form_Input( + 'snaplen', + 'Packet Length', + 'text', + $snaplen +))->setHelp('The Packet length is the number of bytes of each packet that will be captured. Default value is 0, ' . + 'which will capture the entire frame regardless of its size.'); + +$section->addInput(new Form_Input( + 'count', + 'Count', + 'text', + $count +))->setHelp('This is the number of packets the packet capture will grab. Default value is 100.' . '<br />' . + 'Enter 0 (zero) for no count limit.'); + +$section->addInput(new Form_Select( + 'detail', + 'Level of detail', + $detail, + array('' => 'Any', + 'normal' => 'Normal', + 'medium' => 'Medium', + 'high' => 'High', + 'full' => 'Full', + ) +))->setHelp('This is the level of detail that will be displayed after hitting "Stop" when the packets have been captured.' . '<br />' . + 'This option does not affect the level of detail when downloading the packet capture. '); + +$section->addInput(new Form_Checkbox( + 'dnsquery', + 'Reverse DNS Lookup', + null, + $_POST['dnsquery'] +))->setHelp('This check box will cause the packet capture to perform a reverse DNS lookup associated with all IP addresses.' . '<br />' . + 'This option can cause delays for large packet captures.'); + +$form->add($section); + +/* check to see if packet capture tcpdump is already running */ +$processcheck = (trim(shell_exec("/bin/ps axw -O pid= | /usr/bin/grep tcpdump | /usr/bin/grep {$fn} | /usr/bin/egrep -v '(pflog|grep)'"))); + +$processisrunning = ($processcheck != ""); + +if (($action == gettext("Stop") or $action == "") and $processisrunning != true) { + $form->addGlobal(new Form_Button( + 'startbtn', + 'Start' + ))->removeClass('btn-primary')->addClass('btn-success'); +} +else { + $form->addGlobal(new Form_Button( + 'stopbtn', + 'Stop' + ))->removeClass('btn-primary')->addClass('btn-warning'); +} + +if (file_exists($fp.$fn) and $processisrunning != true) { + $form->addGlobal(new Form_Button( + 'viewbtn', + 'View Capture' + ))->removeClass('btn-primary'); + + $form->addGlobal(new Form_Button( + 'downloadbtn', + 'Download Capture' + ))->removeClass('btn-primary'); + + $section->addInput(new Form_StaticText( + 'Last capture', + date("F jS, Y g:i:s a.", filemtime($fp.$fn)) + )); +} + +print($form); + +if ($do_tcpdump) : + $matches = array(); + + if (in_array($fam, $fams)) + $matches[] = $fam; + + if (in_array($proto, $protos)) + $matches[] = fixup_not($proto); + + if ($port != "") + $matches[] = "port ".fixup_not($port); + + if ($host != "") { + $hostmatch = ""; + $hostcount = 0; + + foreach ($hosts as $h) { + $h = fixup_host($h, $hostcount++); + + if (!empty($h)) + $hostmatch .= " " . $h; + } + + if (!empty($hostmatch)) + $matches[] = "({$hostmatch})"; + } + + if ($count != "0" ) { + $searchcount = "-c " . $count; + } else { + $searchcount = ""; + } + + $selectedif = convert_friendly_interface_to_real_interface_name($selectedif); + + if ($action == gettext("Start")) { + $matchstr = implode($matches, " and "); + + print_info_box(gettext('Packet Capture is running'), 'info'); + + $cmd = "/usr/sbin/tcpdump -i {$selectedif} {$disablepromiscuous} {$searchcount} -s {$snaplen} -w {$fp}{$fn} " . escapeshellarg($matchstr); + // Debug + //echo $cmd; + mwexec_bg ($cmd); + } else { +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Packets Captured')?></h2></div> + <div class="panel-body"> + +<?php + $detail_args = ""; + switch ($detail) { + case "full": + $detail_args = "-vv -e"; + break; + case "high": + $detail_args = "-vv"; + break; + case "medium": + $detail_args = "-v"; + break; + case "normal": + default: + $detail_args = "-q"; + break; + } + + print('<pre>'); + system("/usr/sbin/tcpdump {$disabledns} {$detail_args} -r {$fp}{$fn}"); + print('</pre>'); + + conf_mount_ro(); +?> + </div> +</div> +<?php + } +endif; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_patterns.php b/src/usr/local/www/diag_patterns.php new file mode 100644 index 0000000..8386f25 --- /dev/null +++ b/src/usr/local/www/diag_patterns.php @@ -0,0 +1,123 @@ +<?php +/* $Id$ */ +/* + diag_patterns.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Exec+ v1.02-000 - Copyright 2001-2003, All rights reserved + * Created by André Ribeiro and Hélder Pereira + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: shaper +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-patters +##|*NAME=Diagnostics: Patterns page +##|*DESCR=Allow access to the 'Diagnostics: Patterns' page. +##|*MATCH=patterns.php* +##|-PRIV + +require("guiconfig.inc"); + +// Defining this here ensures that both instances (button name and POST check) are identical +$buttonlabel = gettext("Upload Pattern file"); + +//Move the upload file to /usr/local/share/protocols (is_uploaded_file must use tmp_name as argument) +if (($_POST['submit'] == $buttonlabel) && is_uploaded_file($_FILES['ulfile']['tmp_name'])) { + if(fileExtension($_FILES['ulfile']['name'])) { + if (!is_array($config['l7shaper']['custom_pat'])) + $config['l7shaper']['custom_pat'] = array(); + + $config['l7shaper']['custom_pat'][$_FILES['ulfile']['name']] = base64_encode(file_get_contents($_FILES['ulfile']['tmp_name'])); + write_config(sprintf(gettext("Added custom l7 pattern %s"), $_FILES['ulfile']['name'])); + move_uploaded_file($_FILES['ulfile']['tmp_name'], "/usr/local/share/protocols/" . $_FILES['ulfile']['name']); + $ulmsg = gettext("Uploaded file to") . " /usr/local/share/protocols/" . htmlentities($_FILES['ulfile']['name']); + $class = 'alert-success'; + } + else { + $ulmsg = gettext("Error: You must upload a file with .pat extension."); + $class = 'alert-danger'; + } +} + +//Check if file has correct extension (.pat) +function fileExtension($nameFile) { + $format = substr($nameFile, -4); + return ($format == ".pat"); +} + +$pgtitle = array(gettext("Diagnostics"), gettext("Add layer7 pattern")); +include("head.inc"); + +if($ulmsg) + print_info_box($ulmsg, $class); + +require('classes/Form.class.php'); + +$form = new Form($buttonlabel); + +$form->setMultipartEncoding(); + +$section = new Form_Section('Upload Layer7 pattern file'); + +$filepicker = new Form_Input( + 'ulfile', + 'File to upload', + 'file' +); + +$section->addInput($filepicker)->setHelp('Choose the file you wish to upload (*.pat)'); + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_pf_info.php b/src/usr/local/www/diag_pf_info.php new file mode 100644 index 0000000..881da29 --- /dev/null +++ b/src/usr/local/www/diag_pf_info.php @@ -0,0 +1,146 @@ +<?php +/* $Id$ */ +/* + diag_pf_info.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2010 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /usr/bin/top + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-pf-info +##|*NAME=Diagnostics: pfInfo +##|*DESCR=Allows access to the 'Diagnostics: pfInfo' page +##|*MATCH=diag_pf_info.php* +##|-PRIV + +require("guiconfig.inc"); + +$pgtitle = gettext("Diagnostics: pfInfo"); + +if (stristr($_POST['Submit'], gettext("No"))) { + header("Location: index.php"); + exit; +} + +if($_REQUEST['getactivity']) { + $text = `/sbin/pfctl -vvsi`; + $text .= "<p/>"; + $text .= `/sbin/pfctl -vvsm`; + $text .= "<p/>"; + $text .= `/sbin/pfctl -vvst`; + $text .= "<p/>"; + $text .= `/sbin/pfctl -vvsI`; + echo $text; + exit; +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); +$form = new Form(false); +$form->addGlobal(new Form_Input( + 'getactivity', + null, + 'hidden', + 'yes' +)); +$section = new Form_Section('Auto update page'); + +$section->addInput(new Form_Checkbox( + 'refresh', + 'Refresh', + 'Automatically refresh the output below', + true +)); + +$form->add($section); +print $form; + +?> +<script> + function getpfinfo() { + if (!$('#refresh').is(':checked')) + return; + + $.ajax( + '/diag_pf_info.php', + { + type: 'post', + data: $(document.forms[0]).serialize(), + success: function (data) { + $('#xhrOutput').html(data); + }, + }); + } + + events.push(function(){ + setInterval('getpfinfo()', 2500); + getpfinfo(); + }); +</script> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Output')?></h2></div> + <div class="panel panel-body"> + <pre id="xhrOutput"><?=gettext("Gathering PF information, please wait...")?></pre> + </div> +</div> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_ping.php b/src/usr/local/www/diag_ping.php new file mode 100644 index 0000000..cb4bf2e --- /dev/null +++ b/src/usr/local/www/diag_ping.php @@ -0,0 +1,228 @@ +<?php +/* + diag_ping.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2003-2005 Bob Zoller (bob@kludgebox.com) and Manuel Kasper <mk@neon1.net> + * part of m0n0wall (http://m0n0.ch/wall) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /sbin/ping /sbin/ping6 + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-ping +##|*NAME=Diagnostics: Ping page +##|*DESCR=Allow access to the 'Diagnostics: Ping' page. +##|*MATCH=diag_ping.php* +##|-PRIV + +$allowautocomplete = true; +$pgtitle = array(gettext("Diagnostics"), gettext("Ping")); +require_once("guiconfig.inc"); + +define('MAX_COUNT', 10); +define('DEFAULT_COUNT', 3); + +function create_sourceaddresslist() { + $list = array('any' => 'Any'); + + foreach (get_possible_traffic_source_addresses(true) as $sipname) + $list[$sipname['value']] = $sipname['name']; + + return $list; +} + +if ($_POST || $_REQUEST['host']) { + unset($input_errors); + unset($do_ping); + + /* input validation */ + $reqdfields = explode(" ", "host count"); + $reqdfieldsn = array(gettext("Host"), gettext("Count")); + do_input_validation($_REQUEST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($_REQUEST['count'] < 1) || ($_REQUEST['count'] > MAX_COUNT)) { + $input_errors[] = sprintf(gettext("Count must be between 1 and %s"), MAX_COUNT); + } + + $host = trim($_REQUEST['host']); + $ipproto = $_REQUEST['ipproto']; + if (($ipproto == "ipv4") && is_ipaddrv6($host)) { + $input_errors[] = gettext("When using IPv4, the target host must be an IPv4 address or hostname."); + } + if (($ipproto == "ipv6") && is_ipaddrv4($host)) { + $input_errors[] = gettext("When using IPv6, the target host must be an IPv6 address or hostname."); + } + + if (!$input_errors) { + $do_ping = true; + $sourceip = $_REQUEST['sourceip']; + $count = $_POST['count']; + if (preg_match('/[^0-9]/', $count)) { + $count = DEFAULT_COUNT; + } + } +} + +if (!isset($do_ping)) { + $do_ping = false; + $host = ''; + $count = DEFAULT_COUNT; +} + +if($do_ping) { +?> + <script type="text/javascript"> + //<![CDATA[ + window.onload=function(){ + document.getElementById("pingCaptured").wrap='off'; + } + //]]> + </script> +<?php + $ifscope = ''; + $command = "/sbin/ping"; + if ($ipproto == "ipv6") { + $command .= "6"; + $ifaddr = is_ipaddr($sourceip) ? $sourceip : get_interface_ipv6($sourceip); + if (is_linklocal($ifaddr)) + $ifscope = get_ll_scope($ifaddr); + } else { + $ifaddr = is_ipaddr($sourceip) ? $sourceip : get_interface_ip($sourceip); + } + + if ($ifaddr && (is_ipaddr($host) || is_hostname($host))) { + $srcip = "-S" . escapeshellarg($ifaddr); + if (is_linklocal($host) && !strstr($host, "%") && !empty($ifscope)) + $host .= "%{$ifscope}"; + } + + $cmd = "{$command} {$srcip} -c" . escapeshellarg($count) . " " . escapeshellarg($host); + //echo "Ping command: {$cmd}\n"; + $result = shell_exec($cmd); + + if(empty($result)) + $input_errors[] = "Host \"" . $host . "\" did not respond or could not be resolved."; + +} + +include('head.inc'); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form('Ping'); + +$section = new Form_Section('Ping'); + +$section->addInput(new Form_Input( + 'host', + 'Hostname', + 'text', + $host, + ['placeholder' => 'Hostname to ping'] +)); + +$group = new Form_Group('IP Protocol'); +$group->add(new Form_Checkbox( + 'ipproto', + null, + 'IPv4', + ('ipv6' != $ipproto), # negative check, so this would be checked by default + 'ipv4' +))->displayAsRadio(); +$group->add(new Form_Checkbox( + 'ipproto', + null, + 'IPv6', + ('ipv6' == $ipproto), + 'ipv6' +))->displayAsRadio(); +$group->setHelp('Select the protocol to use'); +$section->add($group); + +$section->addInput(new Form_Select( + 'sourceip', + 'Source address', + $pconfig['source'], + create_sourceaddresslist() +))->setHelp('Select source address for the ping'); + +$section->addInput(new Form_Select( + 'count', + 'Maximum number of pings', + $count, + array_combine(range(1, MAX_COUNT), range(1, MAX_COUNT)) +))->setHelp('Select the maximum number pings'); + +$form->add($section); +print $form; + +if($do_ping && !empty($result) && !$input_errors) { +?> + <div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title">Results</h2> + </div> + + <div class="panel-body"> + <pre><?= $result ?></pre> + </div> + </div> +<?php +} + +include('foot.inc'); diff --git a/src/usr/local/www/diag_pkglogs.php b/src/usr/local/www/diag_pkglogs.php new file mode 100755 index 0000000..e02cfff --- /dev/null +++ b/src/usr/local/www/diag_pkglogs.php @@ -0,0 +1,148 @@ +<?php +/* + $Id$ + + diag_pkglogs.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2005 Colin Smith + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + <logging> + <logtab>arpwatch</logtab> + <grepfor>arpwatch</logtab> + </logging> + + <invertgrep/> + <logfile>/var/log/arpwatch.log</logfile> + +*/ + +/* + pfSense_BUILDER_BINARIES: /usr/bin/netstat + pfSense_MODULE: pkgs +*/ + +##|+PRIV +##|*IDENT=page-status-packagelogs +##|*NAME=Status: Package logs page +##|*DESCR=Allow access to the 'Status: Package logs' page. +##|*MATCH=diag_pkglogs.php* +##|-PRIV + +require("guiconfig.inc"); +require("pkg-utils.inc"); + +if (!($nentries = $config['syslog']['nentries'])) { + $nentries = 50; +} + +$i = 0; +$pkgwithlogging = false; +$apkg = $_GET['pkg']; +if (!$apkg) { // If we aren't looking for a specific package, locate the first package that handles logging. + if ($config['installedpackages']['package'] <> "") { + foreach ($config['installedpackages']['package'] as $package) { + if (is_array($package['logging'])) { + $pkgwithlogging = true; + $apkg = $package['name']; + $apkgid = $i; + break; + } + $i++; + } + } +} elseif ($apkg) { + $apkgid = get_package_id($apkg); + if ($apkgid != -1) { + $pkgwithlogging = true; + $i = $apkgid; + } +} + +$pgtitle = array(gettext("Status"), gettext("Package logs")); +include("head.inc"); + +if($pkgwithlogging == false) { + print_info_box(gettext("No packages with logging facilities are currently installed.")); +} else { + $tab_array = array(); + foreach($config['installedpackages']['package'] as $package) { + if(is_array($package['logging'])) { + if(!($logtab = $package['logging']['logtab'])) + $logtab = $package['name']; + + if($apkg == $package['name']) { + $curtab = $logtab; + $tab_array[] = array(sprintf(gettext("%s"), $logtab), true, "diag_pkglogs.php?pkg=".$package['name']); + } else { + $tab_array[] = array(sprintf(gettext("%s"), $logtab), false, "diag_pkglogs.php?pkg=".$package['name']); + } + } + } + display_top_tabs($tab_array); +?> + + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=printf(gettext('Last %1$s %2$s log entries'),$nentries,$curtab)?></h2></div> + <div>class="panel-body"> + <pre> +<?php + $package = $config['installedpackages']['package'][$apkgid]; + dump_clog_no_table($g['varlog_path'] . '/' . $package['logging']['logfilename'], $nentries, true, array()); +?> + </pre> + </div> + </div> + +<?php } + +include("foot.inc"); ?>
\ No newline at end of file diff --git a/src/usr/local/www/diag_resetstate.php b/src/usr/local/www/diag_resetstate.php new file mode 100644 index 0000000..7dff11c --- /dev/null +++ b/src/usr/local/www/diag_resetstate.php @@ -0,0 +1,158 @@ +<?php +/* $Id$ */ +/* + diag_resetstate.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004-2009 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-resetstate +##|*NAME=Diagnostics: Reset state page +##|*DESCR=Allow access to the 'Diagnostics: Reset state' page. +##|*MATCH=diag_resetstate.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("filter.inc"); + +if ($_POST) { + $savemsg = ""; + + if ($_POST['statetable']) { + filter_flush_state_table(); + if ($savemsg) { + $savemsg .= " "; + } + $savemsg .= gettext("The state table has been flushed successfully."); + } + + if ($_POST['sourcetracking']) { + mwexec("/sbin/pfctl -F Sources"); + if ($savemsg) { + $savemsg .= " <br />"; + } + $savemsg .= gettext("The source tracking table has been flushed successfully."); + } +} + +$pgtitle = array(gettext("Diagnostics"), gettext("Reset state")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'alert-success'); + +$statetabelhelp = 'Resetting the state tables will remove all entries from the corresponding tables. This means that all open connections ' . + 'will be broken and will have to be re-established. This may be necessary after making substantial changes to the ' . + 'firewall and/or NAT rules, especially if there are IP protocol mappings (e.g. for PPTP or IPv6) with open connections.' . + '<br /><br />' . + 'The firewall will normally leave the state tables intact when changing rules.' . + '<br /><br />' . + '<strong>NOTE:</strong> If you reset the firewall state table, the browser session may appear to be hung after clicking "Reset". ' . + 'Simply refresh the page to continue.'; + +$sourcetablehelp = 'Resetting the source tracking table will remove all source/destination associations. ' . + 'This means that the \"sticky\" source/destination association ' . + 'will be cleared for all clients.' . + ' <br /><br />' . + 'This does not clear active connection states, only source tracking.'; + +$tab_array = array(); +$tab_array[] = array(gettext("States"), false, "diag_dump_states.php"); + +if (isset($config['system']['lb_use_sticky'])) + $tab_array[] = array(gettext("Source Tracking"), false, "diag_dump_states_sources.php"); + +$tab_array[] = array(gettext("Reset States"), true, "diag_resetstate.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$resetbtn = new Form_Button( + 'Submit', + 'Reset' +); + +$resetbtn->removeClass('btn-primary')->addClass('btn-danger'); + +$form = new Form($resetbtn); + +$section = new Form_Section('Select states to reset'); + +$section->addInput(new Form_Checkbox( + 'statetable', + 'State Table', + 'Reset the firewall state table', + true +))->setHelp($statetabelhelp); + +if(isset($config['system']['lb_use_sticky'])) { + $section->addInput(new Form_Checkbox( + 'sourcetracking', + 'Source Tracking', + 'Reset firewall source tracking', + true + ))->setHelp($sourcetablehelp); +} + +$form->add($section); +print $form; +?> + +<?php include("foot.inc"); ?>
\ No newline at end of file diff --git a/src/usr/local/www/diag_routes.php b/src/usr/local/www/diag_routes.php new file mode 100644 index 0000000..3b8e3ab --- /dev/null +++ b/src/usr/local/www/diag_routes.php @@ -0,0 +1,242 @@ +<?php + +/* $Id$ */ +/* + diag_routes.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2006 Fernando Lamos + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /usr/bin/netstat + pfSense_MODULE: routing +*/ +##|+PRIV +##|*IDENT=page-diagnostics-routingtables +##|*NAME=Diagnostics: Routing tables page +##|*DESCR=Allow access to the 'Diagnostics: Routing tables' page. +##|*MATCH=diag_routes.php* +##|-PRIV + +include('guiconfig.inc'); + +$limit='100'; +$filter=''; + +if (isset($_REQUEST['isAjax'])) { + $netstat = "/usr/bin/netstat -rW"; + if (isset($_REQUEST['IPv6'])) { + $netstat .= " -f inet6"; + echo "IPv6\n"; + } else { + $netstat .= " -f inet"; + echo "IPv4\n"; + + } + if (!isset($_REQUEST['resolve'])) { + $netstat .= " -n"; + } + + if (!empty($_REQUEST['filter'])) { + $netstat .= " | /usr/bin/sed -e '1,3d; 5,\$ { /" . escapeshellarg(htmlspecialchars($_REQUEST['filter'])) . "/!d; };'"; + } else { + $netstat .= " | /usr/bin/sed -e '1,3d'"; + } + + if (is_numeric($_REQUEST['limit']) && $_REQUEST['limit'] > 0) { + $netstat .= " | /usr/bin/head -n {$_REQUEST['limit']}"; + } + + echo htmlspecialchars_decode(shell_exec($netstat)); + + exit; +} + +$pgtitle = array(gettext("Diagnostics"), gettext("Routing tables")); +$shortcut_section = "routing"; + +include('head.inc'); + +require('classes/Form.class.php'); + +$form = new Form('Update'); +$form->addGlobal(new Form_Input( + 'isAjax', + null, + 'hidden', + 1 +)); +$section = new Form_Section('Traceroute'); + +$section->addInput(new Form_Checkbox( + 'resolve', + 'Resolve names', + 'Enable', + $resolve +))->setHelp('Enabling name resolution may cause the query should take longer.'. + ' You can stop it at any time by clicking the Stop button in your browser.'); + +$validLimits = array('10', '50', '100', '200', '500', '1000', 'all'); +$section->addInput(new Form_Select( + 'limit', + 'Rows to display', + $limit, + array_combine($validLimits, $validLimits) +)); + +$section->addInput(new Form_Input( + 'filter', + 'Filter', + 'text', + $host +))->setHelp('Use a regular expression to filter IP address or hostnames'); + +$form->add($section); +print $form; +?> +<script> +function update_routes(section) { + $.ajax( + '/diag_routes.php', + { + type: 'post', + data: $(document.forms[0]).serialize() +'&'+ section +'=true', + success: update_routes_callback, + }); +} + +function update_routes_callback(html) { + // First line contains section + var responseTextArr = html.split("\n"); + var section = responseTextArr.shift(); + var tbody = ''; + var field = ''; + var tr_class = ''; + var thead = '<tr>'; + + for (var i = 0; i < responseTextArr.length; i++) { + if (responseTextArr[i] == "") + continue; + var tmp = '<tr>'; + var j = 0; + var entry = responseTextArr[i].split(" "); + for (var k = 0; k < entry.length; k++) { + if (entry[k] == "") + continue; + if (i == 0) + tmp += '<th>' + entry[k] + '<\/th>'; + else + tmp += '<td>' + entry[k] + '<\/td>'; + j++; + } + + tmp += '<td><\/td>'; + + if (i == 0) + thead += tmp; + else + tbody += tmp; + } + + $('#' + section + ' > thead').html(thead); + $('#' + section + ' > tbody').html(tbody); +} + +function update_all_routes() { + update_routes("IPv4"); + update_routes("IPv6"); +} + +events.push(function(){ + setInterval('update_all_routes()', 5000); + update_all_routes(); + + $(document.forms[0]).on('submit', function(e){ + update_all_routes(); + + e.preventDefault(); + }); +}); +</script> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">IPv4 Routes</h2></div> + <div class="panel panel-body"> + <table class="table table-striped table-compact" id="IPv4"> + <thead> + <!-- filled by xhr --> + </thead> + <tbody> + <tr> + <td><?=gettext("Gathering data, please wait...")?></td> + </tr> + </tbody> + </table> + </div> +</div> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">IPv6 Routes</h2></div> + <div class="panel panel-body"> + <table class="table table-striped table-compact" id="IPv6"> + <thead> + <!-- filled by xhr --> + </thead> + <tbody> + <tr> + <td><?=gettext("Gathering data, please wait...")?></td> + </tr> + </tbody> + </table> + </div> +</div> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_smart.php b/src/usr/local/www/diag_smart.php new file mode 100644 index 0000000..d09a891 --- /dev/null +++ b/src/usr/local/www/diag_smart.php @@ -0,0 +1,501 @@ +<?php +/* + diag_smart.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2010 - Jim Pingle + * Copyright (c) 2006, Eric Friesen + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +require("guiconfig.inc"); + +$pgtitle = array(gettext("Diagnostics"), gettext("S.M.A.R.T. Monitor Tools")); +$smartctl = "/usr/local/sbin/smartctl"; +$smartd = "/usr/local/sbin/smartd"; +$start_script = "/usr/local/etc/rc.d/smartd.sh"; + +$valid_test_types = array("offline", "short", "long", "conveyance"); +$valid_info_types = array("i", "H", "c", "A", "a"); +$valid_log_types = array("error", "selftest"); + +$closehead = false; +include("head.inc"); + +// Highlights the words "PASSED", "FAILED", and "WARNING". +function add_colors($string) +{ + // To add words keep arrays matched by numbers + $patterns[0] = '/PASSED/'; + $patterns[1] = '/FAILED/'; + $patterns[2] = '/Warning/'; + $replacements[0] = '<b><font color="#00ff00">' . gettext("PASSED") . '</font></b>'; + $replacements[1] = '<b><font color="#ff0000">' . gettext("FAILED") . '</font></b>'; + $replacements[2] = '<font color="#ff0000">' . gettext("Warning") . '</font>'; + ksort($patterns); + ksort($replacements); + return preg_replace($patterns, $replacements, $string); +} + +// Edits smartd.conf file, adds or removes email for failed disk reporting +function update_email($email) { + // Did they pass an email? + if (!empty($email)) { + // Put it in the smartd.conf file + shell_exec("/usr/bin/sed -i old 's/^DEVICESCAN.*/DEVICESCAN -H -m " . escapeshellarg($email) . "/' /usr/local/etc/smartd.conf"); + } else { + // Remove email flags in smartd.conf + shell_exec("/usr/bin/sed -i old 's/^DEVICESCAN.*/DEVICESCAN/' /usr/local/etc/smartd.conf"); + } +} + +function smartmonctl($action) { + global $start_script; + shell_exec($start_script . escapeshellarg($action)); +} + +// What page, aka. action is being wanted +// If they "get" a page but don't pass all arguments, smartctl will throw an error +$action = (isset($_POST['action']) ? $_POST['action'] : $_GET['action']); +$targetdev = basename($_POST['device']); + +if (!file_exists('/dev/' . $targetdev)) { + echo "Device does not exist, bailing."; + return; +} + +require('classes/Form.class.php'); + +$tab_array = array(); +$tab_array[0] = array(gettext("Information/Tests"), ($action != 'config'), $_SERVER['PHP_SELF'] . "?action=default"); +$tab_array[1] = array(gettext("Config"), ($action == 'config'), $_SERVER['PHP_SELF'] . "?action=config"); +display_top_tabs($tab_array); + +switch($action) { + // Testing devices + case 'test': + { + $test = $_POST['testType']; + if (!in_array($test, $valid_test_types)) { + echo "Invalid test type, bailing."; + return; + } + + $output = add_colors(shell_exec($smartctl . " -t " . escapeshellarg($test) . " /dev/" . escapeshellarg($targetdev))); +?> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Test results')?></h2></div> + <div class="panel-body"> + <pre><?=$output?></pre> + </div> + </div> + + <form action="diag_smart.php" method="post" name="abort"> + <input type="hidden" name="device" value="<?=$targetdev?>" /> + <input type="hidden" name="action" value="abort" /> + <nav class="action-buttons"> + <input type="submit" name="submit" class="btn btn-danger" value="<?=gettext("Abort")?>" /> + <a href="<?=$_SERVER['PHP_SELF']?>" class="btn btn-default"><?=gettext("Back")?></a> + </nav> + </form> + +<?php + break; + } + + // Info on devices + case 'info': + { + $type = $_POST['type']; + + if (!in_array($type, $valid_info_types)) { + print_info_box(gettext("Invalid info type, bailing."), 'danger'); + return; + } + + $output = add_colors(shell_exec($smartctl . " -" . escapeshellarg($type) . " /dev/" . escapeshellarg($targetdev))); +?> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Information')?></h2></div> + <div class="panel-body"> + <pre><?=$output?></pre> + </div> + </div> + + <nav class="action-buttons"> + <a href="<?=$_SERVER['PHP_SELF']?>" class="btn btn-default"><?=gettext("Back")?></a> + </nav> +<?php + break; + } + + // View logs + case 'logs': + { + $type = $_POST['type']; + if (!in_array($type, $valid_log_types)) { + print_info_box(gettext("Invalid log type, bailing."), 'danger'); + return; + } + + $output = add_colors(shell_exec($smartctl . " -l " . escapeshellarg($type) . " /dev/" . escapeshellarg($targetdev))); +?> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Logs')?></h2></div> + <div class="panel-body"> + <pre><?=$output?></pre> + </div> + </div> + + <nav class="action-buttons"> + <a href="<?=$_SERVER['PHP_SELF']?>" class="btn btn-default"><?=gettext("Back")?></a> + </nav> +<?php + break; + } + + // Abort tests + case 'abort': + { + $output = shell_exec($smartctl . " -X /dev/" . escapeshellarg($targetdev)); +?> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Abort')?></h2></div> + <div class="panel-body"> + <pre><?=$output?></pre> + </div> + </div> +<?php + break; + } + + // Config changes, users email in xml config and write changes to smartd.conf + case 'config': + { + if(isset($_POST['test'])) { + +// FIXME shell_exec($smartd . " -M test -m " . $config['system']['smartmonemail']); + $savemsg = sprintf(gettext("Email sent to %s"), $config['system']['smartmonemail']); + smartmonctl("stop"); + smartmonctl("start"); + $style = 'warning'; + } + else if(isset($_POST['save'])) + { + $config['system']['smartmonemail'] = $_POST['smartmonemail']; + write_config(); + + // Don't know what all this means, but it adds the config changed header when config is saved + $retval = 0; + config_lock(); + if(stristr($retval, "error") != true) { + $savemsg = get_std_save_message($retval); + $style = 'success'; + } + else { + $savemsg = $retval; + $style='danger'; + } + + config_unlock(); + + // Write the changes to the smartd.conf file + update_email($_POST['smartmonemail']); + + // Send sig HUP to smartd, rereads the config file + shell_exec("/usr/bin/killall -HUP smartd"); + } + + // Was the config changed? if so , print the message + if ($savemsg) + print_info_box($savemsg, $style); + + // Get users email from the xml file + $pconfig['smartmonemail'] = $config['system']['smartmonemail']; + + $form = new Form(); + + $section = new Form_Section('Configuration'); + + $section->addInput(new Form_Input( + 'smartmonemail', + 'Email Address', + 'text', + $pconfig['smartmonemail'] + )); + + $form->add($section); + + if(!empty($pconfig['smartmonemail'])) { + $form->addGlobal(new Form_Button( + 'test', + 'Send test email' + ))->removeClass('btn-primary')->addClass('btn-default'); + } + + print($form); + + break; + } + + // Default page, prints the forms to view info, test, etc... + default: { +// Information + $devs = get_smart_drive_list(); + + $form = new Form(new Form_Button( + 'submit', + 'View' + )); + + $section = new Form_Section('Information'); + + $section->addInput(new Form_Input( + 'action', + null, + 'hidden', + 'info' + )); + + $group = new Form_Group('Info type'); + + $group->add(new Form_Checkbox( + 'type', + null, + 'Info', + false, + 'i' + ))->displayAsRadio(); + + $group->add(new Form_Checkbox( + 'type', + null, + 'Health', + true, + 'H' + ))->displayAsRadio(); + + $group->add(new Form_Checkbox( + 'type', + null, + 'SMART Capabilities', + false, + 'c' + ))->displayAsRadio(); + + $group->add(new Form_Checkbox( + 'type', + null, + 'Attributes', + false, + 'A' + ))->displayAsRadio(); + + $group->add(new Form_Checkbox( + 'type', + null, + 'All', + false, + 'a' + ))->displayAsRadio(); + + $section->add($group); + + $section->addInput(new Form_Select( + 'device', + 'Device: /dev/', + false, + array_combine($devs, $devs) + )); + + $form->add($section); + print($form); + +// Tests + $form = new Form(new Form_Button( + 'submit', + 'Test' + )); + + $section = new Form_Section('Perform self-tests'); + + $section->addInput(new Form_Input( + 'action', + null, + 'hidden', + 'test' + )); + + $group = new Form_Group('Test type'); + + $group->add(new Form_Checkbox( + 'testType', + null, + 'Offline', + false, + 'offline' + ))->displayAsRadio(); + + $group->add(new Form_Checkbox( + 'testType', + null, + 'Short', + true, + 'short' + ))->displayAsRadio(); + + $group->add(new Form_Checkbox( + 'testType', + null, + 'Long', + false, + 'long' + ))->displayAsRadio(); + + $group->add(new Form_Checkbox( + 'testType', + null, + 'Conveyance', + false, + 'conveyance' + ))->displayAsRadio(); + + $group->setHelp('Select "Conveyance" for ATA disks only'); + $section->add($group); + + $section->addInput(new Form_Select( + 'device', + 'Device: /dev/', + false, + array_combine($devs, $devs) + )); + + $form->add($section); + print($form); + +// Logs + $form = new Form(new Form_Button( + 'submit', + 'View' + )); + + $section = new Form_Section('View logs'); + + $section->addInput(new Form_Input( + 'action', + null, + 'hidden', + 'logs' + )); + + $group = new Form_Group('Log type'); + + $group->add(new Form_Checkbox( + 'type', + null, + 'Error', + true, + 'error' + ))->displayAsRadio(); + + $group->add(new Form_Checkbox( + 'test', + null, + 'Self-test', + false, + 'selftest' + ))->displayAsRadio(); + + $section->add($group); + + $section->addInput(new Form_Select( + 'device', + 'Device: /dev/', + false, + array_combine($devs, $devs) + )); + + $form->add($section); + print($form); + +// Abort + $btnabort = new Form_Button( + 'submit', + 'Abort' + ); + + $btnabort->removeClass('btn-primary')->addClass('btn-danger'); + + $form = new Form($btnabort); + + $section = new Form_Section('Abort'); + + $section->addInput(new Form_Input( + 'action', + null, + 'hidden', + 'abort' + )); + + $section->addInput(new Form_Select( + 'device', + 'Device: /dev/', + false, + array_combine($devs, $devs) + )); + + $form->add($section); + print($form); + + break; + } +} + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_sockets.php b/src/usr/local/www/diag_sockets.php new file mode 100644 index 0000000..84c9642 --- /dev/null +++ b/src/usr/local/www/diag_sockets.php @@ -0,0 +1,174 @@ +<?php + +/* $Id$ */ +/* + diag_sockets.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /usr/bin/sockstat +*/ +##|+PRIV +##|*IDENT=page-diagnostics-sockets +##|*NAME=Diagnostics: Sockets page +##|*DESCR=Allow access to the 'Diagnostics: Sockets' page. +##|*MATCH=diag_sockets.php* +##|-PRIV + +include('guiconfig.inc'); + +$pgtitle = array(gettext("Diagnostics"), gettext("Sockets")); + +include('head.inc'); + +?> + +<?php include("fbegin.inc"); + +$showAll = isset($_GET['showAll']); +$showAllText = $showAll ? "Show only listening sockets" : "Show all socket connections"; +$showAllOption = $showAll ? "" : "?showAll"; + +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">System socket information for both IPv4 and IPv6</h2></div> + <div class="panel-body"> + +(Click <a href="#about">here </a>for explanation of the information listed for each socket) <br /><br /> +<input class="btn btn-info btn-xs" type="button" value="<?=$showAllText?>" onclick="window.location.href='diag_sockets.php<?=$showAllOption?>'"/> + + +<?php + if (isset($_GET['showAll'])) { + $internet4 = shell_exec('sockstat -4'); + $internet6 = shell_exec('sockstat -6'); + } else { + $internet4 = shell_exec('sockstat -4l'); + $internet6 = shell_exec('sockstat -6l'); + } + foreach (array(&$internet4, &$internet6) as $tabindex => $table) { + $elements = ($tabindex == 0 ? 7 : 7); + $name = ($tabindex == 0 ? 'IPv4' : 'IPv6'); +?> + <div class="table table-responsive"> + <table class="table table-hover table-striped table-condensed"> + <thead> + <tr> + <th> + <?=$name?> + </th> + </tr> + </thead> + <tbody> + +<?php + foreach (explode("\n", $table) as $i => $line) { + if ($i == 0) + $class = 'info'; + else + $class = ''; + + if (trim($line) == "") + continue; + + print("<tr>\n"); + $j = 0; + foreach (explode(' ', $line) as $entry) { + if ($entry == '' || $entry == "ADDRESS") continue; + if ($i == 0) + print("<th class=\"$class\">$entry</th>\n"); + else + print("<td class=\"$class\">$entry</td>\n"); + + $j++; + } + print("</tr>\n"); + } +?> + </tbody> + </table> + </div> +<?php + } +?> + </div> + <a name="about"></a> + <div class="alert alert-success" role="alert"> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">Socket information - explanation</h2></div> + <div class="panel-body"> + This page show the output for the commands: "sockstat -4lL" and "sockstat -6lL".<br /> + Or in case of showing all sockets the output for: "sockstat -4" and "sockstat -6".<br /> + <br /> + The information listed for each socket is: + <br /><br /> + <dl class="dl-horizontal responsive"> + <dt>USER</dt> <dd>The user who owns the socket.</dd> + <dt>COMMAND</dt> <dd>The command which holds the socket.</dd> + <dt>PID</dt> <dd>The process ID of the command which holds the socket.</dd> + <dt>FD</dt> <dd>The file descriptor number of the socket.</dd> + <dt>PROTO</dt> <dd>The transport protocol associated with the socket for Internet sockets, or the type of socket (stream or data-gram) for UNIX sockets.</dd> + <dt>ADDRESS</dt> <dd>(UNIX sockets only) For bound sockets, this is the file-name of the socket. For other sockets, it is the name, PID and file descriptor number of the peer, or ``(none)'' if the socket is neither bound nor connected.</dd> + <dt>LOCAL ADDRESS</dt> <dd>(Internet sockets only) The address the local end of the socket is bound to (see getsockname(2)).</dd> + <dt>FOREIGN ADDRESS</dt><dd>(Internet sockets only) The address the foreign end of the socket is bound to (see getpeername(2)).</dd> + </dl> + </div> + </div> + </div> +</div> +<?php + +include('foot.inc'); + + diff --git a/src/usr/local/www/diag_states_summary.php b/src/usr/local/www/diag_states_summary.php new file mode 100644 index 0000000..587ddcd --- /dev/null +++ b/src/usr/local/www/diag_states_summary.php @@ -0,0 +1,235 @@ +<?php +/* + diag_states_summary.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2005-2009 Scott Ullrich + * Copyright (c) 2005 Colin Smith + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /sbin/pfctl + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-statessummary +##|*NAME=Diagnostics: States Summary page +##|*DESCR=Allow access to the 'Diagnostics: States Summary' page. +##|*MATCH=diag_states_summary.php* +##|-PRIV + +exec("/sbin/pfctl -s state", $states); + +$srcipinfo = array(); +$dstipinfo = array(); +$allipinfo = array(); +$pairipinfo = array(); + +function addipinfo(&$iparr, $ip, $proto, $srcport, $dstport) { + $iparr[$ip]['seen']++; + $iparr[$ip]['protos'][$proto]['seen']++; + if (!empty($srcport)) { + $iparr[$ip]['protos'][$proto]['srcports'][$srcport]++; + } + if (!empty($dstport)) { + $iparr[$ip]['protos'][$proto]['dstports'][$dstport]++; + } +} + +$row = 0; +if (count($states) > 0) { + foreach ($states as $line) { + $line_split = preg_split("/\s+/", $line); + $iface = array_shift($line_split); + $proto = array_shift($line_split); + $state = array_pop($line_split); + $info = implode(" ", $line_split); + + /* Handle NAT cases + Replaces an external IP + NAT by the internal IP */ + if (strpos($info, ') ->') !== FALSE) { + /* Outbound NAT */ + $info = preg_replace('/(\S+) \((\S+)\)/U', "$2", $info); + } elseif (strpos($info, ') <-') !== FALSE) { + /* Inbound NAT/Port Forward */ + $info = preg_replace('/(\S+) \((\S+)\)/U', "$1", $info); + } + + /* break up info and extract $srcip and $dstip */ + $ends = preg_split("/\<?-\>?/", $info); + + if (strpos($info, '->') === FALSE) { + $srcinfo = $ends[count($ends) - 1]; + $dstinfo = $ends[0]; + } else { + $srcinfo = $ends[0]; + $dstinfo = $ends[count($ends) - 1]; + } + + /* Handle IPv6 */ + $parts = explode(":", $srcinfo); + $partcount = count($parts); + if ($partcount <= 2) { + $srcip = trim($parts[0]); + $srcport = trim($parts[1]); + } else { + preg_match("/([0-9a-f:]+)(\[([0-9]+)\])?/i", $srcinfo, $matches); + $srcip = $matches[1]; + $srcport = trim($matches[3]); + } + + $parts = explode(":", $dstinfo); + $partcount = count($parts); + if ($partcount <= 2) { + $dstip = trim($parts[0]); + $dstport = trim($parts[1]); + } else { + preg_match("/([0-9a-f:]+)(\[([0-9]+)\])?/i", $dstinfo, $matches); + $dstip = $matches[1]; + $dstport = trim($matches[3]); + } + + addipinfo($srcipinfo, $srcip, $proto, $srcport, $dstport); + addipinfo($dstipinfo, $dstip, $proto, $srcport, $dstport); + addipinfo($pairipinfo, "{$srcip} -> {$dstip}", $proto, $srcport, $dstport); + + addipinfo($allipinfo, $srcip, $proto, $srcport, $dstport); + addipinfo($allipinfo, $dstip, $proto, $srcport, $dstport); + } +} + +function sort_by_ip($a, $b) { + return ip2ulong($a) < ip2ulong($b) ? -1 : 1; +} + +function build_port_info($portarr, $proto) { + if (!$portarr) { + return ''; + } + $ports = array(); + asort($portarr); + foreach (array_reverse($portarr, TRUE) as $port => $count) { + $service = getservbyport($port, strtolower($proto)); + $port = "{$proto}/{$port}"; + if ($service) { + $port = "{$port} ({$service})"; + } + $ports[] = "{$port}: {$count}"; + } + return implode($ports, ', '); +} + +function print_summary_table($label, $iparr, $sort = TRUE) +{ + if ($sort) + uksort($iparr, "sort_by_ip"); + +?> + <div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title"><?=$label?></h2> + </div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-hover table-condensed table-striped"> + <thead> + <tr> + <th ><?=gettext("IP");?></th> + <th class="text-center"># <?=gettext("States");?></th> + <th ><?=gettext("Proto");?></th> + <th class="text-center"># <?=gettext("States");?></th> + <th class="text-center"><?=gettext("Src Ports");?></th> + <th class="text-center"><?=gettext("Dst Ports");?></th> + </tr> + </thead> + <tbody> +<?php foreach($iparr as $ip => $ipinfo): + $protocolCount = count($ipinfo['protos']); + $rowSpan = ''; + $i = 0; + + if ($protocolCount > 1) + $rowSpan = ' rowspan="' . $protocolCount . '"'; +?> + <tr> + <td<?= $rowSpan ?>><?php echo $ip; ?></td> + <td<?= $rowSpan ?> class="text-center"><?php echo $ipinfo['seen']; ?></td> + +<?php foreach($ipinfo['protos'] as $proto => $protoinfo): ?> +<?php if ($protocolCount > 1 && $i > 0): ?> + </tr><tr> +<?php endif; ?> + <td><?php echo $proto; ?></td> + <td class="text-center" ><?php echo $protoinfo['seen']; ?></td> + <td class="text-center" ><span title="<?php echo build_port_info($protoinfo['srcports'], $proto); ?>"><?php echo count($protoinfo['srcports']); ?></span></td> + <td class="text-center" ><span title="<?php echo build_port_info($protoinfo['dstports'], $proto); ?>"><?php echo count($protoinfo['dstports']); ?></span></td> +<?php $i++; endforeach; ?> + </tr> +<?php endforeach; ?> + </tbody> + </table> + </div> + </div> + </div> +<?php +} + +$pgtitle = array(gettext("Diagnostics"), gettext("State Table Summary")); +require_once("guiconfig.inc"); +include("head.inc"); + +print_summary_table(gettext("By Source IP"), $srcipinfo); +print_summary_table(gettext("By Destination IP"), $dstipinfo); +print_summary_table(gettext("Total per IP"), $allipinfo); +print_summary_table(gettext("By IP Pair"), $pairipinfo, FALSE); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_system_activity.php b/src/usr/local/www/diag_system_activity.php new file mode 100644 index 0000000..488f69f --- /dev/null +++ b/src/usr/local/www/diag_system_activity.php @@ -0,0 +1,116 @@ +<?php +/* $Id$ */ +/* + diag_system_activity.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2008, 2009 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /usr/bin/top + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-system-activity +##|*NAME=Diagnostics: System Activity +##|*DESCR=Allows access to the 'Diagnostics: System Activity' page +##|*MATCH=diag_system_activity.php* +##|-PRIV + +require("guiconfig.inc"); + +$pgtitle = array(gettext("Diagnostics"),gettext("System Activity")); + +if ($_REQUEST['getactivity']) { + $text = `/usr/bin/top -aHS | /usr/bin/cut -c1-105`; + echo $text; + exit; +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +?> +<script> +function getcpuactivity() { + $.ajax( + '/diag_system_activity.php', + { + method: 'post', + data: { + getactivity: 'yes' + }, + dataType: "html", + success: function (data) { + $('#xhrOutput').html(data); + }, + } + ); +} + +events.push(function(){ + setInterval('getcpuactivity()', 2500); + getcpuactivity(); +}); +</script> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('CPU Activity')?></h2></div> + <div class="panel panel-body"> + <pre id="xhrOutput"><?=gettext("Gathering CPU activity, please wait...")?></pre> + </div> +</div> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_system_pftop.php b/src/usr/local/www/diag_system_pftop.php new file mode 100644 index 0000000..be2a493 --- /dev/null +++ b/src/usr/local/www/diag_system_pftop.php @@ -0,0 +1,216 @@ +<?php +/* $Id$ */ +/* + diag_system_pftop.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2008, 2009 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-system-pftop +##|*NAME=Diagnostics: pfTop +##|*DESCR=Allows access to the 'Diagnostics: pfTop' page +##|*MATCH=diag_system_pftop.php* +##|-PRIV + +require("guiconfig.inc"); + +$pgtitle = gettext("Diagnostics: pfTop"); + +$sorttypes = array('age', 'bytes', 'dest', 'dport', 'exp', 'none', 'peak', 'pkt', 'rate', 'size', 'sport', 'src'); +$viewtypes = array('default', 'label', 'long', 'queue', 'rules', 'size', 'speed', 'state', 'time'); +$viewall = array('queue', 'label', 'rules'); +$numstates = array('50', '100', '200', '500', '1000', 'all'); + +if ($_REQUEST['getactivity']) { + if ($_REQUEST['sorttype'] && in_array($_REQUEST['sorttype'], $sorttypes) && + $_REQUEST['viewtype'] && in_array($_REQUEST['viewtype'], $viewtypes) && + $_REQUEST['states'] && in_array($_REQUEST['states'], $numstates)) { + $viewtype = escapeshellarg($_REQUEST['viewtype']); + if (in_array($_REQUEST['viewtype'], $viewall)) { + $sorttype = ""; + $numstate = "-a"; + } else { + $sorttype = "-o " . escapeshellarg($_REQUEST['sorttype']); + $numstate = ($_REQUEST['states'] == "all" ? "-a" : escapeshellarg($_REQUEST['states'])); + } + } else { + $sorttype = "bytes"; + $viewtype = "default"; + $numstate = "100"; + } + + $text = `pftop -b {$sorttype} -v {$viewtype} {$numstate}`; + echo trim($text); + exit; +} + +include("head.inc"); + +if ($_REQUEST['sorttype'] && in_array($_REQUEST['sorttype'], $sorttypes) && + $_REQUEST['viewtype'] && in_array($_REQUEST['viewtype'], $viewtypes) && + $_REQUEST['states'] && in_array($_REQUEST['states'], $numstates)) { + $viewtype = escapeshellarg($_REQUEST['viewtype']); + if (in_array($_REQUEST['viewtype'], $viewall)) { + $sorttype = ""; + $numstate = "-a"; + } else { + $sorttype = "-o " . escapeshellarg($_REQUEST['sorttype']); + $numstate = ($_REQUEST['states'] == "all" ? "-a" : escapeshellarg($_REQUEST['states'])); + } +} else { + $sorttype = "bytes"; + $viewtype = "default"; + $numstate = "100"; +} + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); +$form = new Form(false); +$form->addGlobal(new Form_Input( + 'getactivity', + null, + 'hidden', + 'yes' +)); +$section = new Form_Section('pfTop Configuration'); + +$validViews = array( + 'default', 'label', 'long', + 'queue', 'rules', 'size', + 'speed', 'state', 'time', +); +$section->addInput(new Form_Select( + 'viewtype', + 'View', + $viewtype, + array_combine($validViews, $validViews) +)); + +$section->addInput(new Form_Select( + 'sorttype', + 'Sort by', + $sorttype, + array( + 'none' => 'None', + 'age' => 'Age', + 'bytes' => 'Bytes', + 'dest' => 'Destination Address', + 'dport' => 'Destination Port', + 'exp' => 'Expiry', + 'peak' => 'Peak', + 'pkt' => 'Packet', + 'rate' => 'Rate', + 'size' => 'Size', + 'sport' => 'Source Port', + 'src' => 'Source Address', + ) +)); + +$validStates = array(50, 100, 200, 500, 100, 'all'); +$section->addInput(new Form_Select( + 'states', + 'Maximum # of States', + $numstate, + array_combine($validStates, $validStates) +)); + +$form->add($section); +print $form; +?> + +<script> + function getpftopactivity() { + $.ajax( + '/diag_system_pftop.php', + { + method: 'post', + data: $(document.forms[0]).serialize(), + dataType: "html", + success: function (data) { + $('#xhrOutput').html(data); + }, + } + ); + } + + events.push(function(){ + setInterval('getpftopactivity()', 2500); + getpftopactivity(); + }); +</script> +<?php +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Output')?></h2></div> + <div class="panel panel-body"> + <pre id="xhrOutput"><?=gettext("Gathering pfTOP activity, please wait...")?></pre> + </div> +</div> + +<script> +events.push(function(){ + $('#viewtype').on('change', function(){ + if (['queue', 'label', 'rules'].indexOf($(this).val()) > -1) + $("#sorttype, #sorttypediv, #statesdiv, #states").parents('.form-group').hide(); + else + $("#sorttype, #sorttypediv, #statesdiv, #states").parents('.form-group').show(); + }); +}); +</script> +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_tables.php b/src/usr/local/www/diag_tables.php new file mode 100644 index 0000000..762b83e --- /dev/null +++ b/src/usr/local/www/diag_tables.php @@ -0,0 +1,227 @@ +<?php +/* + diag_tables.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2010 Jim Pingle + * Portions borrowed from diag_dump_states.php + * Copyright (c) 2010 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /sbin/pfctl + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-tables +##|*NAME=Diagnostics: PF Table IP addresses +##|*DESCR=Allow access to the 'Diagnostics: Tables' page. +##|*MATCH=diag_tables.php* +##|-PRIV + +$pgtitle = array(gettext("Diagnostics"), gettext("Tables")); +$shortcut_section = "aliases"; + +require_once("guiconfig.inc"); + +// Set default table +$tablename = "sshlockout"; +$bogons = false; + +if ($_REQUEST['type']) { + $tablename = $_REQUEST['type']; +} + +if ($_REQUEST['delete']) { + if (is_ipaddr($_REQUEST['delete']) || is_subnet($_REQUEST['delete'])) { + exec("/sbin/pfctl -t " . escapeshellarg($_REQUEST['type']) . " -T delete " . escapeshellarg($_REQUEST['delete']), $delete); + echo htmlentities($_REQUEST['delete']); + } + exit; +} + +if($_POST['deleteall']) { + exec("/sbin/pfctl -t " . escapeshellarg($tablename) . " -T show", $entries); + if (is_array($entries)) { + foreach ($entries as $entryA) { + $entry = trim($entryA); + exec("/sbin/pfctl -t " . escapeshellarg($tablename) . " -T delete " . escapeshellarg($entry), $delete); + } + } +} + +if(($tablename == "bogons") || ($tablename == "bogonsv6")) { + $bogons = true; + + if($_POST['Download']) { + mwexec_bg("/etc/rc.update_bogons.sh now"); + $maxtimetowait = 0; + $loading = true; + while($loading == true) { + $isrunning = `/bin/ps awwwux | /usr/bin/grep -v grep | /usr/bin/grep bogons`; + if($isrunning == "") + $loading = false; + $maxtimetowait++; + if($maxtimetowait > 89) + $loading = false; + sleep(1); + } + if($maxtimetowait < 90) + $savemsg = gettext("The bogons database has been updated."); + } +} + +exec("/sbin/pfctl -t " . escapeshellarg($tablename) . " -T show", $entries); +exec("/sbin/pfctl -sT", $tables); + +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg); + +require('classes/Form.class.php'); +$form = new Form('Show'); + +$section = new Form_Section('Table to display'); + +$section->addInput(new Form_Select( + 'type', + 'Table', + $tablename, + array_combine($tables, $tables) +)); + +$form->add($section); +print $form; +?> + +<script> +events.push(function(){ + $('a[data-entry]').on('click', function(){ + var el = $(this); + + $.ajax( + '/diag_tables.php', + { + type: 'post', + data: { + type: '<?=htmlspecialchars($tablename)?>', + delete: $(this).data('entry') + }, + success: function(){ + el.parents('tr').remove(); + }, + }); + }); +}); +</script> + +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("IP Address")?></th> + </tr> + </thead> + <tbody> +<?php + foreach ($entries as $entry): + $entry = trim($entry); +?> + <tr> + <td> + <?=$entry?> + </td> + <td> + <?php if (!$bogons): ?> + <a class="btn btn-xs btn-default" data-entry="<?=htmlspecialchars($entry)?>">Remove</a> + <?php endif ?> + </td> + </tr> +<?php endforeach ?> + </tbody> + </table> +</div> +<?php if (empty($entries)): ?> + <div class="alert alert-warning" role="alert">No entries exist in this table</div> +<?php endif ?> + +<?php + +if ($bogons || !empty($entries)) { + $form = new Form; + + $section = new Form_Section('Table Data'); + + if ($bogons) { + $last_updated = exec('/usr/bin/grep -i -m 1 -E "^# last updated" /etc/' . escapeshellarg($tablename) . '|cut -d"(" -f2|tr -d ")" '); + + $section->addInput(new Form_StaticText( + 'Last update', + $last_updated + )); + + $section->addInput(new Form_Button( + 'Download', + 'Download' + ))->setHelp('Download the latest bogon data')->addClass('btn-warning'); + } elseif (!empty($entries)) { + $section->addInput(new Form_Button( + 'deleteall', + 'Clear Table' + ))->setHelp('Clear all of the entries in this table')->addClass('btn-danger'); + } + + $form->add($section); + print $form; +} + +include("foot.inc"); diff --git a/src/usr/local/www/diag_testport.php b/src/usr/local/www/diag_testport.php new file mode 100644 index 0000000..5ff0140 --- /dev/null +++ b/src/usr/local/www/diag_testport.php @@ -0,0 +1,304 @@ +<?php +/* + diag_testport.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2013 Jim P (jimp@pfsense.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /usr/bin/nc + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-testport +##|*NAME=Diagnostics: Test Port +##|*DESCR=Allow access to the 'Diagnostics: Test Port' page. +##|*MATCH=diag_testport.php* +##|-PRIV + +// Calling netcat and parsing hte results has been moved to the if ($_POST) section so that the results are known +// before we draw the form and any resulting error messages will allear in the correct place + +$allowautocomplete = true; + +$pgtitle = array(gettext("Diagnostics"), gettext("Test Port")); +require("guiconfig.inc"); + +define('NC_TIMEOUT', 10); +$do_testport = false; +$retval = 1; + +if ($_POST || $_REQUEST['host']) { + unset($input_errors); + + /* input validation */ + $reqdfields = explode(" ", "host port"); + $reqdfieldsn = array(gettext("Host"), gettext("Port")); + do_input_validation($_REQUEST, $reqdfields, $reqdfieldsn, $input_errors); + + if (!is_ipaddr($_REQUEST['host']) && !is_hostname($_REQUEST['host'])) { + $input_errors[] = gettext("Please enter a valid IP or hostname."); + } + + if (!is_port($_REQUEST['port'])) { + $input_errors[] = gettext("Please enter a valid port number."); + } + + if (($_REQUEST['srcport'] != "") && (!is_numeric($_REQUEST['srcport']) || !is_port($_REQUEST['srcport']))) { + $input_errors[] = gettext("Please enter a valid source port number, or leave the field blank."); + } + + if (is_ipaddrv4($_REQUEST['host']) && ($_REQUEST['ipprotocol'] == "ipv6")) { + $input_errors[] = gettext("You cannot connect to an IPv4 address using IPv6."); + } + if (is_ipaddrv6($_REQUEST['host']) && ($_REQUEST['ipprotocol'] == "ipv4")) { + $input_errors[] = gettext("You cannot connect to an IPv6 address using IPv4."); + } + + if (!$input_errors) { + $do_testport = true; + $timeout = NC_TIMEOUT; + } + + /* Save these request vars even if there were input errors. Then the fields are refilled for the user to correct. */ + $host = $_REQUEST['host']; + $sourceip = $_REQUEST['sourceip']; + $port = $_REQUEST['port']; + $srcport = $_REQUEST['srcport']; + $showtext = isset($_REQUEST['showtext']); + $ipprotocol = $_REQUEST['ipprotocol']; + + if ( $do_testport ) { +?> + <script type="text/javascript"> + //<![CDATA[ + window.onload=function(){ + document.getElementById("testportCaptured").wrap='off'; + } + //]]> + </script> +<?php + $result = ""; + $ncoutput = ""; + $nc_base_cmd = '/usr/bin/nc'; + $nc_args = "-w " . escapeshellarg($timeout); + if (!$showtext) + $nc_args .= ' -z '; + if (!empty($srcport)) + $nc_args .= ' -p ' . escapeshellarg($srcport) . ' '; + + /* Attempt to determine the interface address, if possible. Else try both. */ + if (is_ipaddrv4($host)) { + $ifaddr = ($sourceip == "any") ? "" : get_interface_ip($sourceip); + $nc_args .= ' -4'; + } elseif (is_ipaddrv6($host)) { + if ($sourceip == "any") + $ifaddr = ''; + else if (is_linklocal($sourceip)) + $ifaddr = $sourceip; + else + $ifaddr = get_interface_ipv6($sourceip); + $nc_args .= ' -6'; + } else { + switch ($ipprotocol) { + case "ipv4": + $ifaddr = get_interface_ip($sourceip); + $nc_ipproto = ' -4'; + break; + case "ipv6": + $ifaddr = (is_linklocal($sourceip) ? $sourceip : get_interface_ipv6($sourceip)); + $nc_ipproto = ' -6'; + break; + case "any": + $ifaddr = get_interface_ip($sourceip); + $nc_ipproto = (!empty($ifaddr)) ? ' -4' : ''; + if (empty($ifaddr)) { + $ifaddr = (is_linklocal($sourceip) ? $sourceip : get_interface_ipv6($sourceip)); + $nc_ipproto = (!empty($ifaddr)) ? ' -6' : ''; + } + break; + } + /* Netcat doesn't like it if we try to connect using a certain type of IP without specifying the family. */ + if (!empty($ifaddr)) { + $nc_args .= $nc_ipproto; + } elseif ($sourceip == "any") { + switch ($ipprotocol) { + case "ipv4": + $nc_ipproto = ' -4'; + break; + case "ipv6": + $nc_ipproto = ' -6'; + break; + } + $nc_args .= $nc_ipproto; + } + } + /* Only add on the interface IP if we managed to find one. */ + if (!empty($ifaddr)) { + $nc_args .= ' -s ' . escapeshellarg($ifaddr) . ' '; + $scope = get_ll_scope($ifaddr); + if (!empty($scope) && !strstr($host, "%")) + $host .= "%{$scope}"; + } + + $nc_cmd = "{$nc_base_cmd} {$nc_args} " . escapeshellarg($host) . ' ' . escapeshellarg($port) . ' 2>&1'; + exec($nc_cmd, $result, $retval); + // echo "NC CMD: {$nc_cmd}\n\n"; + + if (!empty($result)) { + if (is_array($result)) { + foreach ($result as $resline) { + $ncoutput .= htmlspecialchars($resline) . "\n"; + } + } else { + $ncoutput .= htmlspecialchars($result); + } + } + } +} + +include("head.inc"); + +// Handle the display of all messages here wher the user can readily see them +if ($input_errors) + print_input_errors($input_errors); +else { + // New page + if(empty($result) && $retval != 0 && !$showtext) { + print('<div class="alert alert-warning" role="alert">This page allows you to perform a simple TCP connection test to determine if a host is up and accepting connections on a given port.' . + ' This test does not function for UDP since there is no way to reliably determine if a UDP port accepts connections in this manner.</div>'); + } + + // Good host & port + if($retval == 0 && $do_testport == 1) { + if(!$showtext) + print('<div class="alert alert-success" role="alert">'.gettext("Port test to host: " . $host . " Port: " . $port . " successful").'</div>'); + else + print('<div class="alert alert-success" role="alert">'.gettext("Port test to host: " . $host . " Port: " . $port . " successful") . '. Any text received from the host will be shown below the form.</div>'); + } + + // netcat exit value != 0 + if($retval != 0 && !empty($result)) + if($showtext) + print('<div class="alert alert-danger" role="alert">'.gettext('No output received, or connection failed. Try with "Show Remote Text" unchecked first.').'</div>'); + else + print('<div class="alert alert-danger" role="alert">'.gettext('Connection failed.').'</div>'); +} + +require('classes/Form.class.php'); + +$form = new Form('Test'); + +$section = new Form_Section('Test Port'); + +$section->addInput(new Form_Input( + 'host', + 'Hostname', + 'text', + $host, + ['placeholder' => 'Hostname to look up.'] +)); + +$section->addInput(new Form_Input( + 'port', + 'Port', + 'text', + $port, + ['placeholder' => 'Port to test.'] +)); + +$section->addInput(new Form_Input( + 'srcport', + 'Source Port', + 'text', + $srcport, + ['placeholder' => 'Typically left blank.'] +)); + +$section->addInput(new Form_Checkbox( + 'showtext', + 'Remote text', + 'Show remote text', + $showtext +))->setHelp("Shows the text given by the server when connecting to the port. If checked it will take 10+ seconds to display in a panel below this form."); + +$section->addInput(new Form_Select( + 'sourceip', + 'Source Address', + $sourceip, + array_merge(array('' => 'Any'), get_possible_traffic_source_addresses(true)) +))->setHelp('Select source address for the trace'); + +$section->addInput(new Form_Select( + 'ipproto', + 'IP Protocol', + $ipprotocol, + array('ipv4' => 'IPv4', 'ipv6' => 'IPv6') +))->setHelp("If you force IPv4 or IPv6 and use a hostname that does not contain a result using that protocol, it will result in an error." . + " For example if you force IPv4 and use a hostname that only returns an AAAA IPv6 IP address, it will not work."); + +$form->add($section); +print $form; + +if($ncoutput && !empty($result) && $showtext && $retval == 0): ?> + <div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title">Received Remote Text</h2> + </div> + <div class="panel-body"> + <pre><?= $ncoutput ?></pre> + </div> + </div> +<?php endif; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/diag_traceroute.php b/src/usr/local/www/diag_traceroute.php new file mode 100644 index 0000000..f82b963 --- /dev/null +++ b/src/usr/local/www/diag_traceroute.php @@ -0,0 +1,225 @@ +<?php +/* + diag_traceroute.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2005 Paul Taylor (paultaylor@winndixie.com) and Manuel Kasper <mk@neon1.net> + * part of m0n0wall (http://m0n0.ch/wall) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* + pfSense_BUILDER_BINARIES: /usr/sbin/traceroute + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-traceroute +##|*NAME=Diagnostics: Traceroute page +##|*DESCR=Allow access to the 'Diagnostics: Traceroute' page. +##|*MATCH=diag_traceroute.php* +##|-PRIV + +require("guiconfig.inc"); + +$allowautocomplete = true; +$pgtitle = array(gettext("Diagnostics"), gettext("Traceroute")); +include("head.inc"); + +define('MAX_TTL', 64); +define('DEFAULT_TTL', 18); + +$pconfig['ttl'] = DEFAULT_TTL; +$pconfig['ipproto'] = 'IPv4'; +$pconfig['sourceip'] = 'Any'; + +function create_sourceaddresslist() { + $list = array('any' => 'Any'); + + $sourceips = get_possible_traffic_source_addresses(true); + + foreach ($sourceips as $sipvalue => $sipname) + $list[$sipname[value]] = $sipname[name]; + + return($list); +} + +if ($_POST || $_REQUEST['host']) { + unset($input_errors); + unset($do_traceroute); + + /* input validation */ + $reqdfields = explode(" ", "host ttl"); + $reqdfieldsn = array(gettext("Host"), gettext("ttl")); + do_input_validation($_REQUEST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($_REQUEST['ttl'] < 1) || ($_REQUEST['ttl'] > MAX_TTL)) { + $input_errors[] = sprintf(gettext("Maximum number of hops must be between 1 and %s"), MAX_TTL); + } + $host = trim($_REQUEST['host']); + $ipproto = $_REQUEST['ipproto']; + if (($ipproto == "ipv4") && is_ipaddrv6($host)) { + $input_errors[] = gettext("When using IPv4, the target host must be an IPv4 address or hostname."); + } + if (($ipproto == "ipv6") && is_ipaddrv4($host)) { + $input_errors[] = gettext("When using IPv6, the target host must be an IPv6 address or hostname."); + } + + if (!$input_errors) + $host = $_REQUEST['host']; + + $sourceip = $_REQUEST['sourceip']; + $do_traceroute = true; + $ttl = $_REQUEST['ttl']; + $resolve = $_REQUEST['resolve']; + $useicmp = $_REQUEST['useicmp']; + +} else { + $resolve = false; + $useicmp = false; +} + +if (!isset($do_traceroute)) { + $do_traceroute = false; + $host = ''; + $ttl = DEFAULT_TTL; +} + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form('Traceroute'); + +$section = new Form_Section('Traceroute'); + +$section->addInput(new Form_Input( + 'host', + 'Hostname', + 'text', + $host, + ['placeholder' => 'Hostname to trace.'] +)); + +$section->addInput(new Form_Select( + 'ipproto', + 'IP Protocol', + $pconfig['protocol'], + array('ipv4' => 'IPv4', 'ipv6' => 'IPv6') +))->setHelp('Select the protocol to use'); + +$section->addInput(new Form_Select( + 'sourceip', + 'Source Address', + $pconfig['source'], + create_sourceaddresslist() +))->setHelp('Select source address for the trace'); + +$section->addInput(new Form_Select( + 'ttl', + 'Maximum nuber of hops', + $ttl, + array_combine(range(1, MAX_TTL), range(1, MAX_TTL)) +))->setHelp('Select the maximum number of network hops to trace'); + +$section->addInput(new Form_Checkbox( + 'resolve', + 'Reverse Address Lookup', + '', + $resolve +))->setHelp('When checked, traceroute will attempt to perform a PTR lookup to locate hostnames for hops along the path. Will slow down the process as it has to wait for DNS replies.'); + +$section->addInput(new Form_Checkbox( + 'useicmp', + gettext("Use ICMP"), + '', + $useicmp +))->setHelp('By default, traceroute uses UDP but that may be blocked by some routers. Check this box to use ICMP instead, which may succeed. '); + +$form->add($section); +print $form; + +/* Show the traceroute results */ +if (!$input_errors && $do_traceroute) { + + $useicmp = isset($_REQUEST['useicmp']) ? "-I" : ""; + $n = isset($resolve) ? "" : "-n"; + + $command = "/usr/sbin/traceroute"; + if ($ipproto == "ipv6") { + $command .= "6"; + $ifaddr = is_ipaddr($sourceip) ? $sourceip : get_interface_ipv6($sourceip); + } else { + $ifaddr = is_ipaddr($sourceip) ? $sourceip : get_interface_ip($sourceip); + } + + if ($ifaddr && (is_ipaddr($host) || is_hostname($host))) { + $srcip = "-s " . escapeshellarg($ifaddr); + } + + $cmd = "{$command} {$n} {$srcip} -w 2 {$useicmp} -m " . escapeshellarg($ttl) . " " . escapeshellarg($host); +?> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">Results</h2></div> + <div class="panel-body"> +<?php + if ($result = shell_exec($cmd)) + print(nl2br($result)); + else + print('Error: ' . $host . ' ' . gettext("could not be traced/resolved")); +?> + </div> + </div> +<?php +} + +include("foot.inc"); +?> diff --git a/src/usr/local/www/easyrule.php b/src/usr/local/www/easyrule.php new file mode 100644 index 0000000..9f58af0 --- /dev/null +++ b/src/usr/local/www/easyrule.php @@ -0,0 +1,115 @@ +<?php +/* + easyrule.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2009-2010 Jim Pingle (jpingle@gmail.com) + * Originally Sponsored By Anathematic @ pfSense Forums + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-firewall-easyrule +##|*NAME=Firewall: Easy Rule add/status page +##|*DESCR=Allow access to the 'Firewall: Easy Rule' add/status page. +##|*MATCH=easyrule.php* +##|-PRIV + +$pgtitle = gettext("Firewall: EasyRule"); +require_once("guiconfig.inc"); +require_once("easyrule.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +$retval = 0; +$message = ""; +$specialsrcdst = explode(" ", "any pptp pppoe l2tp openvpn"); + +if ($_GET && isset($_GET['action'])) { + switch ($_GET['action']) { + case 'block': + /* Check that we have a valid host */ + easyrule_parse_block($_GET['int'], $_GET['src'], $_GET['ipproto']); + break; + case 'pass': + easyrule_parse_pass($_GET['int'], $_GET['proto'], $_GET['src'], $_GET['dst'], $_GET['dstport'], $_GET['ipproto']); + break; + } +} + +if (stristr($retval, "error") == true) { + $message = $retval; +} + +include("head.inc"); ?> +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<table width="100%" border="0" cellpadding="0" cellspacing="0"> + <tr> + <td> +<?php if ($input_errors) print_input_errors($input_errors); ?> + +<?php if ($message) { ?> +<br /> +<?=gettext("Message"); ?>: <?php echo $message; ?> +<br /> +<?php } else { ?> +<?=gettext("This is the Easy Rule status page, mainly used to display errors when adding rules. " . +"If you are seeing this, there apparently was not an error, and you navigated to the " . +"page directly without telling it what to do"); ?>.<br /><br /> +<?=gettext("This page is meant to be called from the block/pass buttons on the Firewall Logs page"); ?>, <a href="diag_logs_filter.php"><?=gettext("Status"); ?> > <?=gettext("System Logs, " . +"Firewall Tab"); ?></a>. +<br /> +<?php } ?> +</td></tr></table> +<?php include("fend.inc"); ?> diff --git a/src/usr/local/www/edit.php b/src/usr/local/www/edit.php new file mode 100644 index 0000000..aedf9ed --- /dev/null +++ b/src/usr/local/www/edit.php @@ -0,0 +1,214 @@ +<?php +/* + edit.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: shell +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-edit +##|*NAME=Diagnostics: Edit FIle +##|*DESCR=Allow access to the 'Diagnostics: Edit File' page. +##|*MATCH=edit.php* +##|*MATCH=browser.php* +##|*MATCH=filebrowser/browser.php* +##|-PRIV + +$pgtitle = array(gettext("Diagnostics"), gettext("Edit file")); +require("guiconfig.inc"); + +if ($_POST['action']) { + switch ($_POST['action']) { + case 'load': + if(strlen($_POST['file']) < 1) { + print('|5|' . '<div class="alert alert-danger" role="alert">'.gettext("No file name specified").'</div>' . '|'); + } elseif(is_dir($_POST['file'])) { + print('|4|' . '<div class="alert alert-danger" role="alert">' . gettext("Loading a directory is not supported") .'</div>' . '|'); + } elseif(! is_file($_POST['file'])) { + print('|3|' . '<div class="alert alert-danger" role="alert">' . gettext("File does not exist or is not a regular file") . '</div>' . '|'); + } else { + $data = file_get_contents(urldecode($_POST['file'])); + if($data === false) { + print('|1|' . '<div class="alert alert-danger" role="alert">' . gettext("Failed to read file") . '</div>' . '|'); + } else { + $data = base64_encode($data); + print("|0|{$_POST['file']}|{$data}|"); + } + } + exit; + + case 'save': + if(strlen($_POST['file']) < 1) { + print('|' . '<div class="alert alert-danger" role="alert">'.gettext("No file name specified").'</div>' . '|'); + } else { + conf_mount_rw(); + $_POST['data'] = str_replace("\r", "", base64_decode($_POST['data'])); + $ret = file_put_contents($_POST['file'], $_POST['data']); + conf_mount_ro(); + if ($_POST['file'] == "/conf/config.xml" || $_POST['file'] == "/cf/conf/config.xml") { + if (file_exists("/tmp/config.cache")) { + unlink("/tmp/config.cache"); + } + disable_security_checks(); + } + if($ret === false) { + print('|' . '<div class="alert alert-danger" role="alert">' . gettext("Failed to write file") . '</div>' . '|'); + } elseif($ret != strlen($_POST['data'])) { + print('|' . '<div class="alert alert-danger" role="alert">' . gettext("Error while writing file") . '</div>' . '|'); + } else { + print('|' . '<div class="alert alert-success" role="alert">' . gettext("File saved successfully") . '</div>' . '|'); + } + } + exit; + } + exit; +} + +require("head.inc"); +?> +<!-- file status box --> +<div style="display:none; background:#eeeeee;" id="fileStatusBox"> + <strong id="fileStatus"></strong> +</div> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Save / Load a file from the filesystem")?></h2></div> + <div class="panel-body"> + <form> + <input type="text" class="form-control" id="fbTarget"/> + <input type="button" class="btn btn-default btn-sm" onclick="loadFile();" value="<?=gettext('Load')?>" /> + <input type="button" class="btn btn-default btn-sm" id="fbOpen" value="<?=gettext('Browse')?>" /> + <input type="button" class="btn btn-default btn-sm" onclick="saveFile();" value="<?=gettext('Save')?>" /> + </form> + + <div id="fbBrowser" style="display:none; border:1px dashed gray; width:98%;"></div> + + <div style="background:#eeeeee;" id="fileOutput"> + <script type="text/javascript"> + //<![CDATA[ + window.onload=function(){ + document.getElementById("fileContent").wrap='off'; + } + //]]> + </script> + <textarea id="fileContent" name="fileContent" class="form-control" rows="30" cols=""></textarea> + </div> + + </div> +</div> + +<script> + function loadFile() { + jQuery("#fileStatus").html(""); + jQuery("#fileStatusBox").show(500); + jQuery.ajax( + "<?=$_SERVER['SCRIPT_NAME']?>", { + type: "post", + data: "action=load&file=" + jQuery("#fbTarget").val(), + complete: loadComplete + } + ); + } + + function loadComplete(req) { + jQuery("#fileContent").show(1000); + var values = req.responseText.split("|"); + values.shift(); values.pop(); + + if (values.shift() == "0") { + var file = values.shift(); + var fileContent = window.atob(values.join("|")); + + jQuery("#fileContent").val(fileContent); + } + else { + jQuery("#fileStatus").html(values[0]); + jQuery("#fileContent").val(""); + } + + jQuery("#fileContent").show(1000); + } + + function saveFile(file) { + jQuery("#fileStatus").html(""); + jQuery("#fileStatusBox").show(500); + + var fileContent = Base64.encode(jQuery("#fileContent").val()); + fileContent = fileContent.replace(/\+/g, "%2B"); + + jQuery.ajax( + "<?=$_SERVER['SCRIPT_NAME']?>", { + type: "post", + data: "action=save&file=" + jQuery("#fbTarget").val() + + "&data=" + fileContent, + complete: function(req) { + var values = req.responseText.split("|"); + jQuery("#fileStatus").html(values[1]); + } + } + ); + } + + <?php if($_GET['action'] == "load"): ?> + events.push(function() { + jQuery("#fbTarget").val("<?=htmlspecialchars($_GET['path'])?>"); + loadFile(); + }); + <?php endif; ?> +</script> + +<?php include("foot.inc"); + +outputJavaScriptFileInline("filebrowser/browser.js");
\ No newline at end of file diff --git a/src/usr/local/www/exec.php b/src/usr/local/www/exec.php new file mode 100644 index 0000000..a7d9cfd --- /dev/null +++ b/src/usr/local/www/exec.php @@ -0,0 +1,302 @@ +<?php +/* $Id$ */ +/* + exec.php +*/ +/* ==================================================================== + * Exec+ v1.02-000 - Copyright 2001-2003, All rights reserved + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Created by technologEase (http://www.technologEase.com) + * (modified for m0n0wall by Manuel Kasper <mk@neon1.net>)\ + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: shell +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-command +##|*NAME=Diagnostics: Command page +##|*DESCR=Allow access to the 'Diagnostics: Command' page. +##|*MATCH=exec.php* +##|-PRIV + +$allowautocomplete = true; + +require("guiconfig.inc"); + +if (($_POST['submit'] == "Download") && file_exists($_POST['dlPath'])) { + session_cache_limiter('public'); + $fd = fopen($_POST['dlPath'], "rb"); + header("Content-Type: application/octet-stream"); + header("Content-Length: " . filesize($_POST['dlPath'])); + header("Content-Disposition: attachment; filename=\"" . + trim(htmlentities(basename($_POST['dlPath']))) . "\""); + if (isset($_SERVER['HTTPS'])) { + header('Pragma: '); + header('Cache-Control: '); + } else { + header("Pragma: private"); + header("Cache-Control: private, must-revalidate"); + } + + fpassthru($fd); + exit; +} else if (($_POST['submit'] == "Upload") && is_uploaded_file($_FILES['ulfile']['tmp_name'])) { + move_uploaded_file($_FILES['ulfile']['tmp_name'], "/tmp/" . $_FILES['ulfile']['name']); + $ulmsg = "Uploaded file to /tmp/" . htmlentities($_FILES['ulfile']['name']); + unset($_POST['txtCommand']); +} + +if ($_POST) { + conf_mount_rw(); +} + +// Function: is Blank +// Returns true or false depending on blankness of argument. + +function isBlank($arg) { + return preg_match("/^\s*$/", $arg); +} + +// Function: Puts +// Put string, Ruby-style. + +function puts($arg) { + echo "$arg\n"; +} + +// "Constants". + +$Version = ''; +$ScriptName = $REQUEST['SCRIPT_NAME']; + +// Get year. + +$arrDT = localtime(); +$intYear = $arrDT[5] + 1900; + +$closehead = false; +$pgtitle = array(gettext("Diagnostics"), gettext("Execute command")); +include("head.inc"); +?> +<script> + // Create recall buffer array (of encoded strings). +<?php + +if (isBlank($_POST['txtRecallBuffer'])) { + puts(" var arrRecallBuffer = new Array;"); +} else { + puts(" var arrRecallBuffer = new Array("); + $arrBuffer = explode("&", $_POST['txtRecallBuffer']); + for ($i = 0; $i < (count($arrBuffer) - 1); $i++) { + puts(" '" . htmlspecialchars($arrBuffer[$i], ENT_QUOTES | ENT_HTML401) . "',"); + } + puts(" '" . htmlspecialchars($arrBuffer[count($arrBuffer) - 1], ENT_QUOTES | ENT_HTML401) . "'"); + puts(" );"); +} +?> + // Set pointer to end of recall buffer. + var intRecallPtr = arrRecallBuffer.length-1; + + // Set pointer to end of recall buffer. + var intRecallPtr = arrRecallBuffer.length-1; + + // Functions to extend String class. + function str_encode() { return escape( this ) } + function str_decode() { return unescape( this ) } + + // Extend string class to include encode() and decode() functions. + String.prototype.encode = str_encode + String.prototype.decode = str_decode + + // Function: is Blank + // Returns boolean true or false if argument is blank. + function isBlank( strArg ) { return strArg.match( /^\s*$/ ) } + + // Function: frmExecPlus onSubmit (event handler) + // Builds the recall buffer from the command string on submit. + function frmExecPlus_onSubmit( form ) { + + if (!isBlank(form.txtCommand.value)) { + // If this command is repeat of last command, then do not store command. + if (form.txtCommand.value.encode() == arrRecallBuffer[arrRecallBuffer.length-1]) { return true } + + // Stuff encoded command string into the recall buffer. + if (isBlank(form.txtRecallBuffer.value)) { + form.txtRecallBuffer.value = form.txtCommand.value.encode(); + } else { + form.txtRecallBuffer.value += '&' + form.txtCommand.value.encode(); + } + } + + return true; + } + + // Function: btnRecall onClick (event handler) + // Recalls command buffer going either up or down. + function btnRecall_onClick( form, n ) { + + // If nothing in recall buffer, then error. + if (!arrRecallBuffer.length) { + alert('<?=gettext("Nothing to recall"); ?>!'); + form.txtCommand.focus(); + return; + } + + // Increment recall buffer pointer in positive or negative direction + // according to <n>. + intRecallPtr += n; + + // Make sure the buffer stays circular. + if (intRecallPtr < 0) { intRecallPtr = arrRecallBuffer.length - 1 } + if (intRecallPtr > (arrRecallBuffer.length - 1)) { intRecallPtr = 0 } + + // Recall the command. + form.txtCommand.value = arrRecallBuffer[intRecallPtr].decode(); + } + + // Function: Reset onClick (event handler) + // Resets form on reset button click event. + function Reset_onClick( form ) { + + // Reset recall buffer pointer. + intRecallPtr = arrRecallBuffer.length; + + // Clear form (could have spaces in it) and return focus ready for cmd. + form.txtCommand.value = ''; + form.txtCommand.focus(); + + return true; + } +//]]> +</script> +<?php + +if (isBlank($_POST['txtCommand']) && isBlank($_POST['txtPHPCommand']) && isBlank($ulmsg)) + print('<div class="alert alert-warning" role="alert">'.gettext("The capabilities offered here can be dangerous. No support is available. Use them at your own risk!").'</div>'); + +if (!isBlank($_POST['txtCommand'])):?> + <div class="panel panel-success responsive"> + <div class="panel-heading"><h2 class="panel-title">Shell Output - <?=htmlspecialchars($_POST['txtCommand'])?></h2></div> + <div class="panel-body"> + <pre> +<?php + putenv("PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"); + putenv("SCRIPT_FILENAME=" . strtok($_POST['txtCommand'], " ")); + print htmlspecialchars(system($_POST['txtCommand'].' 2>&1')); +?> + </pre> + </div> + </div> +<? endif ?> + +<form action="exec.php" method="post" enctype="multipart/form-data" name="frmExecPlus" onsubmit="return frmExecPlus_onSubmit( this );"> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Execute Shell Command')?></h2></div> + <div class="panel-body"> + <input id="txtCommand" name="txtCommand" placeholder="Command" type="text" class="col-sm-4" value="<?=htmlspecialchars($_POST['txtCommand'])?>" /> + <br /><br /> + <input type="hidden" name="txtRecallBuffer" value="<?=htmlspecialchars($_POST['txtRecallBuffer']) ?>" /> + <input type="button" class="btn btn-default btn-sm" name="btnRecallPrev" value="<" onclick="btnRecall_onClick( this.form, -1 );" /> + <input type="submit" class="btn btn-default btn-sm" value="<?=gettext("Execute"); ?>" /> + <input type="button" class="btn btn-default btn-sm" name="btnRecallNext" value=">" onclick="btnRecall_onClick( this.form, 1 );" /> + <input type="button" class="btn btn-default btn-sm" value="<?=gettext("Clear"); ?>" onclick="return Reset_onClick( this.form );" /> + </div> + </div> + + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Download file')?></h2></div> + <div class="panel-body"> + <input name="dlPath" type="text" id="dlPath" placeholder="File to download" class="col-sm-4"/> + <br /><br /> + <input name="submit" type="submit" class="btn btn-default btn-sm" id="download" value="<?=gettext("Download"); ?>" /> + </div> + </div> + +<?php + if ($ulmsg) + print('<div class="alert alert-success" role="alert">' . $ulmsg .'</div>'); +?> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Upload a file')?></h2></div> + <div class="panel-body"> + <input name="ulfile" type="file" class="btn btn-default btn-sm btn-file" id="ulfile" /> + <br /> + <input name="submit" type="submit" class="btn btn-default btn-sm pull-left" id="upload" value="<?=gettext("Upload"); ?>" /> + + </div> + </div> +<?php + if (!isBlank($_POST['txtPHPCommand'])) { + puts("<div class=\"panel panel-success responsive\"><div class=\"panel-heading\">PHP response</div>"); + puts("<pre>"); + require_once("config.inc"); + require_once("functions.inc"); + echo eval($_POST['txtPHPCommand']); + puts(" </pre>"); + puts("</div>"); +} +?> + <div class="panel panel-default responsive"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Execute PHP Commands')?></h2></div> + <div class="panel-body"> + <textarea id="txtPHPCommand" placeholder="Command" name="txtPHPCommand" rows="9" cols="80"><?=htmlspecialchars($_POST['txtPHPCommand'])?></textarea> + <br /> + <input type="submit" class="btn btn-default btn-sm" value="<?=gettext("Execute")?>" /> + <?=gettext("Example"); ?>: <code>print("Hello World!");</code> + </div> + </div> +</form> + +<?php +include("foot.inc"); + +if($_POST) + conf_mount_ro();
\ No newline at end of file diff --git a/src/usr/local/www/favicon.ico b/src/usr/local/www/favicon.ico Binary files differnew file mode 100755 index 0000000..3440bf2 --- /dev/null +++ b/src/usr/local/www/favicon.ico diff --git a/src/usr/local/www/fbegin.inc b/src/usr/local/www/fbegin.inc new file mode 100644 index 0000000..20a0066 --- /dev/null +++ b/src/usr/local/www/fbegin.inc @@ -0,0 +1,2 @@ +<!-- temporary until migration to bootstrap has completed --> +<div style="background-color: gray;">
\ No newline at end of file diff --git a/src/usr/local/www/fend.inc b/src/usr/local/www/fend.inc new file mode 100644 index 0000000..262aeb7 --- /dev/null +++ b/src/usr/local/www/fend.inc @@ -0,0 +1,2 @@ +<!-- temporary --> +<?php include "foot.inc"; ?> diff --git a/src/usr/local/www/filebrowser/browser.js b/src/usr/local/www/filebrowser/browser.js new file mode 100644 index 0000000..2769db3 --- /dev/null +++ b/src/usr/local/www/filebrowser/browser.js @@ -0,0 +1,48 @@ +/* + pfSense_MODULE: shell +*/ + +jQuery(document).ready( + function() { + jQuery("#fbOpen").click( + function() { + jQuery("#fbBrowser").fadeIn(750); + fbBrowse(jQuery("#fbTarget").val()); + } + ); + } +); + +function fbBrowse(path) { + jQuery("#fileContent").fadeOut(); + + if (jQuery("#fbCurrentDir")) { + jQuery("#fbCurrentDir").html("Loading ..."); + } + + jQuery.ajax( + "/filebrowser/browser.php?path=" + encodeURI(path ? path : "/"), + { type: "get", complete: fbComplete } + ); + +} + +function fbComplete(req) { + jQuery("#fbBrowser").html(req.responseText); + + var actions = { + fbHome: function() { fbBrowse("/"); }, + fbClose: function() { jQuery("#fbBrowser").fadeOut(750); }, + fbDir: function() { fbBrowse(this.id); }, + fbFile: function() { jQuery("#fbTarget").val(this.id); } + } + + for (var type in actions) { + jQuery("#fbBrowser ." + type).each( + function() { + jQuery(this).click(actions[type]); + jQuery(this).css("cursor","pointer"); + } + ); + } +} diff --git a/src/usr/local/www/filebrowser/browser.php b/src/usr/local/www/filebrowser/browser.php new file mode 100644 index 0000000..8ab05db --- /dev/null +++ b/src/usr/local/www/filebrowser/browser.php @@ -0,0 +1,165 @@ +<?php + +require_once("guiconfig.inc"); + +/* + pfSense_MODULE: shell + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + +*/ +// Fetch a list of directories and files inside a given directory +function get_content($dir) { + $dirs = array(); + $files = array(); + + clearstatcache(); + $fd = @opendir($dir); + + while ($entry = @readdir($fd)) { + if ($entry == ".") { + continue; + } + if ($entry == ".." && $dir == "/") { + continue; + } + if (is_dir("{$dir}/{$entry}")) { + array_push($dirs, $entry); + } else { + array_push($files, $entry); + } + } + + @closedir($fd); + + natsort($dirs); + natsort($files); + + return array($dirs, $files); +} + +$path = realpath(strlen($_GET['path']) > 0 ? $_GET['path'] : "/"); +if (is_file($path)) { + $path = dirname($path); +} + +// ----- header ----- +?> +<table width="100%"> + <tr> + <td class="fbHome" width="25px" align="left"> + <img onClick="jQuery('#fbTarget').val('<?=$realDir?>'); fbBrowse('/');" src="/filebrowser/images/icon_home.gif" alt="Home" title="Home" /> + </td> + <td><b><?=$path;?></b></td> + <td class="fbClose" align="right"> + <img onClick="jQuery('#fbBrowser').fadeOut();" border="0" src="/filebrowser/images/icon_cancel.gif" alt="Close" title="Close" /> + </td> + </tr> + <tr> + <td id="fbCurrentDir" colspan="3" class="vexpl" align="left"> +<?php + +// ----- read contents ----- +if (is_dir($path)) { + list($dirs, $files) = get_content($path); +?> + + </td> + </tr> +<?php +} else { +?> + Directory does not exist. + </td> + </tr> +</table> +<?php + exit; +} + +// ----- directories ----- +foreach ($dirs as $dir): + $realDir = realpath("{$path}/{$dir}"); +?> + <tr> + <td></td> + <td class="fbDir vexpl" id="<?=$realDir;?>" align="left"> + <div onClick="jQuery('#fbTarget').val('<?=$realDir?>'); fbBrowse('<?=$realDir?>');"> + <img src="/filebrowser/images/folder_generic.gif" /> + <?=$dir;?> + </div> + </td> + <td></td> + </tr> +<?php +endforeach; + +// ----- files ----- +foreach ($files as $file): + $ext = strrchr($file, "."); + + switch ($ext) { + case ".css": + case ".html": + case ".xml": + $type = "code"; + break; + case ".rrd": + $type = "database"; + break; + case ".gif": + case ".jpg": + case ".png": + $type = "image"; + break; + case ".js": + $type = "js"; + break; + case ".pdf": + $type = "pdf"; + break; + case ".inc": + case ".php": + $type = "php"; + break; + case ".conf": + case ".pid": + case ".sh": + $type = "system"; + break; + case ".bz2": + case ".gz": + case ".tgz": + case ".zip": + $type = "zip"; + break; + default: + $type = "generic"; + } + + $fqpn = "{$path}/{$file}"; + + if (is_file($fqpn)) { + $fqpn = realpath($fqpn); + $size = sprintf("%.2f KiB", filesize($fqpn) / 1024); + } else { + $size = ""; + } + +?> + <tr> + <td></td> + <td class="fbFile vexpl" id="<?=$fqpn;?>" align="left"> + <?php $filename = str_replace("//","/", "{$path}/{$file}"); ?> + <div onClick="jQuery('#fbTarget').val('<?=$filename?>'); loadFile(); jQuery('#fbBrowser').fadeOut();"> + <img src="/filebrowser/images/file_<?=$type;?>.gif" alt="" title=""> + <?=$file;?> + </div> + </td> + <td align="right" class="vexpl"> + <?=$size;?> + </td> + </tr> +<?php +endforeach; +?> +</table> diff --git a/src/usr/local/www/filebrowser/images/file_code.gif b/src/usr/local/www/filebrowser/images/file_code.gif Binary files differnew file mode 100755 index 0000000..f06a205 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_code.gif diff --git a/src/usr/local/www/filebrowser/images/file_database.gif b/src/usr/local/www/filebrowser/images/file_database.gif Binary files differnew file mode 100755 index 0000000..d479c91 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_database.gif diff --git a/src/usr/local/www/filebrowser/images/file_doc.gif b/src/usr/local/www/filebrowser/images/file_doc.gif Binary files differnew file mode 100755 index 0000000..222c31b --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_doc.gif diff --git a/src/usr/local/www/filebrowser/images/file_flash.gif b/src/usr/local/www/filebrowser/images/file_flash.gif Binary files differnew file mode 100755 index 0000000..e565c37 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_flash.gif diff --git a/src/usr/local/www/filebrowser/images/file_generic.gif b/src/usr/local/www/filebrowser/images/file_generic.gif Binary files differnew file mode 100755 index 0000000..5f39482 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_generic.gif diff --git a/src/usr/local/www/filebrowser/images/file_image.gif b/src/usr/local/www/filebrowser/images/file_image.gif Binary files differnew file mode 100755 index 0000000..4960683 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_image.gif diff --git a/src/usr/local/www/filebrowser/images/file_js.gif b/src/usr/local/www/filebrowser/images/file_js.gif Binary files differnew file mode 100755 index 0000000..9b6601f --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_js.gif diff --git a/src/usr/local/www/filebrowser/images/file_pdf.gif b/src/usr/local/www/filebrowser/images/file_pdf.gif Binary files differnew file mode 100755 index 0000000..b01bb23 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_pdf.gif diff --git a/src/usr/local/www/filebrowser/images/file_php.gif b/src/usr/local/www/filebrowser/images/file_php.gif Binary files differnew file mode 100755 index 0000000..ab7f459 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_php.gif diff --git a/src/usr/local/www/filebrowser/images/file_ppt.gif b/src/usr/local/www/filebrowser/images/file_ppt.gif Binary files differnew file mode 100755 index 0000000..0383c98 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_ppt.gif diff --git a/src/usr/local/www/filebrowser/images/file_system.gif b/src/usr/local/www/filebrowser/images/file_system.gif Binary files differnew file mode 100755 index 0000000..f1997c7 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_system.gif diff --git a/src/usr/local/www/filebrowser/images/file_xls.gif b/src/usr/local/www/filebrowser/images/file_xls.gif Binary files differnew file mode 100755 index 0000000..d004013 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_xls.gif diff --git a/src/usr/local/www/filebrowser/images/file_zip.gif b/src/usr/local/www/filebrowser/images/file_zip.gif Binary files differnew file mode 100755 index 0000000..ec98255 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/file_zip.gif diff --git a/src/usr/local/www/filebrowser/images/folder_generic.gif b/src/usr/local/www/filebrowser/images/folder_generic.gif Binary files differnew file mode 100755 index 0000000..45b191d --- /dev/null +++ b/src/usr/local/www/filebrowser/images/folder_generic.gif diff --git a/src/usr/local/www/filebrowser/images/icon_cancel.gif b/src/usr/local/www/filebrowser/images/icon_cancel.gif Binary files differnew file mode 100755 index 0000000..246a819 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/icon_cancel.gif diff --git a/src/usr/local/www/filebrowser/images/icon_contract.gif b/src/usr/local/www/filebrowser/images/icon_contract.gif Binary files differnew file mode 100755 index 0000000..cf82159 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/icon_contract.gif diff --git a/src/usr/local/www/filebrowser/images/icon_expand.gif b/src/usr/local/www/filebrowser/images/icon_expand.gif Binary files differnew file mode 100755 index 0000000..06c0c68 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/icon_expand.gif diff --git a/src/usr/local/www/filebrowser/images/icon_home.gif b/src/usr/local/www/filebrowser/images/icon_home.gif Binary files differnew file mode 100755 index 0000000..f888434 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/icon_home.gif diff --git a/src/usr/local/www/filebrowser/images/icon_left.gif b/src/usr/local/www/filebrowser/images/icon_left.gif Binary files differnew file mode 100755 index 0000000..0d1137c --- /dev/null +++ b/src/usr/local/www/filebrowser/images/icon_left.gif diff --git a/src/usr/local/www/filebrowser/images/icon_magnifier.gif b/src/usr/local/www/filebrowser/images/icon_magnifier.gif Binary files differnew file mode 100755 index 0000000..e96e548 --- /dev/null +++ b/src/usr/local/www/filebrowser/images/icon_magnifier.gif diff --git a/src/usr/local/www/firewall_aliases.php b/src/usr/local/www/firewall_aliases.php new file mode 100644 index 0000000..c7be7d9 --- /dev/null +++ b/src/usr/local/www/firewall_aliases.php @@ -0,0 +1,295 @@ +<?php +/* $Id$ */ +/* + firewall_aliases.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: aliases +*/ + +##|+PRIV +##|*IDENT=page-firewall-aliases +##|*NAME=Firewall: Aliases page +##|*DESCR=Allow access to the 'Firewall: Aliases' page. +##|*MATCH=firewall_aliases.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +if (!is_array($config['aliases']['alias'])) { + $config['aliases']['alias'] = array(); +} +$a_aliases = &$config['aliases']['alias']; + +$tab = ($_REQUEST['tab'] == "" ? "ip" : preg_replace("/\W/", "", $_REQUEST['tab'])); + +if ($_POST) { + + if ($_POST['apply']) { + $retval = 0; + + /* reload all components that use aliases */ + $retval = filter_configure(); + + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message($retval); + } else { + $savemsg = $retval; + } + if ($retval == 0) { + clear_subsystem_dirty('aliases'); + } + } +} + +if ($_GET['act'] == "del") { + if ($a_aliases[$_GET['id']]) { + /* make sure rule is not being referenced by any nat or filter rules */ + $is_alias_referenced = false; + $referenced_by = false; + $alias_name = $a_aliases[$_GET['id']]['name']; + // Firewall rules + find_alias_reference(array('filter', 'rule'), array('source', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('filter', 'rule'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('filter', 'rule'), array('source', 'port'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('filter', 'rule'), array('destination', 'port'), $alias_name, $is_alias_referenced, $referenced_by); + // NAT Rules + find_alias_reference(array('nat', 'rule'), array('source', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('nat', 'rule'), array('source', 'port'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('nat', 'rule'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('nat', 'rule'), array('destination', 'port'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('nat', 'rule'), array('target'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('nat', 'rule'), array('local-port'), $alias_name, $is_alias_referenced, $referenced_by); + // NAT 1:1 Rules + //find_alias_reference(array('nat', 'onetoone'), array('external'), $alias_name, $is_alias_referenced, $referenced_by); + //find_alias_reference(array('nat', 'onetoone'), array('source', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('nat', 'onetoone'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + // NAT Outbound Rules + find_alias_reference(array('nat', 'outbound', 'rule'), array('source', 'network'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('nat', 'outbound', 'rule'), array('sourceport'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('nat', 'outbound', 'rule'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('nat', 'outbound', 'rule'), array('dstport'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('nat', 'outbound', 'rule'), array('target'), $alias_name, $is_alias_referenced, $referenced_by); + // Alias in an alias + find_alias_reference(array('aliases', 'alias'), array('address'), $alias_name, $is_alias_referenced, $referenced_by); + // Load Balancer + find_alias_reference(array('load_balancer', 'lbpool'), array('port'), $alias_name, $is_alias_referenced, $referenced_by); + find_alias_reference(array('load_balancer', 'virtual_server'), array('port'), $alias_name, $is_alias_referenced, $referenced_by); + // Static routes + find_alias_reference(array('staticroutes', 'route'), array('network'), $alias_name, $is_alias_referenced, $referenced_by); + if ($is_alias_referenced == true) { + $savemsg = sprintf(gettext("Cannot delete alias. Currently in use by %s"), $referenced_by); + } else { + unset($a_aliases[$_GET['id']]); + if (write_config()) { + filter_configure(); + mark_subsystem_dirty('aliases'); + } + header("Location: firewall_aliases.php?tab=" . $tab); + exit; + } + } +} + +function find_alias_reference($section, $field, $origname, &$is_alias_referenced, &$referenced_by) { + global $config; + if (!$origname || $is_alias_referenced) { + return; + } + + $sectionref = &$config; + foreach ($section as $sectionname) { + if (is_array($sectionref) && isset($sectionref[$sectionname])) { + $sectionref = &$sectionref[$sectionname]; + } else { + return; + } + } + + if (is_array($sectionref)) { + foreach ($sectionref as $itemkey => $item) { + $fieldfound = true; + $fieldref = &$sectionref[$itemkey]; + foreach ($field as $fieldname) { + if (is_array($fieldref) && isset($fieldref[$fieldname])) { + $fieldref = &$fieldref[$fieldname]; + } else { + $fieldfound = false; + break; + } + } + if ($fieldfound && $fieldref == $origname) { + $is_alias_referenced = true; + if (is_array($item)) { + $referenced_by = $item['descr']; + } + break; + } + } + } +} + +$pgtitle = array(gettext("Firewall"), gettext("Aliases")); +$shortcut_section = "aliases"; + +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg); +if (is_subsystem_dirty('aliases')) + print_info_box_np(gettext("The alias list has been changed.") . "<br />" . gettext("You must apply the changes in order for them to take effect.")); + +$tab_array = array(); +$tab_array[] = array(gettext("IP"),($tab=="ip" ? true : ($tab=="host" ? true : ($tab == "network" ? true : false))), "/firewall_aliases.php?tab=ip"); +$tab_array[] = array(gettext("Ports"), ($tab=="port"? true : false), "/firewall_aliases.php?tab=port"); +$tab_array[] = array(gettext("URLs"), ($tab=="url"? true : false), "/firewall_aliases.php?tab=url"); +$tab_array[] = array(gettext("All"), ($tab=="all"? true : false), "/firewall_aliases.php?tab=all"); +display_top_tabs($tab_array); + +?> +<div class="table-responsive"> +<table class="table table-striped table-hover"> + <thead> + <tr> + <th><?=gettext("Name")?></th> + <th><?=gettext("Values")?></th> + <th><?=gettext("Description")?></th> + </tr> + </thead> + <tbody> +<?php + asort($a_aliases); + foreach ($a_aliases as $i=> $alias): + unset ($show_alias); + switch ($tab){ + case "all": + $show_alias= true; + break; + case "ip": + case "host": + case "network": + if (preg_match("/(host|network)/",$alias["type"])) + $show_alias= true; + break; + case "url": + if (preg_match("/(url)/i",$alias["type"])) + $show_alias= true; + break; + case "port": + if($alias["type"] == "port") + $show_alias= true; + break; + } + if ($show_alias): +?> + <tr> + <td> + <?=htmlspecialchars($alias['name'])?> + </td> + <td> +<?php + if ($alias["url"]) { + echo $alias["url"] . "<br />"; + } else { + if(is_array($alias["aliasurl"])) { + $aliasurls = implode(", ", array_slice($alias["aliasurl"], 0, 10)); + echo $aliasurls; + if(count($aliasurls) > 10) { + echo "…<br />"; + } + echo "<br />\n"; + } + $tmpaddr = explode(" ", $alias['address']); + $addresses = implode(", ", array_slice($tmpaddr, 0, 10)); + echo $addresses; + if(count($tmpaddr) > 10) { + echo '…'; + } + } +?> + </td> + <td> + <?=htmlspecialchars($alias['descr'])?> + </td> + <td> + <a href="firewall_aliases_edit.php?id=<?=$i?>" class="btn btn-xs btn-primary">edit</a> + <a href="?act=del&tab=<?=$tab?>&id=<?=$i?>" class="btn btn-xs btn-danger">delete</a> + </td> + </tr> +<?php endif?> +<?php endforeach?> + </tbody> +</table> +</div> + +<nav class="action-buttons"> + <a href="firewall_aliases_edit.php?tab=<?=$tab?>" role="button" class="btn btn-success"> + <?=gettext("add new alias");?> + </a> + <a href="firewall_aliases_import.php" role="button" class="btn btn-default"> + <?=gettext("bulk import");?> + </a> +</nav> + +<?php + +print_info_box(gettext('Aliases act as placeholders for real hosts, networks or ports. They can be used to minimize the number ' . + 'of changes that have to be made if a host, network or port changes.' . '<br />' . + 'You can enter the name of an alias instead of the host, network or port where indicated. The alias will be resolved according to the list above.' . '<br />' . + 'If an alias cannot be resolved (e.g. because you deleted it), the corresponding element (e.g. filter/NAT/shaper rule) will be considered invalid and skipped.')); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_aliases_edit.php b/src/usr/local/www/firewall_aliases_edit.php new file mode 100755 index 0000000..84d9d96 --- /dev/null +++ b/src/usr/local/www/firewall_aliases_edit.php @@ -0,0 +1,781 @@ +<?php +/* $Id$ */ +/* + firewall_aliases_edit.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004 Scott Ullrich + * Copyright (c) 2009 Ermal Luçi + * Copyright (c) 2010 Jim Pingle + * originally part of m0n0wall (http://m0n0.ch/wall) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /bin/rm /bin/mkdir /usr/bin/fetch + pfSense_MODULE: aliases +*/ + +##|+PRIV +##|*IDENT=page-firewall-alias-edit +##|*NAME=Firewall: Alias: Edit page +##|*DESCR=Allow access to the 'Firewall: Alias: Edit' page. +##|*MATCH=firewall_aliases_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("util.inc"); + +$pgtitle = array(gettext("Firewall"), gettext("Aliases"), gettext("Edit")); + +if (isset($_POST['referer'])) { + $referer = $_POST['referer']; +} else { + $referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/firewall_aliases.php'); +} + +// Keywords not allowed in names +$reserved_keywords = array("all", "pass", "block", "out", "queue", "max", "min", "pptp", "pppoe", "L2TP", "OpenVPN", "IPsec"); + +// Add all Load balance names to reserved_keywords +if (is_array($config['load_balancer']['lbpool'])) { + foreach ($config['load_balancer']['lbpool'] as $lbpool) { + $reserved_keywords[] = $lbpool['name']; + } +} + +$reserved_ifs = get_configured_interface_list(false, true); +$reserved_keywords = array_merge($reserved_keywords, $reserved_ifs, $reserved_table_names); +$max_alias_addresses = 5000; + +if (!is_array($config['aliases']['alias'])) { + $config['aliases']['alias'] = array(); +} +$a_aliases = &$config['aliases']['alias']; + +$tab = $_REQUEST['tab']; + +if ($_POST) { + $origname = $_POST['origname']; +} + +$adrs = count($_POST['address']); + +for($idx=0; $idx<$adrs; $idx++) { + if($_POST['address'][$idx] == "") + unset($_POST['address'][$idx]); +} + +// Debugging +if ($debug) { + unlink_if_exists("{$g['tmp_path']}/alias_rename_log.txt"); +} + +function alias_same_type($name, $type) { + global $config; + + foreach ($config['aliases']['alias'] as $alias) { + if ($name == $alias['name']) { + if (in_array($type, array("host", "network")) && + in_array($alias['type'], array("host", "network"))) { + return true; + } + if ($type == $alias['type']) { + return true; + } else { + return false; + } + } + } + return true; +} + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_aliases[$id]) { + $original_alias_name = $a_aliases[$id]['name']; + $pconfig['name'] = $a_aliases[$id]['name']; + $pconfig['detail'] = $a_aliases[$id]['detail']; + $pconfig['address'] = $a_aliases[$id]['address']; + $pconfig['type'] = $a_aliases[$id]['type']; + $pconfig['descr'] = html_entity_decode($a_aliases[$id]['descr']); + + if (preg_match("/urltable/i", $a_aliases[$id]['type'])) { + $pconfig['address'] = $a_aliases[$id]['url']; + + $pconfig['updatefreq'] = $a_aliases[$id]['updatefreq']; + } + if ($a_aliases[$id]['aliasurl'] <> "") { + if (is_array($a_aliases[$id]['aliasurl'])) { + $pconfig['address'] = implode(" ", $a_aliases[$id]['aliasurl']); + } else { + $pconfig['address'] = $a_aliases[$id]['aliasurl']; + } + } +} + +if ($_POST) { + unset($input_errors); + $vertical_bar_err_text = gettext("Vertical bars (|) at start or end, or double in the middle of descriptions not allowed. Descriptions have been cleaned. Check and save again."); + + /* input validation */ + + $reqdfields = explode(" ", "name"); + $reqdfieldsn = array(gettext("Name")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + $x = is_validaliasname($_POST['name']); + if (!isset($x)) { + $input_errors[] = gettext("Reserved word used for alias name."); + } else if ($_POST['type'] == "port" && (getservbyname($_POST['name'], "tcp") || getservbyname($_POST['name'], "udp"))) { + $input_errors[] = gettext("Reserved word used for alias name."); + } else { + if (is_validaliasname($_POST['name']) == false) { + $input_errors[] = gettext("The alias name must be less than 32 characters long, may not consist of only numbers, and may only contain the following characters") . " a-z, A-Z, 0-9, _."; + } + } + /* check for name conflicts */ + if (empty($a_aliases[$id])) { + foreach ($a_aliases as $alias) { + if ($alias['name'] == $_POST['name']) { + $input_errors[] = gettext("An alias with this name already exists."); + break; + } + } + } + + /* Check for reserved keyword names */ + foreach ($reserved_keywords as $rk) { + if ($rk == $_POST['name']) { + $input_errors[] = sprintf(gettext("Cannot use a reserved keyword as alias name %s"), $rk); + } + } + + /* check for name interface description conflicts */ + foreach ($config['interfaces'] as $interface) { + if ($interface['descr'] == $_POST['name']) { + $input_errors[] = gettext("An interface description with this name already exists."); + break; + } + } + + $alias = array(); + $address = array(); + $final_address_details = array(); + $alias['name'] = $_POST['name']; + + if (preg_match("/urltable/i", $_POST['type'])) { + $address = ""; + + /* item is a url table type */ + if ($_POST['address'][0]) { + /* fetch down and add in */ + $_POST['address'][0] = trim($_POST['address'][0]); + $address[] = $_POST['address'][0]; + $alias['url'] = $_POST['address'][0]; + $alias['updatefreq'] = $_POST['frequency'][0] ? $_POST['frequency'][0] : 7; + if (!is_URL($alias['url']) || empty($alias['url'])) { + $input_errors[] = gettext("You must provide a valid URL."); + } elseif (!process_alias_urltable($alias['name'], $alias['url'], 0, true)) { + $input_errors[] = gettext("Unable to fetch usable data."); + } + if ($_POST["detail"][0] != "") { + if ((strpos($_POST["detail"][0], "||") === false) && (substr($_POST["detail"][0], 0, 1) != "|") && (substr($_POST["detail"][0], -1, 1) != "|")) { + $final_address_details[] = $_POST["detail"][0]; + } else { + /* Remove leading and trailing vertical bars and replace multiple vertical bars with single, */ + /* and put in the output array so the text is at least redisplayed for the user. */ + $final_address_details[] = preg_replace('/\|\|+/', '|', trim($_POST["detail"][0], "|")); + $input_errors[] = $vertical_bar_err_text; + } + } else { + $final_address_details[] = sprintf(gettext("Entry added %s"), date('r')); + } + } + } else if ($_POST['type'] == "url" || $_POST['type'] == "url_ports") { + $desc_fmt_err_found = false; + + /* item is a url type */ + foreach ($_POST['address'] as $idx => $post_address) { + /* fetch down and add in */ + $temp_filename = tempnam("{$g['tmp_path']}/", "alias_import"); + unlink_if_exists($temp_filename); + $verify_ssl = isset($config['system']['checkaliasesurlcert']); + mkdir($temp_filename); + download_file($post_address, $temp_filename . "/aliases", $verify_ssl); + + /* if the item is tar gzipped then extract */ + if(stristr($post_address, ".tgz")) + process_alias_tgz($temp_filename); + else if(stristr($post_address, ".zip")) + process_alias_unzip($temp_filename); + + if (!isset($alias['aliasurl'])) { + $alias['aliasurl'] = array(); + } + + $alias['aliasurl'][] = $post_address; + if ($_POST['detail'][$idx] != "") { + if ((strpos($_POST['detail'][$idx], "||") === false) && (substr($_POST['detail'][$idx], 0, 1) != "|") && (substr($_POST['detail'][$idx], -1, 1) != "|")) { + $final_address_details[] = $_POST['detail'][$idx]; + } else { + /* Remove leading and trailing vertical bars and replace multiple vertical bars with single, */ + /* and put in the output array so the text is at least redisplayed for the user. */ + $final_address_details[] = preg_replace('/\|\|+/', '|', trim($_POST['detail'][$idx], "|")); + if (!$desc_fmt_err_found) { + $input_errors[] = $vertical_bar_err_text; + $desc_fmt_err_found = true; + } + } + } else { + $final_address_details[] = sprintf(gettext("Entry added %s"), date('r')); + } + + if (file_exists("{$temp_filename}/aliases")) { + $address = parse_aliases_file("{$temp_filename}/aliases", $_POST['type'], 3000); + if ($address == null) { + /* nothing was found */ + $input_errors[] = sprintf(gettext("You must provide a valid URL. Could not fetch usable data from '%s'."), $post_address); + } + mwexec("/bin/rm -rf " . escapeshellarg($temp_filename)); + } else { + $input_errors[] = sprintf(gettext("URL '%s' is not valid."), $post_address); + } + } + unset($desc_fmt_err_found); + if ($_POST['type'] == "url_ports") { + $address = group_ports($address); + } + } else { + /* item is a normal alias type */ + $wrongaliases = ""; + $desc_fmt_err_found = false; + $alias_address_count = 0; + $input_addresses = array(); + + // First trim and expand the input data. + // Users can paste strings like "10.1.2.0/24 10.3.0.0/16 9.10.11.0/24" into an address box. + // They can also put an IP range. + // This loop expands out that stuff so it can easily be validated. + foreach ($_POST['address'] as $idx => $post_address) { + if ($post_address != "") { + + if ((strpos($post_address, "||") === false) && (substr($post_address, 0, 1) != "|") && (substr($post_address, -1, 1) != "|")) { + $detail_text = $post_address; + } else { + /* Remove leading and trailing vertical bars and replace multiple vertical bars with single, */ + /* and put in the output array so the text is at least redisplayed for the user. */ + $detail_text = preg_replace('/\|\|+/', '|', trim($post_address, "|")); + if (!$desc_fmt_err_found) { + $input_errors[] = $vertical_bar_err_text; + $desc_fmt_err_found = true; + } + } + } else { + $detail_text = sprintf(gettext("Entry added %s"), date('r')); + } + + $address_items = explode(" ", trim($post_address)); + foreach ($address_items as $address_item) { + $iprange_type = is_iprange($address_item); + + if ($iprange_type == 4) { + list($startip, $endip) = explode('-', $address_item); + if ($_POST['type'] == "network") { + // For network type aliases, expand an IPv4 range into an array of subnets. + $rangesubnets = ip_range_to_subnet_array($startip, $endip); + foreach ($rangesubnets as $rangesubnet) { + if ($alias_address_count > $max_alias_addresses) { + break; + } + list($address_part, $subnet_part) = explode("/", $rangesubnet); + $input_addresses[] = $address_part; + $input_address_subnet[] = $subnet_part; + $final_address_details[] = $detail_text; + $alias_address_count++; + } + } else { + // For host type aliases, expand an IPv4 range into a list of individual IPv4 addresses. + $rangeaddresses = ip_range_to_address_array($startip, $endip, $max_alias_addresses - $alias_address_count); + if (is_array($rangeaddresses)) { + foreach ($rangeaddresses as $rangeaddress) { + $input_addresses[] = $rangeaddress; + $input_address_subnet[] = ""; + $final_address_details[] = $detail_text; + $alias_address_count++; + } + } else { + $input_errors[] = sprintf(gettext('Range is too large to expand into individual host IP addresses (%s)'), $address_item); + $input_errors[] = sprintf(gettext('The maximum number of entries in an alias is %s'), $max_alias_addresses); + // Put the user-entered data in the output anyway, so it will be re-displayed for correction. + $input_addresses[] = $address_item; + $input_address_subnet[] = ""; + $final_address_details[] = $detail_text; + } + } + } else if ($iprange_type == 6) { + $input_errors[] = sprintf(gettext('IPv6 address ranges are not supported (%s)'), $address_item); + // Put the user-entered data in the output anyway, so it will be re-displayed for correction. + $input_addresses[] = $address_item; + $input_address_subnet[] = ""; + $final_address_details[] = $detail_text; + } else { + $subnet_type = is_subnet($address_item); + if (($_POST['type'] == "host") && $subnet_type) { + if ($subnet_type == 4) { + // For host type aliases, if the user enters an IPv4 subnet, expand it into a list of individual IPv4 addresses. + if (subnet_size($address_item) <= ($max_alias_addresses - $alias_address_count)) { + $rangeaddresses = subnetv4_expand($address_item); + foreach ($rangeaddresses as $rangeaddress) { + $input_addresses[] = $rangeaddress; + $input_address_subnet[] = ""; + $final_address_details[] = $detail_text; + $alias_address_count++; + } + } else { + $input_errors[] = sprintf(gettext('Subnet is too large to expand into individual host IP addresses (%s)'), $address_item); + $input_errors[] = sprintf(gettext('The maximum number of entries in an alias is %s'), $max_alias_addresses); + // Put the user-entered data in the output anyway, so it will be re-displayed for correction. + $input_addresses[] = $address_item; + $input_address_subnet[] = ""; + $final_address_details[] = $detail_text; + } + } else { + $input_errors[] = sprintf(gettext('IPv6 subnets are not supported in host aliases (%s)'), $address_item); + // Put the user-entered data in the output anyway, so it will be re-displayed for correction. + $input_addresses[] = $address_item; + $input_address_subnet[] = ""; + $final_address_details[] = $detail_text; + } + } else { + list($address_part, $subnet_part) = explode("/", $address_item); + if (!empty($subnet_part)) { + if (is_subnet($address_item)) { + $input_addresses[] = $address_part; + $input_address_subnet[] = $subnet_part; + } else { + // The user typed something like "1.2.3.444/24" or "1.2.3.0/36" or similar rubbish. + // Feed it through without splitting it apart, then it will be caught by the validation loop below. + $input_addresses[] = $address_item; + $input_address_subnet[] = ""; + } + } else { + $input_addresses[] = $address_part; + $input_address_subnet[] = $_POST["address_subnet"][$idx]; + } + + $final_address_details[] = $detail_text; + $alias_address_count++; + } + } + if ($alias_address_count > $max_alias_addresses) { + $input_errors[] = sprintf(gettext('The maximum number of entries in an alias has been exceeded (%s)'), $max_alias_addresses); + break; + } + } + } + + // Validate the input data expanded above. + foreach ($input_addresses as $idx => $input_address) { + if (is_alias($input_address)) { + if (!alias_same_type($input_address, $_POST['type'])) { + // But alias type network can include alias type urltable. Feature#1603. + if (!($_POST['type'] == 'network' && + preg_match("/urltable/i", alias_get_type($input_address)))) { + $wrongaliases .= " " . $input_address; + } + } + } else if ($_POST['type'] == "port") { + if (!is_port($input_address) && !is_portrange($input_address)) { + $input_errors[] = $input_address . " " . gettext("is not a valid port or alias."); + } + } else if ($_POST['type'] == "host" || $_POST['type'] == "network") { + if (is_subnet($input_address) || + (!is_ipaddr($input_address) && !is_hostname($input_address))) { + $input_errors[] = sprintf(gettext('%1$s is not a valid %2$s address, FQDN or alias.'), $input_address, $_POST['type']); + } + } + $tmpaddress = $input_address; + if ($_POST['type'] != "host" && is_ipaddr($input_address) && $input_address_subnet[$idx] <> "") { + if (!is_subnet($input_address . "/" . $input_address_subnet[$idx])) { + $input_errors[] = sprintf(gettext('%s/%s is not a valid subnet.'), $input_address, $input_address_subnet[$idx]); + } else { + $tmpaddress .= "/" . $input_address_subnet[$idx]; + } + } + $address[] = $tmpaddress; + } + unset($desc_fmt_err_found); + if ($wrongaliases <> "") { + $input_errors[] = sprintf(gettext('The alias(es): %s cannot be nested because they are not of the same type.'), $wrongaliases); + } + } + + unset($vertical_bar_err_text); + + // Allow extending of the firewall edit page and include custom input validation + pfSense_handle_custom_code("/usr/local/pkg/firewall_aliases_edit/input_validation"); + + if (!$input_errors) { + $alias['address'] = is_array($address) ? implode(" ", $address) : $address; + $alias['descr'] = $_POST['descr']; + $alias['type'] = $_POST['type']; + $alias['detail'] = implode("||", $final_address_details); + + /* Check to see if alias name needs to be + * renamed on referenced rules and such + */ + if ($_POST['name'] != $_POST['origname']) { + // Firewall rules + update_alias_names_upon_change(array('filter', 'rule'), array('source', 'address'), $_POST['name'], $origname); + update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'address'), $_POST['name'], $origname); + update_alias_names_upon_change(array('filter', 'rule'), array('source', 'port'), $_POST['name'], $origname); + update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'port'), $_POST['name'], $origname); + // NAT Rules + update_alias_names_upon_change(array('nat', 'rule'), array('source', 'address'), $_POST['name'], $origname); + update_alias_names_upon_change(array('nat', 'rule'), array('source', 'port'), $_POST['name'], $origname); + update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'address'), $_POST['name'], $origname); + update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'port'), $_POST['name'], $origname); + update_alias_names_upon_change(array('nat', 'rule'), array('target'), $_POST['name'], $origname); + update_alias_names_upon_change(array('nat', 'rule'), array('local-port'), $_POST['name'], $origname); + // NAT 1:1 Rules + //update_alias_names_upon_change(array('nat', 'onetoone'), array('external'), $_POST['name'], $origname); + //update_alias_names_upon_change(array('nat', 'onetoone'), array('source', 'address'), $_POST['name'], $origname); + update_alias_names_upon_change(array('nat', 'onetoone'), array('destination', 'address'), $_POST['name'], $origname); + // NAT Outbound Rules + update_alias_names_upon_change(array('nat', 'advancedoutbound', 'rule'), array('source', 'network'), $_POST['name'], $origname); + update_alias_names_upon_change(array('nat', 'advancedoutbound', 'rule'), array('sourceport'), $_POST['name'], $origname); + update_alias_names_upon_change(array('nat', 'advancedoutbound', 'rule'), array('destination', 'address'), $_POST['name'], $origname); + update_alias_names_upon_change(array('nat', 'advancedoutbound', 'rule'), array('dstport'), $_POST['name'], $origname); + update_alias_names_upon_change(array('nat', 'advancedoutbound', 'rule'), array('target'), $_POST['name'], $origname); + // Alias in an alias + update_alias_names_upon_change(array('aliases', 'alias'), array('address'), $_POST['name'], $origname); + } + + pfSense_handle_custom_code("/usr/local/pkg/firewall_aliases_edit/pre_write_config"); + + if (isset($id) && $a_aliases[$id]) { + if ($a_aliases[$id]['name'] != $alias['name']) { + foreach ($a_aliases as $aliasid => $aliasd) { + + if ($aliasd['address'] != "") { + $tmpdirty = false; + $tmpaddr = explode(" ", $aliasd['address']); + + foreach ($tmpaddr as $tmpidx => $tmpalias) { + if ($tmpalias == $a_aliases[$id]['name']) { + $tmpaddr[$tmpidx] = $alias['name']; + $tmpdirty = true; + } + } + if ($tmpdirty == true) { + $a_aliases[$aliasid]['address'] = implode(" ", $tmpaddr); + } + } + } + } + $a_aliases[$id] = $alias; + } else { + $a_aliases[] = $alias; + } + + // Sort list + $a_aliases = msort($a_aliases, "name"); + + if (write_config()) { + mark_subsystem_dirty('aliases'); + } + + if (!empty($tab)) { + header("Location: firewall_aliases.php?tab=" . htmlspecialchars ($tab)); + } else { + header("Location: firewall_aliases.php"); + } + exit; + } else { + //we received input errors, copy data to prevent retype + $pconfig['name'] = $_POST['name']; + $pconfig['descr'] = $_POST['descr']; + if (($_POST['type'] == 'url') || ($_POST['type'] == 'url_ports')) { + $pconfig['address'] = implode(" ", $alias['aliasurl']); + } else { + $pconfig['address'] = implode(" ", $address); + } + $pconfig['type'] = $_POST['type']; + $pconfig['detail'] = implode("||", $final_address_details); + } +} + +include("head.inc"); + +$network_str = gettext("Network or FQDN"); +$networks_str = gettext("Network(s)"); +$cidr_str = gettext("CIDR"); +$description_str = gettext("Description"); +$hosts_str = gettext("Host(s)"); +$ip_str = gettext("IP or FQDN"); +$ports_str = gettext("Port(s)"); +$port_str = gettext("Port"); +$url_str = gettext("URL (IPs)"); +$url_ports_str = gettext("URL (Ports)"); +$urltable_str = gettext("URL Table (IPs)"); +$urltable_ports_str = gettext("URL Table (Ports)"); +$update_freq_str = gettext("Update Freq. (days)"); + +$help = array( + 'network' => "Networks are specified in CIDR format. Select the CIDR mask that pertains to each entry. /32 specifies a single IPv4 host, /128 specifies a single IPv6 host, /24 specifies 255.255.255.0, /64 specifies a normal IPv6 network, etc. Hostnames (FQDNs) may also be specified, using a /32 mask for IPv4 or /128 for IPv6. You may also enter an IP range such as 192.168.1.1-192.168.1.254 and a list of CIDR networks will be derived to fill the range.", + 'host' => "Enter as many hosts as you would like. Hosts must be specified by their IP address or fully qualified domain name (FQDN). FQDN hostnames are periodically re-resolved and updated. If multiple IPs are returned by a DNS query, all are used. You may also enter an IP range such as 192.168.1.1-192.168.1.10 or a small subnet such as 192.168.1.16/28 and a list of individual IP addresses will be generated.", + 'port' => "Enter as many ports as you wish. Port ranges can be expressed by separating with a colon.", + 'url' => "Enter as many URLs as you wish. After saving we will download the URL and import the items into the alias. Use only with small sets of IP addresses (less than 3000).", + 'url_ports' => "Enter as many URLs as you wish. After saving we will download the URL and import the items into the alias. Use only with small sets of Ports (less than 3000).", + 'urltable' => "Enter a single URL containing a large number of IPs and/or Subnets. After saving we will download the URL and create a table file containing these addresses. This will work with large numbers of addresses (30,000+) or small numbers.", + 'urltable_ports' => "Enter a single URL containing a list of Port numbers and/or Port ranges. After saving we will download the URL.", +); + +$types = array( + 'host' => 'Host(s)', + 'network' => 'Network(s)', + 'port' => 'Port(s)', + 'url' => 'URL (IPs)', + 'url_ports' => 'URL (Ports)', + 'urltable' => 'URL Table (IPs)', + 'urltable_ports' => 'URL Table (Ports)', +); + +if (empty($tab)) { + if (preg_match("/url/i", $pconfig['type'])) + $tab = 'url'; + else if ($pconfig['type'] == 'host') + $tab = 'ip'; + else + $tab = $pconfig['type']; +} + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); +$form = new Form; + +$form->addGlobal(new Form_Input( + 'tab', + null, + 'hidden', + $tab +)); + +$form->addGlobal(new Form_Input( + 'tab', + null, + 'hidden', + $tab +)); + +$form->addGlobal(new Form_Input( + 'origname', + null, + 'hidden', + $pconfig['name'] +)); + +if (isset($id) && $a_aliases[$id]) +{ + $form->addGlobal(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$section = new Form_Section('Properties'); + +$section->addInput(new Form_Input( + 'name', + 'Name', + 'text', + $pconfig['name'] +))->setPattern('[a-zA-Z0-9_]+')->setHelp('The name of the alias may only consist '. + 'of the characters "a-z, A-Z, 0-9 and _".'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$section->addInput(new Form_Select( + 'type', + 'Type', + isset($pconfig['type']) ? $pconfig['type'] : $tab, + $types +))->toggles(); + +$form->add($section); + +foreach ($types as $type => $typeName) +{ + $section = new Form_Section('Details for '. $typeName); + $section->addClass('toggle-'.$type.' collapse'); + + // Texts are rather long; don't repeat for every input + $section->addInput(new Form_StaticText('', $help[$type])); + + // Only include values for the correct type + if (isset($pconfig['type']) && $type == $pconfig['type']) { + $addresses = explode(' ', $pconfig['address']); + $details = explode('||', $pconfig['detail']); + } + else { + // When creating a new entry show at least one input + $addresses = array(''); + $details = array(); + } + + foreach ($addresses as $idx => $address) + { + $address_subnet = ''; + if (($pconfig['type'] != 'host') && is_subnet($address)) + list($address, $address_subnet) = explode('/', $address); + + if (substr($type, 0, 3) == 'url') { + $group = new Form_Group('URL to download'); + + $group->add(new Form_Input( + 'address', + 'URL to download', + 'url', + $address + )); + + if (in_array($type, ['urltable', 'urltable_ports'])) + { + $group->add(new Form_Input( + 'frequency', + 'Update frequency (days)', + 'number', + $address_subnet, + ['min' => 1] + )); + } + } + elseif ($type == 'port') { + $group = new Form_Group('Port(s)'); + $group->add(new Form_Input( + 'address', + 'Port', + $address + )); + + $group->add(new Form_Input( + 'detail', + 'Description (not parsed)', + 'text', + $details[$idx] + )); + } + else { + $group = new Form_Group('IP or FQDN'); + + $grpaddress = new Form_IpAddress( + 'address', + 'IP or FQDN', + $address + ); + + $grpaddress->addMask(address_subnet, $pconfig['address_subnet']); + + $group->add($grpaddress); + + $group->add(new Form_Input( + 'detail', + 'Description (not parsed)', + 'text', + $details[$idx] + )); + } + + $group->enableDuplication(); + $section->add($group); + } + + $form->add($section); +} + +print $form; +?> + +<script> +//<![CDATA[ +events.push(function(){ + + // Disable address_subnet if type == 'host' + + $("[id^='address_subnet']").prop("disabled", ($('#type').val() == 'host')); + + $('#type').on('change', function() { + $("[id^='address_subnet']").prop("disabled", ($('#type').val() == 'host')); + }); + +}); +//]]> +</script> + +<?php +include("foot.inc"); diff --git a/src/usr/local/www/firewall_aliases_import.php b/src/usr/local/www/firewall_aliases_import.php new file mode 100755 index 0000000..0199426 --- /dev/null +++ b/src/usr/local/www/firewall_aliases_import.php @@ -0,0 +1,234 @@ +<?php +/* $Id$ */ +/* + firewall_aliases_import.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-firewall-alias-import +##|*NAME=Firewall: Alias: Import page +##|*DESCR=Allow access to the 'Firewall: Alias: Import' page. +##|*MATCH=firewall_aliases_import.php* +##|-PRIV + + +// Keywords not allowed in names +$reserved_keywords = array("all", "pass", "block", "out", "queue", "max", "min", "pptp", "pppoe", "L2TP", "OpenVPN", "IPsec"); + +require("guiconfig.inc"); +require_once("util.inc"); +require_once("filter.inc"); +require("shaper.inc"); + +$pgtitle = array(gettext("Firewall"), gettext("Aliases"), gettext("Bulk import")); + +$referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/firewall_aliases.php'); + +// Add all Load balance names to reserved_keywords +if (is_array($config['load_balancer']['lbpool'])) { + foreach ($config['load_balancer']['lbpool'] as $lbpool) { + $reserved_keywords[] = $lbpool['name']; + } +} + +$reserved_ifs = get_configured_interface_list(false, true); +$reserved_keywords = array_merge($reserved_keywords, $reserved_ifs, $reserved_table_names); + +if (!is_array($config['aliases']['alias'])) { + $config['aliases']['alias'] = array(); +} +$a_aliases = &$config['aliases']['alias']; + +if($_POST['aliasimport'] != "") { + $reqdfields = explode(" ", "name aliasimport"); + $reqdfieldsn = array(gettext("Name"), gettext("Aliases")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (is_validaliasname($_POST['name']) == false) { + $input_errors[] = gettext("The alias name may only consist of the characters") . " a-z, A-Z, 0-9, _."; + } + + /* check for name duplicates */ + if (is_alias($_POST['name'])) { + $input_errors[] = gettext("An alias with this name already exists."); + } + + + /* Check for reserved keyword names */ + foreach ($reserved_keywords as $rk) { + if ($rk == $_POST['name']) { + $input_errors[] = sprintf(gettext("Cannot use a reserved keyword as alias name %s"), $rk); + } + } + + /* check for name interface description conflicts */ + foreach ($config['interfaces'] as $interface) { + if ($interface['descr'] == $_POST['name']) { + $input_errors[] = gettext("An interface description with this name already exists."); + break; + } + } + + if ($_POST['aliasimport']) { + $tocheck = explode("\n", $_POST['aliasimport']); + $imported_ips = array(); + $imported_descs = array(); + $desc_len_err_found = false; + $desc_fmt_err_found = false; + foreach ($tocheck as $impline) { + $implinea = explode(" ", trim($impline), 2); + $impip = $implinea[0]; + $impdesc = trim($implinea[1]); + if (strlen($impdesc) < 200) { + if ((strpos($impdesc, "||") === false) && (substr($impdesc, 0, 1) != "|") && (substr($impdesc, -1, 1) != "|")) { + $iprange_type = is_iprange($impip); + if ($iprange_type == 4) { + list($startip, $endip) = explode('-', $impip); + $rangesubnets = ip_range_to_subnet_array($startip, $endip); + $imported_ips = array_merge($imported_ips, $rangesubnets); + $rangedescs = array_fill(0, count($rangesubnets), $impdesc); + $imported_descs = array_merge($imported_descs, $rangedescs); + } else if ($iprange_type == 6) { + $input_errors[] = sprintf(gettext('IPv6 address ranges are not supported (%s)'), $impip); + } else if (!is_ipaddr($impip) && !is_subnet($impip) && !is_hostname($impip) && !empty($impip)) { + $input_errors[] = sprintf(gettext("%s is not an IP address. Please correct the error to continue"), $impip); + } elseif (!empty($impip)) { + $imported_ips[] = $impip; + $imported_descs[] = $impdesc; + } + } else { + if (!$desc_fmt_err_found) { + $input_errors[] = gettext("Descriptions may not start or end with vertical bar (|) or contain double vertical bar ||."); + $desc_fmt_err_found = true; + } + } + } else { + if (!$desc_len_err_found) { + /* Note: The 200 character limit is just a practical check to avoid accidents */ + /* if the user pastes a large number of IP addresses without line breaks. */ + $input_errors[] = gettext("Descriptions must be less than 200 characters long."); + $desc_len_err_found = true; + } + } + } + unset($desc_len_err_found, $desc_fmt_err_found); + } + + if (!$input_errors && is_array($imported_ips)) { + $alias = array(); + $alias['address'] = implode(" ", $imported_ips); + $alias['detail'] = implode("||", $imported_descs); + $alias['name'] = $_POST['name']; + $alias['type'] = "network"; + $alias['descr'] = $_POST['descr']; + unset($imported_ips, $imported_descs); + $a_aliases[] = $alias; + + // Sort list + $a_aliases = msort($a_aliases, "name"); + + if (write_config()) { + mark_subsystem_dirty('aliases'); + } + pfSenseHeader("firewall_aliases.php"); + + exit; + } +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); +$form = new Form; +$section = new Form_Section('Alias details'); + +$section->addInput(new Form_Input( + 'name', + 'Alias Name', + 'text', + $_POST['name'] +))->setPattern('[a-zA-Z0-9_]+')->setHelp('The name of the alias may only consist '. + 'of the characters "a-z, A-Z, 0-9 and _".'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $_POST['descr'] +))->setHelp('You may enter a description here for your reference (not '. + 'parsed).'); + +$section->addInput(new Form_Textarea( + 'aliasimport', + 'Aliases to import', + $_POST["aliasimport"] +))->setHelp('Paste in the aliases to '. + 'import separated by a carriage return. Common examples are lists of IPs, '. + 'networks, blacklists, etc.The list may contain IP addresses, with or without '. + 'CIDR prefix, IP ranges, blank lines (ignored) and an optional description after '. + 'each IP. e.g.:<ul><li>172.16.1.2</li><li>172.16.0.0/24</li><li>10.11.12.100-'. + '10.11.12.200</li><li>192.168.1.254 Home router</li><li>10.20.0.0/16 Office '. + 'network</li><li>10.40.1.10-10.40.1.19 Managed switches</li></ul>'); + +$form->add($section); +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_nat.php b/src/usr/local/www/firewall_nat.php new file mode 100644 index 0000000..651428d --- /dev/null +++ b/src/usr/local/www/firewall_nat.php @@ -0,0 +1,445 @@ +<?php +/* $Id$ */ +/* + firewall_nat.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004 Scott Ullrich + * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net> + * originally part of m0n0wall (http://m0n0.ch/wall) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: nat +*/ + +##|+PRIV +##|*IDENT=page-firewall-nat-portforward +##|*NAME=Firewall: NAT: Port Forward page +##|*DESCR=Allow access to the 'Firewall: NAT: Port Forward' page. +##|*MATCH=firewall_nat.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("itemid.inc"); + +if (!is_array($config['nat']['rule'])) { + $config['nat']['rule'] = array(); +} + +$a_nat = &$config['nat']['rule']; + +/* update rule order, POST[rule] is an array of ordered IDs */ +if (is_array($_POST['rule']) && !empty($_POST['rule'])) { + $a_nat_new = array(); + + // if a rule is not in POST[rule], it has been deleted by the user + foreach ($_POST['rule'] as $id) + $a_nat_new[] = $a_nat[$id]; + + $a_nat = $a_nat_new; + + if (write_config()) + mark_subsystem_dirty('filter'); + + header("Location: firewall_nat.php"); + exit; +} + +/* if a custom message has been passed along, lets process it */ +if ($_GET['savemsg']) { + $savemsg = $_GET['savemsg']; +} + +if ($_POST) { + + $pconfig = $_POST; + + if ($_POST['apply']) { + + $retval = 0; + + $retval |= filter_configure(); + $savemsg = get_std_save_message($retval); + + pfSense_handle_custom_code("/usr/local/pkg/firewall_nat/apply"); + + if ($retval == 0) { + clear_subsystem_dirty('natconf'); + clear_subsystem_dirty('filter'); + } + + } +} + +if ($_GET['act'] == "del") { + if ($a_nat[$_GET['id']]) { + + if (isset($a_nat[$_GET['id']]['associated-rule-id'])) { + delete_id($a_nat[$_GET['id']]['associated-rule-id'], $config['filter']['rule']); + $want_dirty_filter = true; + } + unset($a_nat[$_GET['id']]); + + if (write_config()) { + mark_subsystem_dirty('natconf'); + if ($want_dirty_filter) { + mark_subsystem_dirty('filter'); + } + } + + header("Location: firewall_nat.php"); + exit; + } +} + +if (isset($_POST['del_x'])) { + /* delete selected rules */ + if (is_array($_POST['rule']) && count($_POST['rule'])) { + foreach ($_POST['rule'] as $rulei) { + $target = $rule['target']; + // Check for filter rule associations + if (isset($a_nat[$rulei]['associated-rule-id'])) { + delete_id($a_nat[$rulei]['associated-rule-id'], $config['filter']['rule']); + + mark_subsystem_dirty('filter'); + } + unset($a_nat[$rulei]); + } + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat.php"); + exit; + } + +} else { + /* yuck - IE won't send value attributes for image buttons, while Mozilla does - so we use .x/.y to find move button clicks instead... */ + unset($movebtn); + foreach ($_POST as $pn => $pd) { + if (preg_match("/move_(\d+)_x/", $pn, $matches)) { + $movebtn = $matches[1]; + break; + } + } + /* move selected rules before this rule */ + if (isset($movebtn) && is_array($_POST['rule']) && count($_POST['rule'])) { + $a_nat_new = array(); + + /* copy all rules < $movebtn and not selected */ + for ($i = 0; $i < $movebtn; $i++) { + if (!in_array($i, $_POST['rule'])) { + $a_nat_new[] = $a_nat[$i]; + } + } + + /* copy all selected rules */ + for ($i = 0; $i < count($a_nat); $i++) { + if ($i == $movebtn) { + continue; + } + if (in_array($i, $_POST['rule'])) { + $a_nat_new[] = $a_nat[$i]; + } + } + + /* copy $movebtn rule */ + if ($movebtn < count($a_nat)) { + $a_nat_new[] = $a_nat[$movebtn]; + } + + /* copy all rules > $movebtn and not selected */ + for ($i = $movebtn+1; $i < count($a_nat); $i++) { + if (!in_array($i, $_POST['rule'])) { + $a_nat_new[] = $a_nat[$i]; + } + } + $a_nat = $a_nat_new; + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat.php"); + exit; + } +} + +$closehead = false; +$pgtitle = array(gettext("Firewall"), gettext("NAT"), gettext("Port Forward")); +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('natconf')) + print_info_box_np(gettext('The NAT configuration has been changed.') . '<br />' . + gettext('You must apply the changes in order for them to take effect.') . '<br />'); + +$tab_array = array(); +$tab_array[] = array(gettext("Port Forward"), true, "firewall_nat.php"); +$tab_array[] = array(gettext("1:1"), false, "firewall_nat_1to1.php"); +$tab_array[] = array(gettext("Outbound"), false, "firewall_nat_out.php"); +$tab_array[] = array(gettext("NPt"), false, "firewall_nat_npt.php"); +display_top_tabs($tab_array); +?> + +<form action="firewall_nat.php" method="post" name="iform"> + <div class="panel panel-default"> + <div class="panel-heading"><?=gettext('Rules')?></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><!-- Rule type --></th> + <th><?=gettext("If")?></th> + <th><?=gettext("Proto")?></th> + <th><?=gettext("Src. addr")?></th> + <th><?=gettext("Src. ports")?></th> + <th><?=gettext("Dest. addr")?></th> + <th><?=gettext("Dest. ports")?></th> + <th><?=gettext("NAT IP")?></th> + <th><?=gettext("NAT Ports")?></th> + <th><?=gettext("Description")?></th> + <th><?=gettext("Actions")?></th> + </tr> + </thead> + <tbody class='user-entries'> +<?php + +$nnats = $i = 0; + +foreach ($a_nat as $natent): + + $alias = rule_columns_with_alias( + $natent['source']['address'], + pprint_port($natent['source']['port']), + $natent['destination']['address'], + pprint_port($natent['destination']['port']) + ); + + /* if user does not have access to edit an interface skip on to the next record */ + if(!have_natpfruleint_access($natent['interface'])) + continue; +?> + + <tr id="fr<?=$nnats?>"> + <td> +<?php + if($natent['associated-rule-id'] == "pass"): +?> + <i class="icon-play" title="<?=gettext("All traffic matching this NAT entry is passed")?>"></i> +<?php + elseif (!empty($natent['associated-rule-id'])): +?> + <i class="icon-random" title="<?=gettext("Firewall rule ID ")?><?=htmlspecialchars($nnatid)?> . <?=gettext('is managed by this rule')?>"></i> +<?php + endif; +?> + </td> + <td> + <?=$textss?> +<?php + if (!$natent['interface']) + echo htmlspecialchars(convert_friendly_interface_to_friendly_descr("wan")); + else + echo htmlspecialchars(convert_friendly_interface_to_friendly_descr($natent['interface'])); +?> + <?=$textse?> + </td> + + <td> + <input type="hidden" name="rule[]" value="<?=$i?>" /> + <?=$textss?><?=strtoupper($natent['protocol'])?><?=$textse?> + </td> + + <td> + + +<?php + if (isset($alias['src'])): +?> + <a href="/firewall_aliases_edit.php?id=<?=$alias['src']?>" data-toggle="popover" data-trigger="hover focus" title="Alias details" data-content="<?=alias_info_popup($alias['src'])?>" data-html="true"> +<?php + endif; +?> + <?=htmlspecialchars(pprint_address($natent['source']))?> +<?php + if (isset($alias['src'])): +?> + <i class='icon icon-pencil'></i></a> +<?php + endif; +?> + </td> + <td> +<?php + if (isset($alias['srcport'])): +?> + <a href="/firewall_aliases_edit.php?id=<?=$alias['srcport']?>" data-toggle="popover" data-trigger="hover focus" title="Alias details" data-content="<?=alias_info_popup($alias['srcport'])?>" data-html="true"> +<?php + endif; +?> + <?=htmlspecialchars(pprint_port($natent['source']['port']))?> +<?php + if (isset($alias['srcport'])): +?> + <i class='icon icon-pencil'></i></a> +<?php + endif; +?> + </td> + + <td> +<?php + if (isset($alias['dst'])): +?> + <a href="/firewall_aliases_edit.php?id=<?=$alias['dst']?>" data-toggle="popover" data-trigger="hover focus" title="Alias details" data-content="<?=alias_info_popup($alias['dst'])?>" data-html="true"> +<?php + endif; +?> + <?=htmlspecialchars(pprint_address($natent['destination']))?> +<?php + if (isset($alias['dst'])): +?> + <i class='icon icon-pencil'></i></a> +<?php + endif; +?> + </td> + <td> +<?php + if (isset($alias['dstport'])): +?> + <a href="/firewall_aliases_edit.php?id=<?=$alias['dstport']?>" data-toggle="popover" data-trigger="hover focus" title="Alias details" data-content="<?=alias_info_popup($alias['dstport'])?>" data-html="true"> +<?php + endif; +?> + <?=htmlspecialchars(pprint_port($natent['destination']['port']))?> +<?php + if (isset($alias['dstport'])): +?> + <i class='icon icon-pencil'></i></a> +<?php + endif; +?> + </td> + + <td > + <?=htmlspecialchars($natent['target'])?> + </td> + <td> +<?php + $localport = $natent['local-port']; + + list($dstbeginport, $dstendport) = explode("-", $natent['destination']['port']); + + if ($dstendport) { + $localendport = $natent['local-port'] + $dstendport - $dstbeginport; + $localport .= '-' . $localendport; + } +?> + <?=htmlspecialchars(pprint_port($localport))?> + </td> + + <td> + <?=htmlspecialchars($natent['descr'])?> + </td> + <td> + <a class="btn btn-xs btn-info" title="<?=gettext("Edit rule"); ?>" href="firewall_nat_edit.php?id=<?=$i?>"><?=gettext("Edit"); ?></a> + <a class="btn btn-xs btn-danger" title="<?=gettext("Delete rule")?>" href="firewall_nat.php?act=del&id=<?=$i?>"><?=gettext("Del")?></a> + <a class="btn btn-xs btn-success" title="<?=gettext("Add a new NAT based on this one")?>" href="firewall_nat_edit.php?dup=<?=$i?>"><?=gettext("Clone")?></a> + </td> + </tr> +<?php + $i++; + $nnats++; +endforeach; +?> + </tbody> + </table> + </div> + </div> + + <div class="pull-right"> + <a href="firewall_nat_edit.php?after=-1" class="btn btn-sm btn-success" title="<?=gettext('Add new rule')?>"><?=gettext('Add new rule')?></a> + <input type="submit" id="order-store" class="btn btn-primary btn-sm" value="store changes" disabled="disabled" /> + </div> +</form> + +<script> +events.push(function() { + // Make rules draggable/sortable + $('table tbody.user-entries').sortable({ + cursor: 'grabbing', + update: function(event, ui) { + $('#order-store').removeAttr('disabled'); + } + }); +}); +</script> +<?php + +if(count($a_nat) > 0) { +?> +<!-- Legend --> +<div> + <dl class="dl-horizontal responsive"> + <dt><?=gettext('Legend')?></dt> <dd></dd> + <dt><i class="icon icon-play"></i></dt> <dd><?=gettext('Pass')?></dd> + <dt><i class="icon icon-random"></i></dt> <dd><?=gettext('Linked rule')?></dd> + </dl> +</div> + +<?php +} + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_nat_1to1.php b/src/usr/local/www/firewall_nat_1to1.php new file mode 100644 index 0000000..753d030 --- /dev/null +++ b/src/usr/local/www/firewall_nat_1to1.php @@ -0,0 +1,334 @@ +<?php +/* $Id$ */ +/* + firewall_nat_1to1.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net> + * part of m0n0wall (http://m0n0.ch/wall) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: nat +*/ + +##|+PRIV +##|*IDENT=page-firewall-nat-1-1 +##|*NAME=Firewall: NAT: 1:1 page +##|*DESCR=Allow access to the 'Firewall: NAT: 1:1' page. +##|*MATCH=firewall_nat_1to1.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +if (!is_array($config['nat']['onetoone'])) { + $config['nat']['onetoone'] = array(); +} + +$a_1to1 = &$config['nat']['onetoone']; + +/* update rule order, POST[rule] is an array of ordered IDs */ +if (is_array($_POST['rule']) && !empty($_POST['rule'])) { + $a_1to1_new = array(); + + // if a rule is not in POST[rule], it has been deleted by the user + foreach ($_POST['rule'] as $id) + $a_1to1_new[] = $a_1to1[$id]; + + $a_1to1 = $a_1to1_new; + + if (write_config()) + mark_subsystem_dirty('filter'); + + header("Location: firewall_nat_1to1.php"); + exit; +} + +if ($_POST) { + $pconfig = $_POST; + + if ($_POST['apply']) { + $retval = 0; + $retval |= filter_configure(); + $savemsg = get_std_save_message($retval); + + if ($retval == 0) { + clear_subsystem_dirty('natconf'); + clear_subsystem_dirty('filter'); + } + } +} + +if ($_GET['act'] == "del") { + if ($a_1to1[$_GET['id']]) { + unset($a_1to1[$_GET['id']]); + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat_1to1.php"); + exit; + } +} + +if (isset($_POST['del_x'])) { + /* delete selected rules */ + if (is_array($_POST['rule']) && count($_POST['rule'])) { + foreach ($_POST['rule'] as $rulei) { + unset($a_1to1[$rulei]); + } + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat_1to1.php"); + exit; + } + +} else if ($_GET['act'] == "toggle") { + if ($a_1to1[$_GET['id']]) { + if (isset($a_1to1[$_GET['id']]['disabled'])) { + unset($a_1to1[$_GET['id']]['disabled']); + } else { + $a_1to1[$_GET['id']]['disabled'] = true; + } + if (write_config("Firewall: NAT: Outbound, enable/disable NAT rule")) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat_1to1.php"); + exit; + } +} else { + /* yuck - IE won't send value attributes for image buttons, while Mozilla does - so we use .x/.y to find move button clicks instead... */ + unset($movebtn); + foreach ($_POST as $pn => $pd) { + if (preg_match("/move_(\d+)_x/", $pn, $matches)) { + $movebtn = $matches[1]; + break; + } + } + /* move selected rules before this rule */ + if (isset($movebtn) && is_array($_POST['rule']) && count($_POST['rule'])) { + $a_1to1_new = array(); + + /* copy all rules < $movebtn and not selected */ + for ($i = 0; $i < $movebtn; $i++) { + if (!in_array($i, $_POST['rule'])) { + $a_1to1_new[] = $a_1to1[$i]; + } + } + + /* copy all selected rules */ + for ($i = 0; $i < count($a_1to1); $i++) { + if ($i == $movebtn) { + continue; + } + if (in_array($i, $_POST['rule'])) { + $a_1to1_new[] = $a_1to1[$i]; + } + } + + /* copy $movebtn rule */ + if ($movebtn < count($a_1to1)) { + $a_1to1_new[] = $a_1to1[$movebtn]; + } + + /* copy all rules > $movebtn and not selected */ + for ($i = $movebtn+1; $i < count($a_1to1); $i++) { + if (!in_array($i, $_POST['rule'])) { + $a_1to1_new[] = $a_1to1[$i]; + } + } + if (count($a_1to1_new) > 0) { + $a_1to1 = $a_1to1_new; + } + + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat_1to1.php"); + exit; + } +} + +$pgtitle = array(gettext("Firewall"), gettext("NAT"), gettext("1:1")); +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('natconf')) + print_info_box_np(gettext('The NAT configuration has been changed.') . '<br />' . + gettext('You must apply the changes in order for them to take effect.') . '<br />'); + +$tab_array = array(); +$tab_array[] = array(gettext("Port Forward"), false, "firewall_nat.php"); +$tab_array[] = array(gettext("1:1"), true, "firewall_nat_1to1.php"); +$tab_array[] = array(gettext("Outbound"), false, "firewall_nat_out.php"); +$tab_array[] = array(gettext("NPt"), false, "firewall_nat_npt.php"); +display_top_tabs($tab_array); +?> +<form action="firewall_nat_1to1.php" method="post"> + <div class="panel panel-default"> + <div class="panel-heading"><?=gettext("NAT 1 to 1 mappings")?></div> + <div id="mainarea" class="table-responsive panel-body"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><!-- icon --></th> + <th><?=gettext("Interface"); ?></th> + <th><?=gettext("External IP"); ?></th> + <th><?=gettext("Internal IP"); ?></th> + <th><?=gettext("Destination IP"); ?></th> + <th><?=gettext("Description"); ?></th> + <th><?=gettext('Actions')?></th> + </tr> + </thead> + <tbody class="user-entries"> +<?php + $textse = "</span>"; + $i = 0; + foreach ($a_1to1 as $natent): + if (isset($natent['disabled'])) { + $textss = "<span class=\"gray\">"; + $iconfn = "pass_d"; + } else { + $textss = "<span>"; + $iconfn = "pass"; + } +?> + <tr id="fr<?=$i?>"> + <td> + <a href="?act=toggle&id=<?=$i?>"> + <i class="<?= ($iconfn == "pass") ? "icon-ok":"icon-remove"?>" title="<?=gettext("click to toggle enabled/disabled status")?>"></i> + </a> + </td> + <td> +<?php + echo $textss; + if (!$natent['interface']) { + echo htmlspecialchars(convert_friendly_interface_to_friendly_descr("wan")); + } else { + echo htmlspecialchars(convert_friendly_interface_to_friendly_descr($natent['interface'])); + } + echo $textse; +?> + </td> + <td> + <input type="hidden" name="rule[]" value="<?=$i?>" /> +<?php + $source_net = pprint_address($natent['source']); + $source_cidr = strstr($source_net, '/'); + echo $textss . $natent['external'] . $source_cidr . $textse; +?> + </td> + <td> +<?php + echo $textss . $source_net . $textse; +?> + </td> + <td> +<?php + echo $textss . pprint_address($natent['destination']) . $textse; +?> + </td> + <td> +<?php + echo $textss . htmlspecialchars($natent['descr']) . ' ' . $textse; +?> + </td> + + <td> + <a class="btn btn-xs btn-info" title="<?=gettext("Edit rule")?>" href="firewall_nat_1to1.php?id=<?=$i?>"><?=gettext("Edit")?></a> + <a class="btn btn-xs btn-danger" title="<?=gettext("Delete rule")?>" href="firewall_nat_1to1.php?act=del&id=<?=$i?>"><?=gettext("Del")?></a> + <a class="btn btn-xs btn-success" title="<?=gettext("Add a new rule based on this one")?>" href="firewall_nat_1to1_edit.php?dup=<?=$i?>"><?=gettext("Clone")?></a> + </td> + + </tr> +<?php + $i++; + endforeach; +?> + </tbody> + </table> + </div> + </div> + + <nav class="action-buttons"> + <a href="firewall_nat_1to1_edit.php?after=-1" class="btn btn-sm btn-success" title="<?=gettext('Add new mapping')?>"><?=gettext('Add new mapping')?></a> + <input type="submit" id="order-store" class="btn btn-primary btn-sm" value="store changes" disabled="disabled" /> + </nav> +</form> + +<div> +<?php + +print_info_box(gettext('Depending on the way your WAN connection is setup, you may also need a ') . '<a href="firewall_virtual_ip.php">' . + gettext("Virtual IP.") . '</a>' . '<br />' . + gettext('If you add a 1:1 NAT entry for any of the interface IPs on this system, ' . + 'it will make this system inaccessible on that IP address. i.e. if ' . + 'you use your WAN IP address, any services on this system (IPsec, OpenVPN server, etc.) ' . + 'using the WAN IP address will no longer function.')); +?> +</div> + +<script> +events.push(function() { + // Make rules draggable/sortable + $('table tbody.user-entries').sortable({ + cursor: 'grabbing', + update: function(event, ui) { + $('#order-store').removeAttr('disabled'); + } + }); +}); +</script> +<?php include("foot.inc"); ?>
\ No newline at end of file diff --git a/src/usr/local/www/firewall_nat_1to1_edit.php b/src/usr/local/www/firewall_nat_1to1_edit.php new file mode 100644 index 0000000..ba92003 --- /dev/null +++ b/src/usr/local/www/firewall_nat_1to1_edit.php @@ -0,0 +1,581 @@ +<?php +/* $Id$ */ +/* + firewall_nat_1to1_edit.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net> + * part of m0n0wall (http://m0n0.ch/wall) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: nat +*/ + +##|+PRIV +##|*IDENT=page-firewall-nat-1-1-edit +##|*NAME=Firewall: NAT: 1:1: Edit page +##|*DESCR=Allow access to the 'Firewall: NAT: 1:1: Edit' page. +##|*MATCH=firewall_nat_1to1_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("interfaces.inc"); +require_once("filter.inc"); +require("shaper.inc"); + +$referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/firewall_nat_1to1.php'); + +$specialsrcdst = explode(" ", "any pptp pppoe l2tp openvpn"); +$ifdisp = get_configured_interface_with_descr(); + +foreach ($ifdisp as $kif => $kdescr) { + $specialsrcdst[] = "{$kif}"; + $specialsrcdst[] = "{$kif}ip"; +} + +if (!is_array($config['nat']['onetoone'])) { + $config['nat']['onetoone'] = array(); +} + +$a_1to1 = &$config['nat']['onetoone']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +$after = $_GET['after']; +if (isset($_POST['after'])) { + $after = $_POST['after']; +} + +if (isset($_GET['dup'])) { + $id = $_GET['dup']; + $after = $_GET['dup']; +} + +if (isset($id) && $a_1to1[$id]) { + $pconfig['disabled'] = isset($a_1to1[$id]['disabled']); + + address_to_pconfig($a_1to1[$id]['source'], $pconfig['src'], + $pconfig['srcmask'], $pconfig['srcnot'], + $pconfig['srcbeginport'], $pconfig['srcendport']); + + address_to_pconfig($a_1to1[$id]['destination'], $pconfig['dst'], + $pconfig['dstmask'], $pconfig['dstnot'], + $pconfig['dstbeginport'], $pconfig['dstendport']); + + $pconfig['interface'] = $a_1to1[$id]['interface']; + if (!$pconfig['interface']) { + $pconfig['interface'] = "wan"; + } + + $pconfig['external'] = $a_1to1[$id]['external']; + $pconfig['descr'] = $a_1to1[$id]['descr']; + $pconfig['natreflection'] = $a_1to1[$id]['natreflection']; +} else { + $pconfig['interface'] = "wan"; +} + +if (isset($_GET['dup'])) { + unset($id); +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + /* run through $_POST items encoding HTML entities so that the user + * cannot think he is slick and perform a XSS attack on the unwilling + */ + foreach ($_POST as $key => $value) { + $temp = str_replace(">", "", $value); + $newpost = htmlentities($temp); + + if($newpost != $temp) + $input_errors[] = sprintf(gettext("Invalid characters detected (%s). Please remove invalid characters and save again."),$temp); + } + + /* input validation */ + $reqdfields = explode(" ", "interface external"); + $reqdfieldsn = array(gettext("Interface"), gettext("External subnet")); + + if ($_POST['srctype'] == "single" || $_POST['srctype'] == "network") { + $reqdfields[] = "src"; + $reqdfieldsn[] = gettext("Source address"); + } + + if ($_POST['dsttype'] == "single" || $_POST['dsttype'] == "network") { + $reqdfields[] = "dst"; + $reqdfieldsn[] = gettext("Destination address"); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ($_POST['external']) { + $_POST['external'] = trim($_POST['external']); + } + if ($_POST['src']) { + $_POST['src'] = trim($_POST['src']); + } + if ($_POST['dst']) { + $_POST['dst'] = trim($_POST['dst']); + } + + if (is_specialnet($_POST['srctype'])) { + $_POST['src'] = $_POST['srctype']; + $_POST['srcmask'] = 0; + } else if ($_POST['srctype'] == "single") { + $_POST['srcmask'] = 32; + } + + if (is_specialnet($_POST['dsttype'])) { + $_POST['dst'] = $_POST['dsttype']; + $_POST['dstmask'] = 0; + } else if ($_POST['dsttype'] == "single") { + $_POST['dstmask'] = 32; + } else if (is_ipaddr($_POST['dsttype'])) { + $_POST['dst'] = $_POST['dsttype']; + $_POST['dstmask'] = 32; + $_POST['dsttype'] = "single"; + } + + /* For external, user can enter only ip's */ + if (($_POST['external'] && !is_ipaddr($_POST['external']))) { + $input_errors[] = gettext("A valid external subnet must be specified."); + } + + /* For dst, if user enters an alias and selects "network" then disallow. */ + if ($_POST['dsttype'] == "network" && is_alias($_POST['dst'])) { + $input_errors[] = gettext("You must specify single host or alias for alias entries."); + } + + /* For src, user can enter only ip's or networks */ + if (!is_specialnet($_POST['srctype'])) { + if (($_POST['src'] && !is_ipaddr($_POST['src']))) { + $input_errors[] = sprintf(gettext("%s is not a valid internal IP address."), $_POST['src']); + } + + if (($_POST['srcmask'] && !is_numericint($_POST['srcmask']))) { + $input_errors[] = gettext("A valid internal bit count must be specified."); + } + } + + /* For dst, user can enter ip's, networks or aliases */ + if (!is_specialnet($_POST['dsttype'])) { + if (($_POST['dst'] && !is_ipaddroralias($_POST['dst']))) { + $input_errors[] = sprintf(gettext("%s is not a valid destination IP address or alias."), $_POST['dst']); + } + + if (($_POST['dstmask'] && !is_numericint($_POST['dstmask']))) { + $input_errors[] = gettext("A valid destination bit count must be specified."); + } + } + + /* check for overlaps with other 1:1 */ + foreach ($a_1to1 as $natent) { + if (isset($id) && ($a_1to1[$id]) && ($a_1to1[$id] === $natent)) { + continue; + } + + if (check_subnets_overlap($_POST['internal'], $_POST['subnet'], $natent['internal'], $natent['subnet'])) { + //$input_errors[] = "Another 1:1 rule overlaps with the specified internal subnet."; + //break; + } + } + + if (!$input_errors) { + $natent = array(); + + $natent['disabled'] = isset($_POST['disabled']) ? true:false; + $natent['external'] = $_POST['external']; + $natent['descr'] = $_POST['descr']; + $natent['interface'] = $_POST['interface']; + + pconfig_to_address($natent['source'], $_POST['src'], + $_POST['srcmask'], $_POST['srcnot']); + + pconfig_to_address($natent['destination'], $_POST['dst'], + $_POST['dstmask'], $_POST['dstnot']); + + if ($_POST['natreflection'] == "enable" || $_POST['natreflection'] == "disable") { + $natent['natreflection'] = $_POST['natreflection']; + } else { + unset($natent['natreflection']); + } + + if (isset($id) && $a_1to1[$id]) { + $a_1to1[$id] = $natent; + } else { + if (is_numeric($after)) { + array_splice($a_1to1, $after+1, 0, array($natent)); + } else { + $a_1to1[] = $natent; + } + } + + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat_1to1.php"); + exit; + } +} + +$pgtitle = array(gettext("Firewall"), gettext("NAT"), gettext("1:1"), gettext("Edit")); +include("head.inc"); + +function build_srctype_list() { + global $pconfig, $ifdisp; + + $list = array('any' => 'Any', 'single' => 'Single host or alias', 'network' => 'Network'); + + $sel = is_specialnet($pconfig['src']); + + if(have_ruleint_access("pptp")) + $list['pptp'] = 'PPTP clients'; + + if(have_ruleint_access("pppoe")) + $list['pppoe'] = 'PPPoE clients'; + + if(have_ruleint_access("l2tp")) + $list['l2tp'] = 'L2TP clients'; + + foreach ($ifdisp as $ifent => $ifdesc) { + if(have_ruleint_access($ifent)) { + $list[$ifent] = $ifdesc . ' net'; + $list[$ifent . 'ip'] = $ifdesc . ' address'; + } + } + + return($list); +} + +function srctype_selected() { + global $pconfig; + + $sel = is_specialnet($pconfig['src']); + + if(!$sel) { + if(($pconfig['srcmask'] == 32) || (!isset($pconfig['srcmask']))) + return('single'); + + return('network'); + } + + return($pconfig['src']); +} + +function build_dsttype_list() { + global $pconfig, $config, $ifdisp; + + $sel = is_specialnet($pconfig['dst']); + $list = array('any' => 'Any', 'single' => 'Single host or alias', 'network' => 'Network', '(self)' => 'This Firewall (self)'); + + if(have_ruleint_access("pptp")) + $list['pptp'] = 'PPTP clients'; + + if(have_ruleint_access("pppoe")) + $list['pppoe'] = 'PPPoE clients'; + + if(have_ruleint_access("l2tp")) + $list['l2tp'] = 'L2TP clients'; + + foreach ($ifdisp as $if => $ifdesc) { + if(have_ruleint_access($if)) { + $list[$if] = $ifdesc; + $list[$if . 'ip'] = $ifdesc . ' address'; + } + } + + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $sn) { + if (isset($sn['noexpand'])) + continue; + + if ($sn['mode'] == "proxyarp" && $sn['type'] == "network") { + $start = ip2long32(gen_subnet($sn['subnet'], $sn['subnet_bits'])); + $end = ip2long32(gen_subnet_max($sn['subnet'], $sn['subnet_bits'])); + $len = $end - $start; + + for ($i = 0; $i <= $len; $i++) { + $snip = long2ip32($start+$i); + + $list[$snip] = $snip . ' (' . $sn['descr'] . ')'; + } + + $list[$sn['subnet']] = $sn['subnet'] . ' (' . $sn['descr'] . ')'; + } + } + } + + return($list); +} + +function dsttype_selected() { + global $pconfig; + + $sel = is_specialnet($pconfig['dst']); + + if(empty($pconfig['dst'] || $pconfig['dst'] == "any")) + return('any'); + + if(!$sel) { + if($pconfig['dstmask'] == 32) + return('single'); + + return('network'); + } + + return($pconfig['dst']); +} + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(new Form_Button( + 'Submit', + gettext("Save") +)); + +$section = new Form_Section('Edit NAT 1 to 1 entry'); + +$section->addInput(new Form_Checkbox( + 'nordr', + 'No RDR (NOT)', + 'Disable redirection for traffic matching this rule', + $pconfig['nordr'] +))->setHelp('This option is rarely needed, don\'t use this unless you know what you\'re doing.'); + +$iflist = get_configured_interface_with_descr(false, true); + +foreach ($iflist as $if => $ifdesc) + if(have_ruleint_access($if)) + $interfaces[$if] = $ifdesc; + +if ($config['l2tp']['mode'] == "server") + if(have_ruleint_access("l2tp")) + $interfaces['l2tp'] = "L2TP VPN"; + +if ($config['pptpd']['mode'] == "server") + if(have_ruleint_access("pptp")) + $interfaces['pptp'] = "PPTP VPN"; + +if (is_pppoe_server_enabled() && have_ruleint_access("pppoe")) + $interfaces['pppoe'] = "PPPoE Server"; + +/* add ipsec interfaces */ +if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enable'])) + if(have_ruleint_access("enc0")) + $interfaces["enc0"] = "IPsec"; + +/* add openvpn/tun interfaces */ +if ($config['openvpn']["openvpn-server"] || $config['openvpn']["openvpn-client"]) + $interfaces["openvpn"] = "OpenVPN"; + +$section->addInput(new Form_Select( + 'interface', + 'Interface', + $pconfig['interface'], + $interfaces +))->setHelp('Choose which interface this rule applies to. In most cases "WAN" is specified.'); + +$section->addInput(new Form_IpAddress( + 'external', + 'External subnet IP', + $pconfig['external'] +))->setHelp('Enter the external (usually on a WAN) subnet\'s starting address for the 1:1 mapping. ' . + 'The subnet mask from the internal address below will be applied to this IP address.'); + +$group = new Form_Group('Internal IP'); + +$group->add(new Form_Checkbox( + 'srcnot', + null, + 'Not', + $pconfig['srcnot'] +))->setHelp('Invert the sense of the match.'); + +$group->add(new Form_Select( + 'srctype', + null, + srctype_selected(), + build_srctype_list() +))->setHelp('Type'); + +$group->add(new Form_IpAddress( + 'src', + null, + is_specialnet($pconfig['src']) ? '': $pconfig['src'] +))->addMask('srcmask', $pconfig['srcmask'], 31)->setHelp('Address/mask'); + +$group->setHelp('Enter the internal (LAN) subnet for the 1:1 mapping. ' . + 'The subnet size specified for the internal subnet will be applied to the external subnet.'); + +$section->add($group); + +$group = new Form_Group('Destination'); + +$group->add(new Form_Checkbox( + 'dstnot', + null, + 'Not', + $pconfig['srcnot'] +))->setHelp('Invert the sense of the match.'); + +$group->add(new Form_Select( + 'dsttype', + null, + dsttype_selected(), + build_dsttype_list() +))->setHelp('Type'); + +$group->add(new Form_IpAddress( + 'dst', + null, + is_specialnet($pconfig['dst']) ? '': $pconfig['dst'] +))->addMask('dstmask', $pconfig['dstmask'], 31)->setHelp('Address/mask'); + +$group->setHelp('The 1:1 mapping will only be used for connections to or from the specified destination. Hint: this is usually "Any".'); + +$section->add($group); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$section->addInput(new Form_Select( + 'natreflection', + 'NAT reflection', + $pconfig['natreflection'], + array( + 'default' => 'Use system default', + 'enable' => 'Enable', + 'disable' => 'Disable' + ) +)); + +$form->add($section); + +print($form); +?> + +<script type="text/javascript"> +//<![CDATA[ +events.push(function(){ + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + function typesel_change() { + switch ($('#srctype').find(":selected").index()) { + case 1: // single + disableInput('src', false); + $('#srcmask').val(''); + disableInput('srcmask', true); + break; + case 2: // network + disableInput('src', false); + disableInput('srcmask', false); + break; + default: + $('#src').val(''); + disableInput('src', true); + $('#srcmask').val(''); + disableInput('srcmask', true); + break; + } + + switch ($('#dsttype').find(":selected").index()) { + case 1: // single + disableInput('dst', false); + $('#dstmask').val(''); + disableInput('dstmask', true);; + break; + case 2: // network / + disableInput('dst', false); + disableInput('dstmask', false); + break; + default: + $('#dst').val(''); + disableInput('dst', true); + $('#dstmask').val(''); + disableInput('dstmask', true); + break; + } + } + + // On-click . . + + $('#srctype').click(function () { + typesel_change(); + }); + + $('#dsttype').click(function () { + typesel_change(); + }); + + // Initial page load + typesel_change(); +}); +//]]> +</script> + +<?php include("foot.inc"); diff --git a/src/usr/local/www/firewall_nat_edit.php b/src/usr/local/www/firewall_nat_edit.php new file mode 100644 index 0000000..2b15c80 --- /dev/null +++ b/src/usr/local/www/firewall_nat_edit.php @@ -0,0 +1,1257 @@ +<?php +/* $Id$ */ +/* + firewall_nat_edit.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net> + * part of m0n0wall (http://m0n0.ch/wall) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: nat +*/ + +##|+PRIV +##|*IDENT=page-firewall-nat-portforward-edit +##|*NAME=Firewall: NAT: Port Forward: Edit page +##|*DESCR=Allow access to the 'Firewall: NAT: Port Forward: Edit' page. +##|*MATCH=firewall_nat_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("itemid.inc"); +require_once("filter.inc"); +require("shaper.inc"); + +$referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/firewall_nat.php'); + +$specialsrcdst = explode(" ", "any (self) pptp pppoe l2tp openvpn"); +$ifdisp = get_configured_interface_with_descr(); + +foreach ($ifdisp as $kif => $kdescr) { + $specialsrcdst[] = "{$kif}"; + $specialsrcdst[] = "{$kif}ip"; +} + +if (!is_array($config['nat']['rule'])) { + $config['nat']['rule'] = array(); +} + +$a_nat = &$config['nat']['rule']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (is_numericint($_GET['after']) || $_GET['after'] == "-1") { + $after = $_GET['after']; +} +if (isset($_POST['after']) && (is_numericint($_POST['after']) || $_POST['after'] == "-1")) { + $after = $_POST['after']; +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + $id = $_GET['dup']; + $after = $_GET['dup']; +} + +if (isset($id) && $a_nat[$id]) { + if (isset($a_nat[$id]['created']) && is_array($a_nat[$id]['created'])) { + $pconfig['created'] = $a_nat[$id]['created']; + } + + if (isset($a_nat[$id]['updated']) && is_array($a_nat[$id]['updated'])) { + $pconfig['updated'] = $a_nat[$id]['updated']; + } + + $pconfig['disabled'] = isset($a_nat[$id]['disabled']); + $pconfig['nordr'] = isset($a_nat[$id]['nordr']); + + address_to_pconfig($a_nat[$id]['source'], $pconfig['src'], + $pconfig['srcmask'], $pconfig['srcnot'], + $pconfig['srcbeginport'], $pconfig['srcendport']); + + address_to_pconfig($a_nat[$id]['destination'], $pconfig['dst'], + $pconfig['dstmask'], $pconfig['dstnot'], + $pconfig['dstbeginport'], $pconfig['dstendport']); + + $pconfig['proto'] = $a_nat[$id]['protocol']; + $pconfig['localip'] = $a_nat[$id]['target']; + $pconfig['localbeginport'] = $a_nat[$id]['local-port']; + $pconfig['descr'] = $a_nat[$id]['descr']; + $pconfig['interface'] = $a_nat[$id]['interface']; + $pconfig['associated-rule-id'] = $a_nat[$id]['associated-rule-id']; + $pconfig['nosync'] = isset($a_nat[$id]['nosync']); + $pconfig['natreflection'] = $a_nat[$id]['natreflection']; + + if (!$pconfig['interface']) { + $pconfig['interface'] = "wan"; + } +} else { + $pconfig['interface'] = "wan"; + $pconfig['src'] = "any"; + $pconfig['srcbeginport'] = "any"; + $pconfig['srcendport'] = "any"; +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + unset($id); +} + +/* run through $_POST items encoding HTML entities so that the user + * cannot think he is slick and perform a XSS attack on the unwilling + */ +unset($input_errors); + +foreach ($_POST as $key => $value) { + $temp = $value; + $newpost = htmlentities($temp); + if($newpost != $temp) + $input_errors[] = sprintf(gettext("Invalid characters detected %s. Please remove invalid characters and save again."), $temp); +} + +if ($_POST) { + + if (strtoupper($_POST['proto']) == "TCP" || strtoupper($_POST['proto']) == "UDP" || strtoupper($_POST['proto']) == "TCP/UDP") { + if ($_POST['srcbeginport_cust'] && !$_POST['srcbeginport']) { + $_POST['srcbeginport'] = trim($_POST['srcbeginport_cust']); + } + if ($_POST['srcendport_cust'] && !$_POST['srcendport']) { + $_POST['srcendport'] = trim($_POST['srcendport_cust']); + } + + if ($_POST['srcbeginport'] == "any") { + $_POST['srcbeginport'] = 0; + $_POST['srcendport'] = 0; + } else { + if (!$_POST['srcendport']) { + $_POST['srcendport'] = $_POST['srcbeginport']; + } + } + if ($_POST['srcendport'] == "any") { + $_POST['srcendport'] = $_POST['srcbeginport']; + } + + if ($_POST['dstbeginport_cust'] && !$_POST['dstbeginport']) { + $_POST['dstbeginport'] = trim($_POST['dstbeginport_cust']); + } + if ($_POST['dstendport_cust'] && !$_POST['dstendport']) { + $_POST['dstendport'] = trim($_POST['dstendport_cust']); + } + + if ($_POST['dstbeginport'] == "any") { + $_POST['dstbeginport'] = 0; + $_POST['dstendport'] = 0; + } else { + if (!$_POST['dstendport']) { + $_POST['dstendport'] = $_POST['dstbeginport']; + } + } + if ($_POST['dstendport'] == "any") { + $_POST['dstendport'] = $_POST['dstbeginport']; + } + + if ($_POST['localbeginport_cust'] && !$_POST['localbeginport']) { + $_POST['localbeginport'] = trim($_POST['localbeginport_cust']); + } + + /* Make beginning port end port if not defined and endport is */ + if (!$_POST['srcbeginport'] && $_POST['srcendport']) { + $_POST['srcbeginport'] = $_POST['srcendport']; + } + if (!$_POST['dstbeginport'] && $_POST['dstendport']) { + $_POST['dstbeginport'] = $_POST['dstendport']; + } + } else { + $_POST['srcbeginport'] = 0; + $_POST['srcendport'] = 0; + $_POST['dstbeginport'] = 0; + $_POST['dstendport'] = 0; + } + + if (is_specialnet($_POST['srctype'])) { + $_POST['src'] = $_POST['srctype']; + $_POST['srcmask'] = 0; + } else if ($_POST['srctype'] == "single") { + $_POST['srcmask'] = 32; + } + + if (is_specialnet($_POST['dsttype'])) { + $_POST['dst'] = $_POST['dsttype']; + $_POST['dstmask'] = 0; + } else if ($_POST['dsttype'] == "single") { + $_POST['dstmask'] = 32; + } else if (is_ipaddr($_POST['dsttype'])) { + $_POST['dst'] = $_POST['dsttype']; + $_POST['dstmask'] = 32; + $_POST['dsttype'] = "single"; + } + + $pconfig = $_POST; + + /* input validation */ + if (strtoupper($_POST['proto']) == "TCP" or strtoupper($_POST['proto']) == "UDP" or strtoupper($_POST['proto']) == "TCP/UDP") { + $reqdfields = explode(" ", "interface proto dstbeginport dstendport"); + $reqdfieldsn = array(gettext("Interface"), gettext("Protocol"), gettext("Destination port from"), gettext("Destination port to")); + } else { + $reqdfields = explode(" ", "interface proto"); + $reqdfieldsn = array(gettext("Interface"), gettext("Protocol")); + } + + if ($_POST['srctype'] == "single" || $_POST['srctype'] == "network") { + $reqdfields[] = "src"; + $reqdfieldsn[] = gettext("Source address"); + } + + if ($_POST['dsttype'] == "single" || $_POST['dsttype'] == "network") { + $reqdfields[] = "dst"; + $reqdfieldsn[] = gettext("Destination address"); + } + + if (!isset($_POST['nordr'])) { + $reqdfields[] = "localip"; + $reqdfieldsn[] = gettext("Redirect target IP"); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (!$_POST['srcbeginport']) { + $_POST['srcbeginport'] = 0; + $_POST['srcendport'] = 0; + } + + if (!$_POST['dstbeginport']) { + $_POST['dstbeginport'] = 0; + $_POST['dstendport'] = 0; + } + + if ($_POST['src']) { + $_POST['src'] = trim($_POST['src']); + } + if ($_POST['dst']) { + $_POST['dst'] = trim($_POST['dst']); + } + if ($_POST['localip']) { + $_POST['localip'] = trim($_POST['localip']); + } + + if (!isset($_POST['nordr']) && ($_POST['localip'] && !is_ipaddroralias($_POST['localip']))) { + $input_errors[] = sprintf(gettext("\"%s\" is not a valid redirect target IP address or host alias."), $_POST['localip']); + } + + if ($_POST['srcbeginport'] && !is_portoralias($_POST['srcbeginport'])) { + $input_errors[] = sprintf(gettext("%s is not a valid start source port. It must be a port alias or integer between 1 and 65535."), $_POST['srcbeginport']); + } + if ($_POST['srcendport'] && !is_portoralias($_POST['srcendport'])) { + $input_errors[] = sprintf(gettext("%s is not a valid end source port. It must be a port alias or integer between 1 and 65535."), $_POST['srcendport']); + } + if ($_POST['dstbeginport'] && !is_portoralias($_POST['dstbeginport'])) { + $input_errors[] = sprintf(gettext("%s is not a valid start destination port. It must be a port alias or integer between 1 and 65535."), $_POST['dstbeginport']); + } + if ($_POST['dstendport'] && !is_portoralias($_POST['dstendport'])) { + $input_errors[] = sprintf(gettext("%s is not a valid end destination port. It must be a port alias or integer between 1 and 65535."), $_POST['dstendport']); + } + + if ((strtoupper($_POST['proto']) == "TCP" || strtoupper($_POST['proto']) == "UDP" || strtoupper($_POST['proto']) == "TCP/UDP") && (!isset($_POST['nordr']) && !is_portoralias($_POST['localbeginport']))) { + $input_errors[] = sprintf(gettext("A valid redirect target port must be specified. It must be a port alias or integer between 1 and 65535."), $_POST['localbeginport']); + } + + /* if user enters an alias and selects "network" then disallow. */ + if (($_POST['srctype'] == "network" && is_alias($_POST['src'])) || + ($_POST['dsttype'] == "network" && is_alias($_POST['dst']))) { + $input_errors[] = gettext("You must specify single host or alias for alias entries."); + } + + if (!is_specialnet($_POST['srctype'])) { + if (($_POST['src'] && !is_ipaddroralias($_POST['src']))) { + $input_errors[] = sprintf(gettext("%s is not a valid source IP address or alias."), $_POST['src']); + } + if (($_POST['srcmask'] && !is_numericint($_POST['srcmask']))) { + $input_errors[] = gettext("A valid source bit count must be specified."); + } + } + + if (!is_specialnet($_POST['dsttype'])) { + if (($_POST['dst'] && !is_ipaddroralias($_POST['dst']))) { + $input_errors[] = sprintf(gettext("%s is not a valid destination IP address or alias."), $_POST['dst']); + } + if (($_POST['dstmask'] && !is_numericint($_POST['dstmask']))) { + $input_errors[] = gettext("A valid destination bit count must be specified."); + } + } + + if ($_POST['srcbeginport'] > $_POST['srcendport']) { + /* swap */ + $tmp = $_POST['srcendport']; + $_POST['srcendport'] = $_POST['srcbeginport']; + $_POST['srcbeginport'] = $tmp; + } + + if ($_POST['dstbeginport'] > $_POST['dstendport']) { + /* swap */ + $tmp = $_POST['dstendport']; + $_POST['dstendport'] = $_POST['dstbeginport']; + $_POST['dstbeginport'] = $tmp; + } + + if (!$input_errors) { + if (!isset($_POST['nordr']) && ($_POST['dstendport'] - $_POST['dstbeginport'] + $_POST['localbeginport']) > 65535) { + $input_errors[] = gettext("The target port range must be an integer between 1 and 65535."); + } + } + + /* check for overlaps */ + foreach ($a_nat as $natent) { + if (isset($id) && ($a_nat[$id]) && ($a_nat[$id] === $natent)) { + continue; + } + if ($natent['interface'] != $_POST['interface']) { + continue; + } + if ($natent['destination']['address'] != $_POST['dst']) { + continue; + } + if (($natent['proto'] != $_POST['proto']) && ($natent['proto'] != "tcp/udp") && ($_POST['proto'] != "tcp/udp")) { + continue; + } + + list($begp, $endp) = explode("-", $natent['destination']['port']); + if (!$endp) { + $endp = $begp; + } + + if (!((($_POST['dstbeginport'] < $begp) && ($_POST['dstendport'] < $begp)) || + (($_POST['dstbeginport'] > $endp) && ($_POST['dstendport'] > $endp)))) { + $input_errors[] = gettext("The destination port range overlaps with an existing entry."); + break; + } + } + + if (!$input_errors) { + $natent = array(); + + $natent['disabled'] = isset($_POST['disabled']) ? true:false; + $natent['nordr'] = isset($_POST['nordr']) ? true:false; + + if ($natent['nordr']) { + $_POST['associated-rule-id'] = ''; + $_POST['filter-rule-association'] = ''; + } + + pconfig_to_address($natent['source'], $_POST['src'], + $_POST['srcmask'], $_POST['srcnot'], + $_POST['srcbeginport'], $_POST['srcendport']); + + pconfig_to_address($natent['destination'], $_POST['dst'], + $_POST['dstmask'], $_POST['dstnot'], + $_POST['dstbeginport'], $_POST['dstendport']); + + $natent['protocol'] = $_POST['proto']; + + if (!$natent['nordr']) { + $natent['target'] = $_POST['localip']; + $natent['local-port'] = $_POST['localbeginport']; + } + + $natent['interface'] = $_POST['interface']; + $natent['descr'] = $_POST['descr']; + $natent['associated-rule-id'] = $_POST['associated-rule-id']; + + if ($_POST['filter-rule-association'] == "pass") { + $natent['associated-rule-id'] = "pass"; + } + + if ($_POST['nosync'] == "yes") { + $natent['nosync'] = true; + } else { + unset($natent['nosync']); + } + + if ($_POST['natreflection'] == "enable" || $_POST['natreflection'] == "purenat" || $_POST['natreflection'] == "disable") { + $natent['natreflection'] = $_POST['natreflection']; + } else { + unset($natent['natreflection']); + } + + // If we used to have an associated filter rule, but no-longer should have one + if (!empty($a_nat[$id]) && (empty($natent['associated-rule-id']) || $natent['associated-rule-id'] != $a_nat[$id]['associated-rule-id'])) { + // Delete the previous rule + delete_id($a_nat[$id]['associated-rule-id'], $config['filter']['rule']); + mark_subsystem_dirty('filter'); + } + + $need_filter_rule = false; + // Updating a rule with a filter rule associated + if (!empty($natent['associated-rule-id'])) { + $need_filter_rule = true; + } + // Create a rule or if we want to create a new one + if ($natent['associated-rule-id'] == 'new') { + $need_filter_rule = true; + unset($natent['associated-rule-id']); + $_POST['filter-rule-association']='add-associated'; + } + // If creating a new rule, where we want to add the filter rule, associated or not + else if (isset($_POST['filter-rule-association']) && + ($_POST['filter-rule-association'] == 'add-associated' || + $_POST['filter-rule-association'] == 'add-unassociated')) { + $need_filter_rule = true; + } + + if ($need_filter_rule == true) { + /* auto-generate a matching firewall rule */ + $filterent = array(); + unset($filterentid); + // If a rule already exists, load it + if (!empty($natent['associated-rule-id'])) { + $filterentid = get_id($natent['associated-rule-id'], $config['filter']['rule']); + if ($filterentid === false) { + $filterent['associated-rule-id'] = $natent['associated-rule-id']; + } else { + $filterent =& $config['filter']['rule'][$filterentid]; + } + } + + pconfig_to_address($filterent['source'], $_POST['src'], + $_POST['srcmask'], $_POST['srcnot'], + $_POST['srcbeginport'], $_POST['srcendport']); + + // Update interface, protocol and destination + $filterent['interface'] = $_POST['interface']; + $filterent['protocol'] = $_POST['proto']; + $filterent['destination']['address'] = $_POST['localip']; + + $dstpfrom = $_POST['localbeginport']; + $dstpto = $dstpfrom + $_POST['dstendport'] - $_POST['dstbeginport']; + + if ($dstpfrom == $dstpto) { + $filterent['destination']['port'] = $dstpfrom; + } else { + $filterent['destination']['port'] = $dstpfrom . "-" . $dstpto; + } + + /* + * Our firewall filter description may be no longer than + * 63 characters, so don't let it be. + */ + $filterent['descr'] = substr("NAT " . $_POST['descr'], 0, 62); + + // If this is a new rule, create an ID and add the rule + if ($_POST['filter-rule-association'] == 'add-associated') { + $filterent['associated-rule-id'] = $natent['associated-rule-id'] = get_unique_id(); + $filterent['created'] = make_config_revision_entry(null, gettext("NAT Port Forward")); + $config['filter']['rule'][] = $filterent; + } + + mark_subsystem_dirty('filter'); + } + + if (isset($a_nat[$id]['created']) && is_array($a_nat[$id]['created'])) { + $natent['created'] = $a_nat[$id]['created']; + } + + $natent['updated'] = make_config_revision_entry(); + + // Allow extending of the firewall edit page and include custom input validation + pfSense_handle_custom_code("/usr/local/pkg/firewall_nat/pre_write_config"); + + // Update the NAT entry now + if (isset($id) && $a_nat[$id]) { + $a_nat[$id] = $natent; + } else { + $natent['created'] = make_config_revision_entry(); + if (is_numeric($after)) { + array_splice($a_nat, $after+1, 0, array($natent)); + } else { + $a_nat[] = $natent; + } + } + + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + + header("Location: firewall_nat.php"); + exit; + } +} + +function build_srctype_list() { + global $pconfig, $ifdisp; + + $list = array('any' => 'Any', 'single' => 'Single host or alias', 'network' => 'Network'); + + $sel = is_specialnet($pconfig['src']); + + if(have_ruleint_access("pptp")) + $list['pptp'] = 'PPTP clients'; + + if(have_ruleint_access("pppoe")) + $list['pppoe'] = 'PPPoE clients'; + + if(have_ruleint_access("l2tp")) + $list['l2tp'] = 'L2TP clients'; + + foreach ($ifdisp as $ifent => $ifdesc) { + if(have_ruleint_access($ifent)) { + $list[$ifent] = $ifdesc . ' net'; + $list[$ifent . 'ip'] = $ifdesc . ' address'; + } + } + + return($list); +} + +function build_dsttype_list() { + global $pconfig, $config, $ifdisp; + + $sel = is_specialnet($pconfig['dst']); + $list = array('any' => 'Any', 'single' => 'Single host or alias', 'network' => 'Network', '(self)' => 'This Firewall (self)'); + + if(have_ruleint_access("pptp")) + $list['pptp'] = 'PPTP clients'; + + if(have_ruleint_access("pppoe")) + $list['pppoe'] = 'PPPoE clients'; + + if(have_ruleint_access("l2tp")) + $list['l2tp'] = 'L2TP clients'; + + foreach ($ifdisp as $if => $ifdesc) { + if(have_ruleint_access($if)) { + $list[$if] = $ifdesc; + $list[$if . 'ip'] = $ifdesc . ' address'; + } + } + + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $sn) { + if (isset($sn['noexpand'])) + continue; + + if ($sn['mode'] == "proxyarp" && $sn['type'] == "network") { + $start = ip2long32(gen_subnet($sn['subnet'], $sn['subnet_bits'])); + $end = ip2long32(gen_subnet_max($sn['subnet'], $sn['subnet_bits'])); + $len = $end - $start; + + for ($i = 0; $i <= $len; $i++) { + $snip = long2ip32($start+$i); + + $list[$snip] = $snip . ' (' . $sn['descr'] . ')'; + } + + $list[$sn['subnet']] = $sn['subnet'] . ' (' . $sn['descr'] . ')'; + } + } + } + + return($list); +} + +function dsttype_selected() { + global $pconfig; + + $sel = is_specialnet($pconfig['dst']); + + if(!$sel) { + if($pconfig['dstmask'] == 32) + return('single'); + + return('network'); + } + + return($pconfig['dst']); +} + +function srctype_selected() { + global $pconfig; + + $sel = is_specialnet($pconfig['src']); + + if(!$sel) { + if($pconfig['srcmask'] == 32) + return('single'); + + return('network'); + } + + return($pconfig['src']); +} + +$closehead = false; +$pgtitle = array(gettext("Firewall"), gettext("NAT"), gettext("Port Forward"), gettext("Edit")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(new Form_Button( + 'Submit', + gettext("Save") +)); + +$section = new Form_Section('Edit Redirect entry'); + +$section->addInput(new Form_Checkbox( + 'disabled', + 'Disabled', + 'Disable this rule', + $pconfig['disabled'] +)); + +$section->addInput(new Form_Checkbox( + 'nordr', + 'No RDR (NOT)', + 'Disable redirection for traffic matching this rule', + $pconfig['nordr'] +))->setHelp('This option is rarely needed, don\'t use this unless you know what you\'re doing.'); + +$iflist = get_configured_interface_with_descr(false, true); + +foreach ($iflist as $if => $ifdesc) + if(have_ruleint_access($if)) + $interfaces[$if] = $ifdesc; + +if ($config['l2tp']['mode'] == "server") + if(have_ruleint_access("l2tp")) + $interfaces['l2tp'] = "L2TP VPN"; + +if ($config['pptpd']['mode'] == "server") + if(have_ruleint_access("pptp")) + $interfaces['pptp'] = "PPTP VPN"; + +if (is_pppoe_server_enabled() && have_ruleint_access("pppoe")) + $interfaces['pppoe'] = "PPPoE Server"; + +/* add ipsec interfaces */ +if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enable'])) + if(have_ruleint_access("enc0")) + $interfaces["enc0"] = "IPsec"; + +/* add openvpn/tun interfaces */ +if ($config['openvpn']["openvpn-server"] || $config['openvpn']["openvpn-client"]) + $interfaces["openvpn"] = "OpenVPN"; + +$section->addInput(new Form_Select( + 'interface', + 'Interface', + $pconfig['interface'], + $interfaces +))->setHelp('Choose which interface this rule applies to. In most cases "WAN" is specified.'); + +$protocols = "TCP UDP TCP/UDP ICMP ESP AH GRE IPV6 IGMP PIM OSPF"; + +$section->addInput(new Form_Select( + 'proto', + 'Protocol', + $pconfig['proto'], + array_combine(explode(" ", strtolower($protocols)), explode(" ", $protocols)) +))->setHelp('Choose which protocol this rule should match. In most cases "TCP" is specified.'); + +$group = new Form_Group('Source'); + +$group->add(new Form_Select( + 'srctype', + null, + srctype_selected(), + build_srctype_list() +))->setHelp('Type'); + +$group->add(new Form_IpAddress( + 'src', + null, + is_specialnet($pconfig['src']) ? '': $pconfig['src'] +))->setPattern('[.a-zA-Z0-9_]+')->addMask('srcmask', $pconfig['srcmask'])->setHelp('Address/mask'); + +$section->add($group); + +$portlist = array("" => 'Other', 'any' => 'Any'); + +foreach ($wkports as $wkport => $wkportdesc) + $portlist[$wkport] = $wkportdesc; + +$group = new Form_Group('Source port range'); +$group->addClass('srcportrange'); + +$group->add(new Form_Select( + 'srcbeginport', + null, + $pconfig['srcbeginport'], + $portlist +))->setHelp('From port'); + +$group->add(new Form_Input( + 'srcbeginport_cust', + null, + 'number', + $pconfig['srcbeginport'], + ['min' => '1', 'max' => '65536'] +))->setHelp('Custom'); + +$group->add(new Form_Select( + 'srcendport', + null, + $pconfig['srcendport'], + $portlist +))->setHelp('To port'); + +$group->add(new Form_Input( + 'srcendport_cust', + null, + 'number', + $pconfig['srcendport'], + ['min' => '1', 'max' => '65536'] +))->setHelp('Custom'); + +$group->setHelp('Specify the source port or port range for this rule. This is usually random and almost never ' . + 'equal to the destination port range (and should usually be \'any\'). You can leave the \'to\' field ' . + 'empty if you only want to filter a single port.'); + +$section->add($group); + +$group = new Form_Group('Destination'); + +$group->add(new Form_Select( + 'dsttype', + null, + dsttype_selected(), + build_dsttype_list() +))->setHelp('Type'); + +$group->add(new Form_IpAddress( + 'dst', + null, + is_specialnet($pconfig['dst']) ? '': $pconfig['dst'] +))->setPattern('[.a-zA-Z0-9_]+')->addMask('dstmask', $pconfig['dstmask'], 31)->setHelp('Address/mask'); + +$section->add($group); + +$group = new Form_Group('Destination port range'); +$group->addClass('dstportrange'); + +$group->add(new Form_Select( + 'dstbeginport', + null, + $pconfig['dstbeginport'], + $portlist +))->setHelp('From port'); + +$group->add(new Form_Input( + 'dstbeginport_cust', + null, + 'number', + $pconfig['dstbeginport'], + ['min' => '1', 'max' => '65536'] +))->setHelp('Custom'); + +$group->add(new Form_Select( + 'dstendport', + null, + $pconfig['dstendport'], + $portlist +))->setHelp('To port'); + +$group->add(new Form_Input( + 'dstendport_cust', + null, + 'number', + $pconfig['dstendport'], + ['min' => '1', 'max' => '65536'] +))->setHelp('Custom'); + +$group->setHelp('Specify the port or port range for the destination of the packet for this mapping. ' . + 'You can leave the \'to\' field empty if you only want to map a single port '); + +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'dstnot', + null, + 'Not (Invert the sense of the match)', + $pconfig['dstnot'], + 'yes' +)); + +$section->addInput(new Form_IpAddress( + 'localip', + 'Redirect target IP', + $pconfig['localip'] +))->setPattern('[.a-zA-Z0-9_]+')->setHelp('Enter the internal IP address of the server on which you want to map the ports.' . '<br />' . + 'e.g.: 192.168.1.12'); + +$group = new Form_Group('Redirect target port'); +$group->addClass('lclportrange'); + +$group->add(new Form_Select( + 'localbeginport', + null, + $pconfig['localbeginport'], + array('' => 'Other') + $wkports +))->setHelp('Port'); + +$group->setHelp('Specify the port on the machine with the IP address entered above. In case of a port range, specify the ' . + 'beginning port of the range (the end port will be calculated automatically).' . '<br />' . + 'this is usually identical to "From port" above'); + +$group->add(new Form_Input( + 'localbeginport_cust', + null, + 'number', + $pconfig['localbeginport_cust'], + ['min' => '1', 'max' => '65536'] +))->setHelp('Custom'); + +$section->add($group); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + + +$section->addInput(new Form_Checkbox( + 'nosync', + 'No XMLRPC Sync', + null, + $pconfig['nosync'] +))->setHelp('This prevents the rule on Master from automatically syncing to other CARP members. ' . + 'This does NOT prevent the rule from being overwritten on Slave.'); + +$section->addInput(new Form_Select( + 'natreflection', + 'NAT reflection', + $pconfig['natreflection'], + array( + 'default' => 'Use system default', + 'enable' => 'Enable (NAT + Proxy)', + 'purenat' => 'Enable (Pure NAT)', + 'disable' => 'Disable' + ) +)); + +if (isset($id) && $a_nat[$id] && (!isset($_GET['dup']) || !is_numericint($_GET['dup']))) { + $hlpstr = ''; + $rulelist = array('' => 'None', 'pass' => 'Pass'); + + if (is_array($config['filter']['rule'])) { + filter_rules_sort(); + foreach ($config['filter']['rule'] as $filter_id => $filter_rule) { + if (isset($filter_rule['associated-rule-id'])) { + $rulelist[$filter_rule['associated-rule-id']] = 'Rule ' . $filter_rule['descr']; + + if ($filter_rule['associated-rule-id']==$pconfig['associated-rule-id']) { + $hlpstr = '<a href="firewall_rules_edit.php?id=' . $filter_id . '">' . gettext("View the filter rule") . '</a><br />'; + } + } + } + } + + if (isset($pconfig['associated-rule-id'])) + $rulelist['new'] = 'Create new associated filter rule'; + + $section->addInput(new Form_Select( + 'associated-rule-id', + 'Filter rule association', + 'add-associated', + $rulelist + ))->setHelp($hlpstr); +} else { + $section->addInput(new Form_Select( + 'associated-rule-id', + 'Filter rule association', + 'add-associated', + array( + '' => 'None', + 'add-associated' => 'Add associated filter rule', + 'add-unassociated' => 'Add unassociated filter rule', + 'pass' => 'Pass' + ) + ))->setHelp('The "pass" selection does not work properly with Multi-WAN. It will only work on an interface containing the default gateway.'); +} + +$form->add($section); + +$has_created_time = (isset($a_nat[$id]['created']) && is_array($a_nat[$id]['created'])); +$has_updated_time = (isset($a_nat[$id]['updated']) && is_array($a_nat[$id]['updated'])); + +if ($has_created_time || $has_updated_time) { + $section = new Form_Section('Rule Information'); + + if($has_created_time) { + $section->addInput(new Form_StaticText( + 'Created', + date(gettext("n/j/y H:i:s"), $a_nat[$id]['created']['time']) . gettext("by") . $a_nat[$id]['created']['username'] + )); + } + + if($has_updated_time) { + $section->addInput(new Form_StaticText( + 'Updated', + date(gettext("n/j/y H:i:s"), $a_nat[$id]['updated']['time']) . gettext("by") . $a_nat[$id]['updated']['username'] + )); + } + + $form->add($section); +} + +if (isset($id) && $a_nat[$id]) { + $form->addGlobal(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->addGlobal(new Form_Input( + 'after', + null, + 'hidden', + $after +)); + +print($form); +?> + +<script type="text/javascript"> +//<![CDATA[ +events.push(function(){ + var portsenabled = 1; + var dstenabled = 1; + var showsource = 0; + var iface_old = ''; + + // ---------- "Library" functions --------------------------------------------------------------------------------- + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // Hides all elements of the specified class. This will usually be a section + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // ---------- jQuery functions, lovingly converted from the original javascript------------------------------------------ + function ext_change() { + + if (($('#srcbeginport').find(":selected").index() == 0) && portsenabled) { + disableInput('srcbeginport_cust', false); + } else { + $('#srcbeginport_cust').val(''); + disableInput('srcbeginport_cust', true); + } + + if (($('#srcendport').find(":selected").index() == 0) && portsenabled) { + disableInput('srcendport_cust', false); + } else { + $('#srcendport_cust').val(''); + disableInput('srcendport_cust', true); + } + + if (($('#dstbeginport').find(":selected").index() == 0) && portsenabled && dstenabled) { + disableInput('dstbeginport_cust', false); + } else { + $('#dstbeginport_cust').val(''); + disableInput('dstbeginport_cust', true); + } + + if (($('#dstendport').find(":selected").index() == 0) && portsenabled && dstenabled) { + disableInput('dstendport_cust', false); + } else { + $('#dstendport_cust').val(''); + disableInput('dstendport_cust', true); + } + + if (($('#localbeginport').find(":selected").index() == 0) && portsenabled) { + disableInput('localbeginport_cust', false); + } else { + $('#localbeginport_cust').val(''); + disableInput('localbeginport_cust', true); + } + + if (!portsenabled) { + disableInput('srcbeginport', true); + disableInput('srcendport', true); + disableInput('dstbeginport', true); + disableInput('dstendport', true); + disableInput('localbeginport_cust', true); + } else { + disableInput('srcbeginport', false); + disableInput('srcendport', false); + disableInput('localbeginport_cust', false); + if( dstenabled ) { + disableInput('dstbeginport', false); + disableInput('dstendport', false); + } + } + } + + function nordr_change() { + if( $('#nordr').prop('checked') ) { + hideInput('localip', true); + hideClass('lclportrange', true); + hideInput('associated-rule-id', true); + } else { + hideInput('localip', false); + hideClass('lclportrange', !portsenabled); + hideInput('associated-rule-id', false); + } + } + + var customarray = <?= json_encode(get_alias_list(array("port", "url_ports", "urltable_ports"))) ?>; + + function check_for_aliases() { + // if External port range is an alias, then disallow + // entry of Local port + // + for(i=0; i<customarray.length; i++) { + if($('#dstbeginport_cust').val() == customarray[i]) { + $('#dstendport_cust').val(customarray[i]); + $('#localbeginport_cust').val(customarray[i]); + disableInput('dstendport_cust', true); + disableInput('localbeginport', true); + disableInput('localbeginport_cust', true); + disableInput('dstendport_cust', false); + disableInput('localbeginport', false); + disableInput('localbeginport_cust', false); + } + if($('#dstbeginport').val() == customarray[i]) { + $('#dstendport_cust').val(customarray[i]); + $('#localbeginport_cust').val(customarray[i]); + disableInput('dstendport_cust', true); + disableInput('localbeginport', true); + disableInput('localbeginport_cust', true); + disableInput('dstendport_cust', false); + disableInput('localbeginport', false); + disableInput('localbeginport_cust', false); + } + if($('#dstendport_cust').val() == customarray[i]) { + $('#dstendport_cust').val(customarray[i]); + $('#localbeginport_cust').val(customarray[i]); + disableInput('dstendport_cust', true); + disableInput('localbeginport', true); + disableInput('localbeginport_cust', true); + disableInput('dstendport_cust', false); + disableInput('localbeginport', false); + disableInput('localbeginport_cust', false); + } + if($('#dstendport').val() == customarray[i]) { + $('#dstendport_cust').val(customarray[i]); + $('#localbeginport_cust').val(customarray[i]); + disableInput('dstendport_cust', true); + disableInput('localbeginport', true); + disableInput('localbeginport_cust', true); + disableInput('dstendport_cust', false); + ddisableInput('localbeginport', false); + disableInput('localbeginport_cust', false); + } + + } + } + + function proto_change() { + if ($('#proto').find(":selected").index() >= 0 && $('#proto').find(":selected").index() <= 2) { + portsenabled = 1; + } else { + portsenabled = 0; + } + + if (portsenabled) { + hideClass('srcportrange', showsource == 1); + hideClass('dstportrange', false); + hideClass('lclportrange', false); + } else { + hideClass('srcportrange', true); + hideClass('dstportrange', true); + hideClass('lclportrange', true); + $('#dstbeginport').prop("selectedIndex", 0).selectmenu('refresh'); + $('#dstbeginport_cust').val(''); + $('#dstendport').prop("selectedIndex", 0).selectmenu('refresh'); + $('#dstendport_cust').val(''); + $('#localbeginport').prop("selectedIndex", 0).selectmenu('refresh'); + $('#localbeginport_cust').val(''); + } + } + + function typesel_change() { + switch ($('#srctype').find(":selected").index()) { + case 1: // single + disableInput('src', false); + $('#srcmask').val(''); + disableInput('srcmask', true); + break; + case 2: // network + disableInput('src', false); + disableInput('srcmask', false); + break; + default: + $('#src').val(''); + disableInput('src', true); + $('#srcmask').val(''); + disableInput('srcmask', true); + break; + } + + if(dstenabled) { + switch ($('#dsttype').find(":selected").index()) { + case 1: // single + disableInput('dst', false); + $('#dstmask').val(''); + disableInput('dstmask', true);; + break; + case 2: // network / + disableInput('dst', false); + disableInput('dstmask', false); + break; + default: + $('#dst').val(''); + disableInput('dst', true); + $('#dstmask').val(''); + disableInput('dstmask', true); + break; + } + } + } + + function src_rep_change() { + $('#srcendport').prop("selectedIndex", $('#srcbeginport').find(":selected").index()); + } + + function dst_rep_change() { + $('#dstendport').prop("selectedIndex", $('#dstbeginport').find(":selected").index()); + } + + function dst_change( iface, old_iface, old_dst ) { + if ( ( old_dst == "" ) || ( old_iface.concat("ip") == old_dst ) ) { + $('#dsttype').val(iface + "ip"); + } + } + + // ---------- "onclick" functions --------------------------------------------------------------------------------- + $('#srcbeginport').on('change', function() { + src_rep_change(); + ext_change(); + }); + + $('#srcendport').on('change', function() { + ext_change(); + }); + + $('#dstbeginport').on('change', function() { + dst_rep_change(); + ext_change(); + }); + + $('#dstendport').on('change', function() { + ext_change(); + }); + + $('#localbeginport').on('change', function() { + ext_change(); + check_for_aliases(); + }); + + $('#proto').on('change', function() { + proto_change(); + check_for_aliases() + }); + + $('#nordr').click(function () { + nordr_change(); + }); + + $('#interface').click(function () { + dst_change($('#interface').val(), iface_old, $('#dsttype').val()); + iface_old = $('#interface').val(); + typesel_change(); + }); + + $('#srctype').click(function () { + typesel_change(); + }); + + $('#dsttype').click(function () { + typesel_change(); + }); + + // ---------- On initial page load -------------------------------------------------------------------------------- + + ext_change(); + dst_change($('#interface').val(),'<?=htmlspecialchars($pconfig['interface'])?>','<?=htmlspecialchars($pconfig['dst'])?>'); + iface_old = $('#interface').val(); + typesel_change(); + proto_change(); + nordr_change(); + +}); +//]]> +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_nat_npt.php b/src/usr/local/www/firewall_nat_npt.php new file mode 100644 index 0000000..aedb2dc --- /dev/null +++ b/src/usr/local/www/firewall_nat_npt.php @@ -0,0 +1,198 @@ +<?php +/* $Id$ */ +/* + firewall_nat_npt.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) Copyright (C) 2011 Seth Mos <seth.mos@dds.nl> + * part of m0n0wall (http://m0n0.ch/wall) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: nat +*/ + +##|+PRIV +##|*IDENT=page-firewall-nat-npt +##|*NAME=Firewall: NAT: NPT page +##|*DESCR=Allow access to the 'Firewall: NAT: NPT' page. +##|*MATCH=firewall_nat_npt.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +if (!is_array($config['nat']['npt'])) + $config['nat']['npt'] = array(); + +$a_npt = &$config['nat']['npt']; + +if ($_POST) { + $pconfig = $_POST; + + if ($_POST['apply']) { + $retval = 0; + $retval |= filter_configure(); + $savemsg = get_std_save_message($retval); + + if ($retval == 0) { + clear_subsystem_dirty('natconf'); + clear_subsystem_dirty('filter'); + } + } +} + +if ($_GET['act'] == "del") { + if ($a_npt[$_GET['id']]) { + unset($a_npt[$_GET['id']]); + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat_npt.php"); + exit; + } +} + +$pgtitle = array(gettext("Firewall"), gettext("NAT"), gettext("NPt")); +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('natconf')) + print_info_box_np(gettext("The NAT configuration has been changed") . ".<br />" . gettext("You must apply the changes in order for them to take effect.")); + +$tab_array = array(); +$tab_array[] = array(gettext("Port Forward"), false, "firewall_nat.php"); +$tab_array[] = array(gettext("1:1"), false, "firewall_nat_1to1.php"); +$tab_array[] = array(gettext("Outbound"), false, "firewall_nat_out.php"); +$tab_array[] = array(gettext("NPt"), true, "firewall_nat_npt.php"); +display_top_tabs($tab_array); +?> + +<div class="panel-body table responsive"> + <form method="post"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Interface")?></th> + <th><?=gettext("External Prefix")?></th> + <th><?=gettext("Internal prefix")?></th> + <th><?=gettext("Description")?></th> + <th><!-- Buttons --></th> + </tr> + </thead> + <tbody class="user-entries"> +<?php + +$i = 0; +foreach ($a_npt as $natent): +?> + <tr<?=isset($natent['disabled'])? ' class="disabled"' : ''?>> + <td> + <input type="hidden" name="rule[]" value="<?=$i?>" /> +<?php + if (!$natent['interface']) + print(htmlspecialchars(convert_friendly_interface_to_friendly_descr("wan"))); + else + print(htmlspecialchars(convert_friendly_interface_to_friendly_descr($natent['interface']))); +?> + </td> +<?php + $source_net = pprint_address($natent['source']); + $source_cidr = strstr($source_net, '/'); + $destination_net = pprint_address($natent['destination']); + $destination_cidr = strstr($destination_net, '/'); +?> + <td> + <?=$destination_net?> + </td> + <td> + <?=$source_net?> + </td> + <td> + <?=htmlspecialchars($natent['descr'])?> + </td> + <td> + <a href="firewall_nat_npt_edit.php?id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext("Edit")?></a> + <a href="firewall_nat_npt.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger"><?=gettext("Delete")?></a> + </td> + </tr> +<?php + $i++; +endforeach; +?> + </tbody> + </table> +</div> + +<nav class="action-buttons"> + <a href="firewall_nat_npt_edit.php" class="btn btn-sm btn-success"><?=gettext("Add rule")?></a> + <input type="submit" id="order-store" class="btn btn-primary btn-sm" value="store changes" disabled="disabled" /> +</nav> + +</form> +<script> +events.push(function() { + // Make rules draggable/sortable + $('table tbody.user-entries').sortable({ + cursor: 'grabbing', + update: function(event, ui) { + $('#order-store').removeAttr('disabled'); + } + }); +}); +</script> + +<?php + +include("foot.inc"); diff --git a/src/usr/local/www/firewall_nat_npt_edit.php b/src/usr/local/www/firewall_nat_npt_edit.php new file mode 100644 index 0000000..7e385e5 --- /dev/null +++ b/src/usr/local/www/firewall_nat_npt_edit.php @@ -0,0 +1,285 @@ +<?php +/* $Id$ */ +/* + firewall_nat_npt_edit.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2011 Seth Mos <seth.mos@dds.nl> + * part of pfSense (https://www.pfsense.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: nat +*/ + +##|+PRIV +##|*IDENT=page-firewall-nat-npt-edit +##|*NAME=Firewall: NAT: NPt: Edit page +##|*DESCR=Allow access to the 'Firewall: NAT: NPt: Edit' page. +##|*MATCH=firewall_nat_npt_edit.php* +##|-PRIV + +function natnptcmp($a, $b) { + return ipcmp($a['external'], $b['external']); +} + +function nat_npt_rules_sort() { + global $g, $config; + + if (!is_array($config['nat']['npt'])) { + return; + } + + usort($config['nat']['npt'], "natnptcmp"); +} + +require("guiconfig.inc"); +require_once("interfaces.inc"); +require_once("filter.inc"); +require("shaper.inc"); + +$ifdisp = get_configured_interface_with_descr(); + +foreach ($ifdisp as $kif => $kdescr) { + $specialsrcdst[] = "{$kif}"; + $specialsrcdst[] = "{$kif}ip"; +} + +if (!is_array($config['nat']['npt'])) + $config['nat']['npt'] = array(); + +$a_npt = &$config['nat']['npt']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_npt[$id]) { + $pconfig['disabled'] = isset($a_npt[$id]['disabled']); + + address_to_pconfig($a_npt[$id]['source'], $pconfig['src'], + $pconfig['srcmask'], $pconfig['srcnot'], + $pconfig['srcbeginport'], $pconfig['srcendport']); + + address_to_pconfig($a_npt[$id]['destination'], $pconfig['dst'], + $pconfig['dstmask'], $pconfig['dstnot'], + $pconfig['dstbeginport'], $pconfig['dstendport']); + + $pconfig['interface'] = $a_npt[$id]['interface']; + if (!$pconfig['interface']) { + $pconfig['interface'] = "wan"; + } + + $pconfig['external'] = $a_npt[$id]['external']; + $pconfig['descr'] = $a_npt[$id]['descr']; +} else { + $pconfig['interface'] = "wan"; +} + + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "interface"); + $reqdfieldsn = array(gettext("Interface")); + $reqdfields[] = "src"; + $reqdfieldsn[] = gettext("Source prefix"); + $reqdfields[] = "dst"; + $reqdfieldsn[] = gettext("Destination prefix"); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (!$input_errors) { + $natent = array(); + + $natent['disabled'] = isset($_POST['disabled']) ? true:false; + $natent['descr'] = $_POST['descr']; + $natent['interface'] = $_POST['interface']; + + if ($_POST['src']) { + $_POST['src'] = trim($_POST['src']); + } + if ($_POST['dst']) { + $_POST['dst'] = trim($_POST['dst']); + } + + pconfig_to_address($natent['source'], $_POST['src'], $_POST['srcmask'], $_POST['srcnot']); + + pconfig_to_address($natent['destination'], $_POST['dst'], $_POST['dstmask'], $_POST['dstnot']); + + if (isset($id) && $a_npt[$id]) { + $a_npt[$id] = $natent; + } else { + $a_npt[] = $natent; + } + nat_npt_rules_sort(); + + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + + header("Location: firewall_nat_npt.php"); + exit; + } +} + +function build_if_list() { + global $ifdisp; + + foreach ($ifdisp as $if => $ifdesc) { + if(have_ruleint_access($if)) + $interfaces[$if] = $ifdesc; + } + + if ($config['l2tp']['mode'] == "server") + if(have_ruleint_access("l2tp")) + $interfaces['l2tp'] = "L2TP VPN"; + + if ($config['pptpd']['mode'] == "server") + if(have_ruleint_access("pptp")) + $interfaces['pptp'] = "PPTP VPN"; + + if ($config['pppoe']['mode'] == "server") + if(have_ruleint_access("pppoe")) + $interfaces['pppoe'] = "PPPoE Server"; + + /* add ipsec interfaces */ + if (isset($config['ipsec']['enable']) || isset($config['ipsec']['mobileclients']['enable'])) { + if(have_ruleint_access("enc0")) + $interfaces["enc0"] = "IPsec"; + } + + /* add openvpn/tun interfaces */ + if ($config['openvpn']["openvpn-server"] || $config['openvpn']["openvpn-client"]) + $interfaces["openvpn"] = "OpenVPN"; + + return($interfaces); +} + +$pgtitle = array(gettext("Firewall"),gettext("NAT"),gettext("NPt"),gettext("Edit")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Edit NAT NPt entry'); + +$section->addInput(new Form_Checkbox( + 'disabled', + 'Disabled', + 'Disable this rule', + $pconfig['disabled'] +)); + +$section->addInput(new Form_Select( + 'interface', + 'Interface', + $pconfig['interface'], + build_if_list() +))->setHelp('Choose which interface this rule applies to.' . '<br />' . + 'Hint: in most cases, you\'ll want to use "WAN" here.'); + +$section->addInput(new Form_Checkbox( + 'srcnot', + 'Internal IPv6 prefix', + 'Not', + $pconfig['srcnot'] +))->setHelp('Use this option to invert the sense of the match. '); + +$section->addInput(new Form_IpAddress( + 'src', + 'Address', + $pconfig['src'] +))->addMask('srcmask', $pconfig['srcmask'])->setHelp('Internal (LAN) ULA IPv6 Prefix for the Network Prefix translation. ' . + 'The prefix size specified for the internal IPv6 prefix will be applied to the external prefix.'); + +$section->addInput(new Form_Checkbox( + 'dstnot', + 'Destination IPv6 prefix', + 'Not', + $pconfig['dstnot'] +))->setHelp('Use this option to invert the sense of the match. '); + +$section->addInput(new Form_IpAddress( + 'dst', + 'Address', + $pconfig['dst'] +))->addMask('dstmask', $pconfig['dstmask'])->setHelp('Global Unicast routable IPv6 prefix'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +if (isset($id) && $a_npt[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_nat_out.php b/src/usr/local/www/firewall_nat_out.php new file mode 100644 index 0000000..8f3d458 --- /dev/null +++ b/src/usr/local/www/firewall_nat_out.php @@ -0,0 +1,736 @@ +<?php +/* $Id$ */ +/* + firewall_nat_out.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004 Scott Ullrich + * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net> + * Originally part of pfSense (https://www.pfsense.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: nat +*/ + +##|+PRIV +##|*IDENT=page-firewall-nat-outbound +##|*NAME=Firewall: NAT: Outbound page +##|*DESCR=Allow access to the 'Firewall: NAT: Outbound' page. +##|*MATCH=firewall_nat_out.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +global $FilterIflist; +global $GatewaysList; + +if (!is_array($config['nat']['outbound'])) { + $config['nat']['outbound'] = array(); +} + +if (!is_array($config['nat']['outbound']['rule'])) { + $config['nat']['outbound']['rule'] = array(); +} + +$a_out = &$config['nat']['outbound']['rule']; + +/* update rule order, POST[rule] is an array of ordered IDs */ +if (is_array($_POST['rule']) && !empty($_POST['rule'])) { + $a_out_new = array(); + + // if a rule is not in POST[rule], it has been deleted by the user + foreach ($_POST['rule'] as $id) + $a_out_new[] = $a_out[$id]; + + $a_out = $a_out_new; + + if (write_config()) + mark_subsystem_dirty('filter'); + + header("Location: firewall_nat_out.php"); + exit; +} + +if (!isset($config['nat']['outbound']['mode'])) + $config['nat']['outbound']['mode'] = "automatic"; + +$mode = $config['nat']['outbound']['mode']; + +if ($_POST['apply']) { + $retval = 0; + $retval |= filter_configure(); + + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message($retval); + } else { + $savemsg = $retval; + } + + if ($retval == 0) { + clear_subsystem_dirty('natconf'); + clear_subsystem_dirty('filter'); + } +} + +if (isset($_POST['save']) && $_POST['save'] == "Save") { + /* mutually exclusive settings - if user wants advanced NAT, we don't generate automatic rules */ + if ($_POST['mode'] == "advanced" && ($mode == "automatic" || $mode == "hybrid")) { + /* + * user has enabled advanced outbound NAT and doesn't have rules + * lets automatically create entries + * for all of the interfaces to make life easier on the pip-o-chap + */ + if (empty($FilterIflist)) { + filter_generate_optcfg_array(); + } + if (empty($GatewaysList)) { + filter_generate_gateways(); + } + $tonathosts = filter_nat_rules_automatic_tonathosts(true); + $automatic_rules = filter_nat_rules_outbound_automatic(""); + + foreach ($tonathosts as $tonathost) { + foreach ($automatic_rules as $natent) { + $natent['source']['network'] = $tonathost['subnet']; + $natent['descr'] .= sprintf(gettext(' - %1$s to %2$s'), + $tonathost['descr'], + convert_real_interface_to_friendly_descr($natent['interface'])); + $natent['created'] = make_config_revision_entry(null, gettext("Manual Outbound NAT Switch")); + + /* Try to detect already auto created rules and avoid duplicating them */ + $found = false; + foreach ($a_out as $rule) { + if ($rule['interface'] == $natent['interface'] && + $rule['source']['network'] == $natent['source']['network'] && + $rule['dstport'] == $natent['dstport'] && + $rule['target'] == $natent['target'] && + $rule['descr'] == $natent['descr']) { + $found = true; + break; + } + } + + if ($found === false) { + $a_out[] = $natent; + } + } + } + $savemsg = gettext("Default rules for each interface have been created."); + unset($FilterIflist, $GatewaysList); + } + + $config['nat']['outbound']['mode'] = $_POST['mode']; + + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat_out.php"); + exit; +} + +if ($_GET['act'] == "del") { + if ($a_out[$_GET['id']]) { + unset($a_out[$_GET['id']]); + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat_out.php"); + exit; + } +} + +if (isset($_POST['del_x'])) { + /* delete selected rules */ + if (is_array($_POST['rule']) && count($_POST['rule'])) { + foreach ($_POST['rule'] as $rulei) { + unset($a_out[$rulei]); + } + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat_out.php"); + exit; + } + +} else if ($_GET['act'] == "toggle") { + if ($a_out[$_GET['id']]) { + if (isset($a_out[$_GET['id']]['disabled'])) { + unset($a_out[$_GET['id']]['disabled']); + } else { + $a_out[$_GET['id']]['disabled'] = true; + } + if (write_config("Firewall: NAT: Outbound, enable/disable NAT rule")) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat_out.php"); + exit; + } +} else { + /* yuck - IE won't send value attributes for image buttons, while Mozilla does - so we use .x/.y to find move button clicks instead... */ + unset($movebtn); + foreach ($_POST as $pn => $pd) { + if (preg_match("/move_(\d+)_x/", $pn, $matches)) { + $movebtn = $matches[1]; + break; + } + } + /* move selected rules before this rule */ + if (isset($movebtn) && is_array($_POST['rule']) && count($_POST['rule'])) { + $a_out_new = array(); + + /* copy all rules < $movebtn and not selected */ + for ($i = 0; $i < $movebtn; $i++) { + if (!in_array($i, $_POST['rule'])) { + $a_out_new[] = $a_out[$i]; + } + } + + /* copy all selected rules */ + for ($i = 0; $i < count($a_out); $i++) { + if ($i == $movebtn) { + continue; + } + if (in_array($i, $_POST['rule'])) { + $a_out_new[] = $a_out[$i]; + } + } + + /* copy $movebtn rule */ + if ($movebtn < count($a_out)) { + $a_out_new[] = $a_out[$movebtn]; + } + + /* copy all rules > $movebtn and not selected */ + for ($i = $movebtn+1; $i < count($a_out); $i++) { + if (!in_array($i, $_POST['rule'])) { + $a_out_new[] = $a_out[$i]; + } + } + if (count($a_out_new) > 0) { + $a_out = $a_out_new; + } + + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat_out.php"); + exit; + } +} + +function rule_popup($src,$srcport,$dst,$dstport){ + global $config,$g; + $aliases_array = array(); + if ($config['aliases']['alias'] <> "" and is_array($config['aliases']['alias'])) { + $descriptions = array (); + + foreach ($config['aliases']['alias'] as $alias_id=>$alias_name){ + $loading_image="<a><img src=\'/themes/{$g['theme']}/images/misc/loader.gif\' alt=\'loader\' /> " .gettext("loading...")."</a>"; + + switch ($alias_name['type']){ + case "port": + $width="250"; + break; + case "urltable": + $width="500"; + break; + default: + $width="350"; + + break; + } + $span_begin = "<span style=\"cursor: help;\" onmouseover=\"var response_html=domTT_activate(this, event, 'id','ttalias_{$alias_id}','content','{$loading_image}', 'trail', true, 'delay', 300, 'fade', 'both', 'fadeMax', 93, 'styleClass', 'niceTitle','type','velcro','width',{$width});alias_popup('{$alias_id}','{$g['theme']}','".gettext('loading...')."');\" onmouseout=\"this.style.color = ''; domTT_mouseout(this, event);\"><u>"; + $span_end = "</u></span>"; + + if ($alias_name['name'] == $src) { + $descriptions['src'] = $span_begin; + $descriptions['src_end'] = $span_end; + } + + if ($alias_name['name'] == $srcport) { + $descriptions['srcport'] = $span_begin; + $descriptions['srcport_end'] = $span_end; + } + + if ($alias_name['name'] == $dst ) { + $descriptions['dst'] = $span_begin; + $descriptions['dst_end'] = $span_end; + } + + if ($alias_name['name'] == $dstport) { + $descriptions['dstport'] = $span_begin; + $descriptions['dstport_end'] = $span_end; + } + } + + return $descriptions; + } +} + +$pgtitle = array(gettext("Firewall"),gettext("NAT"),gettext("Outbound")); +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('natconf')) + print_info_box_np(gettext("The NAT configuration has been changed.")."<br />".gettext("You must apply the changes in order for them to take effect.")); + +$tab_array = array(); +$tab_array[] = array(gettext("Port Forward"), false, "firewall_nat.php"); +$tab_array[] = array(gettext("1:1"), false, "firewall_nat_1to1.php"); +$tab_array[] = array(gettext("Outbound"), true, "firewall_nat_out.php"); +$tab_array[] = array(gettext("NPt"), false, "firewall_nat_npt.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('General Logging Options'); + +$group = new Form_Group('Mode'); + +$group->add(new Form_Checkbox( + 'mode', + 'Mode', + null, + $mode == 'automatic', + 'automatic' +))->displayAsRadio()->setHelp('Automatic outbound NAT rule generation.' . '<br />' . '(IPsec passthrough included)'); + +$group->add(new Form_Checkbox( + 'mode', + null, + null, + $mode == 'hybrid', + 'hybrid' +))->displayAsRadio()->setHelp('Hybrid Outbound NAT rule generation.' . '<br />' . '(Automatic Outbound NAT + rules below)'); + +$group->add(new Form_Checkbox( + 'mode', + null, + null, + $mode == 'advanced', + 'advanced' +))->displayAsRadio()->setHelp('Manual Outbound NAT rule generation.' . '<br />' . '(AON - Advanced Outbound NAT)'); + +$group->add(new Form_Checkbox( + 'mode', + null, + null, + $mode == 'disabled', + 'disabled' +))->displayAsRadio()->setHelp('Disable Outbound NAT rule generation.' . '<br />' . '(No Outbound NAT rules)'); + +$section->add($group); + +$form->add($section); +print($form); +?> + +<form action="firewall_nat_out.php" method="post" name="iform"> + <div class="panel panel-default"> + <div class="panel-heading"><?=gettext('Mappings')?></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><!-- status --></th> + <th><?=gettext("Interface")?></th> + <th><?=gettext("Source")?></th> + <th><?=gettext("Source Port")?></th> + <th><?=gettext("Destination")?></th> + <th><?=gettext("Destination Port")?></th> + <th><?=gettext("NAT Address")?></th> + <th><?=gettext("NAT Port")?></th> + <th><?=gettext("Static Port")?></th> + <th><?=gettext("Description")?></th> + <th><?=gettext("Actions")?></th> + </tr> + </thead> + <tbody class="user-entries"> +<?php + $i = 0; + foreach ($a_out as $natent): + $iconfn = "pass"; + $textss = $textse = ""; + if ($mode == "disabled" || $mode == "automatic" || isset($natent['disabled'])) + $iconfn .= "_d"; + + + $alias = rule_columns_with_alias( + $natent['source']['address'], + pprint_port($natent['source']['port']), + $natent['destination']['address'], + pprint_port($natent['destination']['port']) + ); +?> + <tr id="fr<?=$i?>"> + <td> +<?php + if ($mode == "disabled" || $mode == "automatic"): +?> + <i class="<?= ($iconfn == "pass") ? "icon-ok":"icon-remove"?>"title="<?=gettext("Click to toggle enabled/disabled status")?>"></i> +<?php + else: +?> + <a href="?act=toggle&id=<?=$i?>"> + <i class="<?= ($iconfn == "pass") ? "icon-ok":"icon-remove"?>" title="<?=gettext("Click to toggle enabled/disabled status")?>"></i> + </a> + +<?php + endif; +?> + </td> + + <td> + <input type="hidden" name="rule[]" value="<?=$i?>" /> + <?=htmlspecialchars(convert_friendly_interface_to_friendly_descr($natent['interface']))?> + </td> + + <td> +<?php + $natent['source']['network'] = ($natent['source']['network'] == "(self)") ? "This Firewall" : $natent['source']['network']; +?> +<?php + if (isset($alias['src'])): +?> + <a href="/firewall_aliases_edit.php?id=<?=$alias['src']?>" data-toggle="popover" data-trigger="hover focus" title="Alias details" data-content="<?=alias_info_popup($alias['src'])?>" data-html="true"> +<?php + endif; +?> + <?=htmlspecialchars($natent['source']['network'])?> +<?php + if (isset($alias['src'])): +?> + <i class='icon icon-pencil'></i></a> +<?php + endif; +?> + </td> + + <td> +<?php + echo ($natent['protocol']) ? $natent['protocol'] . '/' : "" ; + if (!$natent['sourceport']) + echo "*"; + else { + + if (isset($alias['srcport'])): +?> + <a href="/firewall_aliases_edit.php?id=<?=$alias['srcport']?>" data-toggle="popover" data-trigger="hover focus" title="Alias details" data-content="<?=alias_info_popup($alias['srcport'])?>" data-html="true"> +<?php + endif; +?> + <?=htmlspecialchars($natent['sourceport'])?> +<?php + if (isset($alias['srcport'])): +?> + <i class='icon icon-pencil'></i></a> +<?php + endif; + } +?> + </td> + + <td> +<?php + if (isset($natent['destination']['any'])) + echo "*"; + else { + if (isset($natent['destination']['not'])) + echo "! "; + + + if (isset($alias['dst'])): +?> + <a href="/firewall_aliases_edit.php?id=<?=$alias['dst']?>" data-toggle="popover" data-trigger="hover focus" title="Alias details" data-content="<?=alias_info_popup($alias['dst'])?>" data-html="true"> +<?php + endif; +?> + <?=htmlspecialchars($natent['destination']['address'])?> +<?php + if (isset($alias['dst'])): +?> + <i class='icon icon-pencil'></i></a> +<?php + endif; + } +?> + </td> + + <td> +<?php + echo ($natent['protocol']) ? $natent['protocol'] . '/' : "" ; + + if (!$natent['dstport']) + echo "*"; + else { + if (isset($alias['dstport'])): +?> + <a href="/firewall_aliases_edit.php?id=<?=$alias['dstport']?>" data-toggle="popover" data-trigger="hover focus" title="Alias details" data-content="<?=alias_info_popup($alias['dstport'])?>" data-html="true"> +<?php + endif; +?> + <?=htmlspecialchars($natent['dstport'])?> +<?php + if (isset($alias['dstport'])): +?> + <i class='icon icon-pencil'></i></a> +<?php + endif; + } +?> + + </td> + + <td> +<?php + if (isset($natent['nonat'])) + echo '<I>NO NAT</I>'; + elseif (!$natent['target']) + echo htmlspecialchars(convert_friendly_interface_to_friendly_descr($natent['interface'])) . " address"; + elseif ($natent['target'] == "other-subnet") + echo $natent['targetip'] . '/' . $natent['targetip_subnet']; + else + echo $natent['target']; +?> + </td> + + <td> +<?php + if (!$natent['natport']) + echo "*"; + else + echo $natent['natport']; +?> + </td> + + <td> +<?php + if(isset($natent['staticnatport'])) + echo gettext("YES"); + else + echo gettext("NO"); +?> + </td> + + <td> + <?=htmlspecialchars($natent['descr'])?> + </td> + + <!-- Action icons --> + <td> + <a class="btn btn-xs btn-info" title="<?=gettext("Edit mapping")?>" href="firewall_nat_out_edit.php?id=<?=$i?>"><?=gettext("Edit")?></a> + <a class="btn btn-xs btn-danger" title="<?=gettext("Delete mapping")?>" href="firewall_nat_out.php?act=del&id=<?=$i?>"><?=gettext("Del")?></a> + <a class="btn btn-xs btn-success" title="<?=gettext("Add a new mapping based on this one")?>" href="firewall_nat_out_edit.php?dup=<?=$i?>"><?=gettext("Clone")?></a> + </td> +<?php + $i++; + endforeach; +?> + </tr> + </tbody> + </table> + </div> + </div> + + <nav class="action-buttons"> + <a href="firewall_nat_out_edit.php?after=-1" class="btn btn-sm btn-success" title="<?=gettext('Add new mapping')?>"><?=gettext('Add new mapping')?></a> + <input type="submit" id="order-store" class="btn btn-primary btn-sm" value="store changes" disabled="disabled" /> + </nav> + +<?php +if ($mode == "automatic" || $mode == "hybrid"): + if(empty($FilterIflist)) + filter_generate_optcfg_array(); + + if(empty($GatewaysList)) + filter_generate_gateways(); + + $automatic_rules = filter_nat_rules_outbound_automatic(implode(" ", filter_nat_rules_automatic_tonathosts())); + unset($FilterIflist, $GatewaysList); +?> + <div class="panel panel-default"> + <div class="panel-heading"><?=gettext("Automatic rules:")?></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><!-- status --></th> + <th><?=gettext("Interface")?></th> + <th><?=gettext("Source")?></th> + <th><?=gettext("Source Port")?></th> + <th><?=gettext("Destination")?></th> + <th><?=gettext("Destination Port")?></th> + <th><?=gettext("NAT Address")?></th> + <th><?=gettext("NAT Port")?></th> + <th><?=gettext("Static Port")?></th> + <th><?=gettext("Description")?></th> + + </tr> +<?php + foreach ($automatic_rules as $natent): +?> + <tr> + <td> + <i class="icon-ok" title="<?=gettext("automatic outbound nat")?>"></i> + </td> + <td> + <?=htmlspecialchars(convert_friendly_interface_to_friendly_descr($natent['interface'])); ?> + </td> + <td> + <?=$natent['source']['network']?> + </td> + <td> +<?php + echo ($natent['protocol']) ? $natent['protocol'] . '/' : "" ; + + if (!$natent['sourceport']) + echo "*"; + else + echo $natent['sourceport']; +?> + </td> + <td> +<?php + if (isset($natent['destination']['any'])) + echo "*"; + else { + if (isset($natent['destination']['not'])) + echo "! "; + + echo $natent['destination']['address']; + } +?> + </td> + <td> +<?php + echo ($natent['protocol']) ? $natent['protocol'] . '/' : "" ; + if (!$natent['dstport']) + echo "*"; + else + echo $natent['dstport']; +?> + </td> + <td> +<?php + if (isset($natent['nonat'])) + echo 'NO NAT'; + elseif (!$natent['target']) + echo htmlspecialchars(convert_friendly_interface_to_friendly_descr($natent['interface'])) . " address"; + elseif ($natent['target'] == "other-subnet") + echo $natent['targetip'] . '/' . $natent['targetip_subnet']; + else + echo $natent['target']; +?> + </td> + <td> +<?php + if (!$natent['natport']) + echo "*"; + else + echo $natent['natport']; +?> + </td> + <td> +<?php + if(isset($natent['staticnatport'])) + echo gettext("YES"); + else + echo gettext("NO"); +?> + </td> + <td> + <?=htmlspecialchars($natent['descr'])?> + </td> + </tr> +<?php + endforeach; +endif; +?> + </tbody> + </table> + </div> + </div> +</form> + +<div> +<?php + print_info_box(gettext('If automatic outbound NAT selected, a mapping is automatically generated for each interface\'s subnet (except WAN-type connections) and the rules ' . + 'on "Mappings" section of this page are ignored.' . '<br />' . + 'If manual outbound NAT is selected, outbound NAT rules will not be automatically generated and only the mappings you specify on this page ' . + 'will be used.' . '<br />' . + 'If hybrid outbound NAT is selected, mappings you specify on this page will be used, followed by the automatically generated ones.' . '<br />' . + 'If disable outbound NAT is selected, no rules will be used.' . '<br />' . + 'If a target address other than an interface\'s IP address is used, then depending on the way the WAN connection is setup, a ') . + '<a href="firewall_virtual_ip.php">' . gettext("Virtual IP") . '</a>' . gettext(" may also be required.") + ); +?> +</div> + +<script> +events.push(function() { + // Make rules draggable/sortable + $('table tbody.user-entries').sortable({ + cursor: 'grabbing', + update: function(event, ui) { + $('#order-store').removeAttr('disabled'); + } + }); +}); +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_nat_out_edit.php b/src/usr/local/www/firewall_nat_out_edit.php new file mode 100644 index 0000000..4d85609 --- /dev/null +++ b/src/usr/local/www/firewall_nat_out_edit.php @@ -0,0 +1,851 @@ +<?php +/* $Id$ */ +/* + firewall_nat_out_edit.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004 Scott Ullrich + * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net> + * Originally part of pfSense (https://www.pfsense.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: nat +*/ + +##|+PRIV +##|*IDENT=page-firewall-nat-outbound-edit +##|*NAME=Firewall: NAT: Outbound: Edit page +##|*DESCR=Allow access to the 'Firewall: NAT: Outbound: Edit' page. +##|*MATCH=firewall_nat_out_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("filter.inc"); +require("shaper.inc"); + +if (!is_array($config['nat']['outbound'])) + $config['nat']['outbound'] = array(); + +if (!is_array($config['nat']['outbound']['rule'])) { + $config['nat']['outbound']['rule'] = array(); +} + +$a_out = &$config['nat']['outbound']['rule']; + +if (!is_array($config['aliases']['alias'])) { + $config['aliases']['alias'] = array(); +} +$a_aliases = &$config['aliases']['alias']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (is_numericint($_GET['after']) || $_GET['after'] == "-1") { + $after = $_GET['after']; +} +if (isset($_POST['after']) && (is_numericint($_POST['after']) || $_POST['after'] == "-1")) { + $after = $_POST['after']; +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + $id = $_GET['dup']; + $after = $_GET['dup']; +} + +if (isset($id) && $a_out[$id]) { + if (isset($a_out[$id]['created']) && is_array($a_out[$id]['created'])) { + $pconfig['created'] = $a_out[$id]['created']; + } + + if (isset($a_out[$id]['updated']) && is_array($a_out[$id]['updated'])) { + $pconfig['updated'] = $a_out[$id]['updated']; + } + + $pconfig['protocol'] = $a_out[$id]['protocol']; + list($pconfig['source'], $pconfig['source_subnet']) = explode('/', $a_out[$id]['source']['network']); + if (!is_numeric($pconfig['source_subnet'])) { + $pconfig['source_subnet'] = 32; + } + $pconfig['sourceport'] = $a_out[$id]['sourceport']; + address_to_pconfig($a_out[$id]['destination'], $pconfig['destination'], + $pconfig['destination_subnet'], $pconfig['destination_not'], + $none, $none); + + $pconfig['dstport'] = $a_out[$id]['dstport']; + $pconfig['natport'] = $a_out[$id]['natport']; + $pconfig['target'] = $a_out[$id]['target']; + $pconfig['targetip'] = $a_out[$id]['targetip']; + $pconfig['targetip_subnet'] = $a_out[$id]['targetip_subnet']; + $pconfig['poolopts'] = $a_out[$id]['poolopts']; + $pconfig['interface'] = $a_out[$id]['interface']; + + if (!$pconfig['interface']) { + $pconfig['interface'] = "wan"; + } + + $pconfig['descr'] = $a_out[$id]['descr']; + $pconfig['nonat'] = $a_out[$id]['nonat']; + $pconfig['disabled'] = isset($a_out[$id]['disabled']); + $pconfig['staticnatport'] = isset($a_out[$id]['staticnatport']); + $pconfig['nosync'] = isset($a_out[$id]['nosync']); +} else { + $pconfig['source_subnet'] = 24; + $pconfig['destination'] = "any"; + $pconfig['destination_subnet'] = 24; + $pconfig['interface'] = "wan"; +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + unset($id); +} + +if ($_POST) { + if ($_POST['destination_type'] == "any") { + $_POST['destination'] = "any"; + $_POST['destination_subnet'] = 24; + } + + if ($_POST['source_type'] == "any") { + $_POST['source'] = "any"; + $_POST['source_subnet'] = 24; + } elseif ($_POST['source_type'] == "(self)") { + $_POST['source'] = "(self)"; + $_POST['source_subnet'] = 24; + } + + unset($input_errors); + $pconfig = $_POST; + /* run through $_POST items encoding HTML entitles so that the user + * cannot think he is slick and perform a XSS attack on the unwilling + */ + foreach ($_POST as $key => $value) { + $temp = str_replace(">", "", $value); + $newpost = htmlentities($temp); + if ($newpost <> $temp) { + $input_errors[] = sprintf(gettext("Invalid characters detected (%s). Please remove invalid characters and save again."), $temp); + } + } + + /* input validation */ + $reqdfields = explode(" ", "interface protocol source source_subnet destination destination_subnet"); + $reqdfieldsn = array(gettext("Interface"), gettext("Protocol"), gettext("Source"), gettext("Source bit count"), gettext("Destination"), gettext("Destination bit count")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + $protocol_uses_ports = in_array($_POST['protocol'], explode(" ", "any tcp udp tcp/udp")); + + if ($_POST['source']) { + $_POST['source'] = trim($_POST['source']); + } + if ($_POST['destination']) { + $_POST['destination'] = trim($_POST['destination']); + } + if ($_POST['targetip']) { + $_POST['targetip'] = trim($_POST['targetip']); + } + if ($_POST['sourceport']) { + $_POST['sourceport'] = trim($_POST['sourceport']); + } + if ($_POST['dstport']) { + $_POST['dstport'] = trim($_POST['dstport']); + } + if ($_POST['natport']) { + $_POST['natport'] = trim($_POST['natport']); + } + + if ($protocol_uses_ports && $_POST['sourceport'] <> "" && !(is_portoralias($_POST['sourceport']) || is_portrange($_POST['sourceport']))) { + $input_errors[] = gettext("You must supply either a valid port or port alias for the source port entry."); + } + + if ($protocol_uses_ports && $_POST['dstport'] <> "" && !(is_portoralias($_POST['dstport']) || is_portrange($_POST['dstport']))) { + $input_errors[] = gettext("You must supply either a valid port or port alias for the destination port entry."); + } + + if ($protocol_uses_ports && $_POST['natport'] <> "" && !is_port($_POST['natport']) && !isset($_POST['nonat'])) { + $input_errors[] = gettext("You must supply a valid port for the NAT port entry."); + } + + if (($_POST['source_type'] != "any") && ($_POST['source_type'] != "(self)")) { + if ($_POST['source'] && !is_ipaddroralias($_POST['source']) && $_POST['source'] != "any") { + $input_errors[] = gettext("A valid source must be specified."); + } + } + + if ($_POST['source_subnet'] && !is_numericint($_POST['source_subnet'])) { + $input_errors[] = gettext("A valid source bit count must be specified."); + } + + if ($_POST['destination_type'] != "any") { + if ($_POST['destination'] && !is_ipaddroralias($_POST['destination'])) { + $input_errors[] = gettext("A valid destination must be specified."); + } + } + + if ($_POST['destination_subnet'] && !is_numericint($_POST['destination_subnet'])) { + $input_errors[] = gettext("A valid destination bit count must be specified."); + } + + if ($_POST['destination_type'] == "any") { + if ($_POST['destination_not']) { + $input_errors[] = gettext("Negating destination address of \"any\" is invalid."); + } + } + + if ($_POST['target'] && !is_ipaddr($_POST['target']) && !is_subnet($_POST['target']) && !is_alias($_POST['target']) && !isset($_POST['nonat']) && !($_POST['target'] == "other-subnet")) { + $input_errors[] = gettext("A valid target IP address must be specified."); + } + + if ($_POST['target'] == "other-subnet") { + if (!is_ipaddr($_POST['targetip'])) { + $input_errors[] = gettext("A valid target IP must be specified when using the 'Other Subnet' type."); + } + + if (!is_numericint($_POST['targetip_subnet'])) { + $input_errors[] = gettext("A valid target bit count must be specified when using the 'Other Subnet' type."); + } + } + + /* Verify Pool Options */ + $poolopts = ""; + if ($_POST['poolopts']) { + if (is_subnet($_POST['target']) || ($_POST['target'] == "other-subnet")) { + $poolopts = $_POST['poolopts']; + } elseif (is_alias($_POST['target'])) { + if (substr($_POST['poolopts'], 0, 11) == "round-robin") { + $poolopts = $_POST['poolopts']; + } else { + $input_errors[] = gettext("Only Round Robin pool options may be chosen when selecting an alias."); + } + } + } + + /* if user has selected any as source, set it here */ + if ($_POST['source_type'] == "any") { + $osn = "any"; + } else if ($_POST['source_type'] == "(self)") { + $osn = "(self)"; + } else if (is_alias($_POST['source'])) { + $osn = $_POST['source']; + } else { + $osn = gen_subnet($_POST['source'], $_POST['source_subnet']) . "/" . $_POST['source_subnet']; + } + + /* check for existing entries */ + if ($_POST['destination_type'] == "any") { + $ext = "any"; + } else if (is_alias($_POST['destination'])) { + $ext = $_POST['destination']; + } else { + $ext = gen_subnet($_POST['destination'], $_POST['destination_subnet']) . "/" . $_POST['destination_subnet']; + } + + foreach ($a_out as $natent) { + if (isset($id) && ($a_out[$id]) && ($a_out[$id] === $natent)) { + continue; + } + + if (!$natent['interface']) { + $natent['interface'] = "wan"; + } + } + + if (!$input_errors) { + $natent = array(); + $natent['source']['network'] = $osn; + $natent['sourceport'] = ($protocol_uses_ports) ? $_POST['sourceport'] : ""; + $natent['descr'] = $_POST['descr']; + $natent['target'] = (!isset($_POST['nonat'])) ? $_POST['target'] : ""; + $natent['targetip'] = (!isset($_POST['nonat'])) ? $_POST['targetip'] : ""; + $natent['targetip_subnet'] = (!isset($_POST['nonat'])) ? $_POST['targetip_subnet'] : ""; + $natent['interface'] = $_POST['interface']; + $natent['poolopts'] = $poolopts; + + /* static-port */ + if (isset($_POST['staticnatport']) && $protocol_uses_ports && !isset($_POST['nonat'])) { + $natent['staticnatport'] = true; + } else { + unset($natent['staticnatport']); + } + + if (isset($_POST['disabled'])) { + $natent['disabled'] = true; + } else { + unset($natent['disabled']); + } + + /* if user has selected not nat, set it here */ + if (isset($_POST['nonat'])) { + $natent['nonat'] = true; + } else { + unset($natent['nonat']); + } + + if ($_POST['protocol'] && $_POST['protocol'] != "any") { + $natent['protocol'] = $_POST['protocol']; + } else { + unset($natent['protocol']); + } + + if ($ext == "any") { + $natent['destination']['any'] = true; + } else { + $natent['destination']['address'] = $ext; + } + if ($_POST['natport'] != "" && $protocol_uses_ports && !isset($_POST['nonat'])) { + $natent['natport'] = $_POST['natport']; + } else { + unset($natent['natport']); + } + if ($_POST['dstport'] != "" && $protocol_uses_ports) { + $natent['dstport'] = $_POST['dstport']; + } else { + unset($natent['dstport']); + } + + if ($_POST['nosync'] == "yes") { + $natent['nosync'] = true; + } else { + unset($natent['nosync']); + } + + if (isset($_POST['destination_not']) && $ext != "any") { + $natent['destination']['not'] = true; + } + + if (isset($a_out[$id]['created']) && is_array($a_out[$id]['created'])) { + $natent['created'] = $a_out[$id]['created']; + } + + $natent['updated'] = make_config_revision_entry(); + + // Allow extending of the firewall edit page and include custom input validation + pfSense_handle_custom_code("/usr/local/pkg/firewall_aon/pre_write_config"); + + if (isset($id) && $a_out[$id]) { + $a_out[$id] = $natent; + } else { + $natent['created'] = make_config_revision_entry(); + if (is_numeric($after)) { + array_splice($a_out, $after+1, 0, array($natent)); + } else { + $a_out[] = $natent; + } + } + + if (write_config()) { + mark_subsystem_dirty('natconf'); + } + header("Location: firewall_nat_out.php"); + exit; + } +} + +$pgtitle = array(gettext("Firewall"), gettext("NAT"), gettext("Outbound"), gettext("Edit")); +$closehead = false; +include("head.inc"); + +function build_target_list() { + global $config, $sn, $a_aliases; + $list = array(); + + $list[""] = 'Interface Address'; + + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $sn) { + if (isset($sn['noexpand'])) + continue; + + if ($sn['mode'] == "proxyarp" && $sn['type'] == "network") { + $start = ip2long32(gen_subnet($sn['subnet'], $sn['subnet_bits'])); + $end = ip2long32(gen_subnet_max($sn['subnet'], $sn['subnet_bits'])); + $len = $end - $start; + $list[$sn['subnet'] . '/' . $sn['subnet_bits']] = 'Subnet: ' . $sn['subnet'] . '/' . $sn['subnet_bits'] . ' (' . $sn['descr'] . ')'; + + for ($i = 0; $i <= $len; $i++) { + $snip = long2ip32($start+$i); + + $list[$snip] = $snip . ' (' . $sn['descr'] . ')'; + } + } else { + $list[$sn['subnet']] = $sn['subnet'] . ' (' . $sn['descr'] . ')'; + } + } + } + + foreach ($a_aliases as $alias) { + if ($alias['type'] != "host") + continue; + + $list[$alias['name']] = 'Host Alias: ' . $alias['name'] . ' (' . $alias['descr'] . ')'; + } + + $list['other-subnet'] = 'Other Subnet (Enter Below)'; + + return($list); +} + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(new Form_Button( + 'Submit', + gettext("Save") +)); + +$section = new Form_Section('Edit Advanced Outbound NAT entry'); + +$section->addInput(new Form_Checkbox( + 'disabled', + 'Disabled', + 'Disable this rule', + $pconfig['disabled'] +)); + +$section->addInput(new Form_Checkbox( + 'nonat', + 'Do not NAT', + 'Enabling this option will disable NAT for traffic matching this rule and stop processing Outbound NAT rules', + $pconfig['nonat'] +))->setHelp('In most cases this option is not required'); + +$iflist = get_configured_interface_with_descr(false, true); + +foreach ($iflist as $if => $ifdesc) + if(have_ruleint_access($if)) + $interfaces[$if] = $ifdesc; + +if ($config['l2tp']['mode'] == "server") + if(have_ruleint_access("l2tp")) + $interfaces['l2tp'] = "L2TP VPN"; + +if ($config['pptpd']['mode'] == "server") + if(have_ruleint_access("pptp")) + $interfaces['pptp'] = "PPTP VPN"; + +if (is_pppoe_server_enabled() && have_ruleint_access("pppoe")) + $interfaces['pppoe'] = "PPPoE Server"; + +/* add ipsec interfaces */ +if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enable'])) + if(have_ruleint_access("enc0")) + $interfaces["enc0"] = "IPsec"; + +/* add openvpn/tun interfaces */ +if ($config['openvpn']["openvpn-server"] || $config['openvpn']["openvpn-client"]) + $interfaces["openvpn"] = "OpenVPN"; + +$section->addInput(new Form_Select( + 'interface', + 'Interface', + $pconfig['interface'], + $interfaces +))->setHelp('Choose which interface this rule applies to. In most cases "WAN" is specified.'); + +$protocols = "any TCP UDP TCP/UDP ICMP ESP AH GRE IPV6 IGMP carp pfsync"; + +$section->addInput(new Form_Select( + 'protocol', + 'Protocol', + $pconfig['protocol'], + array_combine(explode(" ", strtolower($protocols)), explode(" ", $protocols)) +))->setHelp('Choose which protocol this rule should match. In most cases "any" is specified.'); + +$group = new Form_Group('Source'); + +$group->add(new Form_Select( + 'source_type', + null, + $pconfig['source_type'], + array('any' => 'Any', '(self)' => 'This Firewall (self)', 'network' => 'Network') +))->setHelp('Type')->setWidth('3'); + +$group->add(new Form_IpAddress( + 'source', + null, + $pconfig['source'] +))->addMask('source_subnet', $pconfig['source_subnet'])->setHelp('Source network for the outbound NAT mapping.'); + +$group->add(new Form_Input( + 'sourceport', + null, + 'number', + $pconfig['sourceport'], + ['min' => '1', 'max' => '65536'] +))->setHelp('Port')->setWidth('2'); + +$section->add($group); + +$group = new Form_Group('Destination'); + +$group->add(new Form_Select( + 'destination_type', + null, + $pconfig['destination'] == "any" ? "any":"network", + array('any' => 'Any', 'network' => 'Network') +))->setHelp('Type')->setWidth('3'); + +$group->add(new Form_IpAddress( + 'destination', + null, + $pconfig['destination'] == "any" ? "":$pconfig['destination'] +))->addMask('destination_subnet', $pconfig['destination_subnet'])->setHelp('Destination network for the outbound NAT mapping.'); + +$group->add(new Form_Input( + 'dstport', + null, + 'number', + $pconfig['dstport'], + ['min' => '1', 'max' => '65536'] +))->setHelp('Port')->setWidth('2'); + +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'destination_not', + null, + 'Not', + $pconfig['destination_not'] +))->setHelp('Invert the sense of the destination match'); + +$form->add($section); + +$section = new Form_Section('Translation'); +$section->addClass('translation'); + +$section->addInput(new Form_Select( + 'target', + 'Address', + $pconfig['target'], + build_target_list() +)); + +$section->addInput(new Form_IpAddress( + 'targetip', + 'Other subnet', + $pconfig['targetip'] +))->addMask('targetip_subnet', $pconfig['targetip_subnet'])->addClass('othersubnet')->setHelp( + 'Packets matching this rule will be mapped to the IP address given here.' . '<br />' . + 'If you want this rule to apply to another IP address rather than the IP address of the interface chosen above, ' . + 'select it here (you will need to define ' . + '<a href="firewall_virtual_ip.php">' . gettext("Virtual IP") . '</a> ' . + 'addresses on the interface first)'); + +$section->addInput(new Form_Select( + 'poolopts', + 'Pool options', + $pconfig['poolopts'], + array( + '' => 'Default', + 'round-robin' => 'Round Robin', + 'round-robin sticky-address' => 'Round Robin with Sticky Address', + 'random' => 'Random', + 'random sticky-address' => 'Random with Sticky Address', + 'source-hash' => 'Source hash', + 'bitmask' => 'Bit mask' + ) +))->setHelp('Only Round Robin types work with Host Aliases. Any type can be used with a Subnet.' . '<br />' . + '<ul>' . + '<li>' . 'Round Robin: Loops through the translation addresses.' . '</li>' . '<br />' . + '<li>' . 'Random: Selects an address from the translation address pool at random.' . '</li>' . '<br />' . + '<li>' . 'Source Hash: Uses a hash of the source address to determine the translation address, ensuring that the redirection address is always the same for a given source.' . '</li>' . '<br />' . + '<li>' . 'Bitmask: Applies the subnet mask and keeps the last portion identical; 10.0.1.50 -> x.x.x.50.' . '</li>' . '<br />' . + '<li>' . 'Sticky Address: The Sticky Address option can be used with the Random and Round Robin pool types to ensure that a particular source address is always mapped to the same translation address.' . '</li>' . + '</ul>'); + +$group = new Form_Group('Port'); +$group->addClass('natportgrp'); + +$group->add(new Form_Input( + 'natport', + null, + 'number', + $pconfig['natport'], + ['min' => '1', 'max' => '65536'] +))->setHelp('Enter the source port for the outbound NAT mapping.'); + +$group->add(new Form_Checkbox( + 'staticnatport', + null, + 'Static port', + $pconfig['staticnatport'] +)); + +$section->add($group); +$form->add($section); + +$section = new Form_Section('Misc'); + +$section->addInput(new Form_Checkbox( + 'nosync', + 'No XMLRPC Sync', + null, + $pconfig['nosync'], + 'yes' +))->setHelp('Prevents the rule on Master from automatically syncing to other CARP members. ' . + 'This does NOT prevent the rule from being overwritten on Slave.'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +if (isset($id) && $a_out[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$section->addInput(new Form_Input( + 'after', + null, + 'hidden', + $after +)); + +$form->add($section); + +$has_created_time = (isset($a_out[$id]['created']) && is_array($a_out[$id]['created'])); +$has_updated_time = (isset($a_out[$id]['updated']) && is_array($a_out[$id]['updated'])); + +if ($has_created_time || $has_updated_time) { + $section = new Form_Section('Rule Information'); + + if($has_created_time) { + $section->addInput(new Form_StaticText( + 'Created', + date(gettext("n/j/y H:i:s"), $a_out[$id]['created']['time']) . gettext("by") . $a_out[$id]['created']['username'] + )); + } + + if($has_updated_time) { + $section->addInput(new Form_StaticText( + 'Updated', + date(gettext("n/j/y H:i:s"), $a_out[$id]['updated']['time']) . gettext("by") . $a_out[$id]['updated']['username'] + )); + } + + $form->add($section); +} + +print($form); + +?> + +<script type="text/javascript"> +//<![CDATA[ +events.push(function(){ + var portsenabled = 1; + + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified group input element lives so that the input, + // its label and help text are hidden + function hideGroupInput(id, hide) { + if(hide) + $('#' + id).parent('div').addClass('hidden'); + else + $('#' + id).parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified checkbox lives so that the checkbox, its label and help text are hidden + function hideCheckbox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // Hides all elements of the specified class. This will usually be a section + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // Hides all elements of the specified class assigned to a group. This will usually be a group + function hideGroupClass(s_class, hide) { + if(hide) + $('.' + s_class).parent().parent().parent().hide(); + else + $('.' + s_class).parent().parent().parent().show(); + } + + function staticportchange() { + if($('#staticnatport').prop('checked')) { + $('#natport').val(""); + disableInput('natport' , true); + } else { + disableInput('natport' , false); + } + } + + function sourcesel_change() { + if($('#source_type').find(":selected").val() == "network") { + disableInput('source', false); + disableInput('source_subnet', false); + } + else { + $('#source').val(""); + disableInput('source', true); + $('#source_subnet').val("24"); + disableInput('source_subnet', true); + } + } + + function typesel_change() { + if($('#destination_type').find(":selected").val() == "network") { + disableInput('destination', false); + disableInput('destination_subnet', false); + } + else { + $('#destination').val(""); + disableInput('destination', true); + $('#destination_subnet').val("24"); + disableInput('destination_subnet', true); + } + } + + function nonat_change() { + hideClass('translation', $('#nonat').prop('checked')); + } + + function proto_change() { + if( ($('#protocol').find(":selected").index() > 0) && ($('#protocol').find(":selected").index() <= 3) ) { + hideGroupInput('sourceport', false); + hideGroupInput('dstport', false); + hideClass('natportgrp', false); + } else { + hideGroupInput('sourceport', true); + hideGroupInput('dstport', true); + hideClass('natportgrp', true); + } + } + + function poolopts_change() { + if ($('#target option:selected').text().trim().substring(0,4) == "Host") { + hideInput('poolopts', false); + hideGroupClass('othersubnet', true); + } else if ($('#target option:selected').text().trim().substring(0,6) == "Subnet") { + hideInput('poolopts', false); + hideGroupClass('othersubnet', true); + } else if ($('#target option:selected').text().trim().substring(0,5) == "Other") { + hideInput('poolopts', false); + hideGroupClass('othersubnet', false); + } else { + $('#poolopts').prop('selectedIndex',0); + hideInput('poolopts', true); + hideGroupClass('othersubnet', true); + $('#targetip').val(''); + $('#targetip_subnet').val('0'); + } + } + + // When controls are clicked . . + $('#staticnatport').click(function () { + staticportchange(); + }); + + $('#source_type').on('change', function() { + sourcesel_change(); + }); + + $('#destination_type').on('change', function() { + typesel_change(); + }); + + $('#nonat').on('change', function() { + nonat_change(); + }); + + $('#protocol').on('change', function() { + proto_change(); + }); + + $('#target').on('change', function() { + poolopts_change(); + }); + + // Set initial states + staticportchange(); + sourcesel_change(); + typesel_change(); + nonat_change(); + proto_change(); + poolopts_change(); + +}); +//]]> +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_rules.php b/src/usr/local/www/firewall_rules.php new file mode 100644 index 0000000..cf5497b --- /dev/null +++ b/src/usr/local/www/firewall_rules.php @@ -0,0 +1,683 @@ +<?php +/* $Id$ */ +/* + firewall_rules.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004 Scott Ullrich + * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net> + * Originally part of pfSense (https://www.pfsense.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-firewall-rules +##|*NAME=Firewall: Rules page +##|*DESCR=Allow access to the 'Firewall: Rules' page. +##|*MATCH=firewall_rules.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +$pgtitle = array(gettext("Firewall"), gettext("Rules")); +$shortcut_section = "firewall"; + +function delete_nat_association($id) { + global $config; + + if (!$id || !is_array($config['nat']['rule'])) { + return; + } + + $a_nat = &$config['nat']['rule']; + + foreach ($a_nat as &$natent) { + if ($natent['associated-rule-id'] == $id) { + $natent['associated-rule-id'] = ''; + } + } +} + +if (!is_array($config['filter']['rule'])) { + $config['filter']['rule'] = array(); +} + +filter_rules_sort(); +$a_filter = &$config['filter']['rule']; + +$if = $_GET['if']; +if ($_POST['if']) { + $if = $_POST['if']; +} + +$ifdescs = get_configured_interface_with_descr(); + +/* add group interfaces */ +if (is_array($config['ifgroups']['ifgroupentry'])) { + foreach ($config['ifgroups']['ifgroupentry'] as $ifgen) { + if (have_ruleint_access($ifgen['ifname'])) { + $iflist[$ifgen['ifname']] = $ifgen['ifname']; + } + } +} + +foreach ($ifdescs as $ifent => $ifdesc) { + if (have_ruleint_access($ifent)) { + $iflist[$ifent] = $ifdesc; + } +} + +if ($config['l2tp']['mode'] == "server") { + if (have_ruleint_access("l2tp")) { + $iflist['l2tp'] = "L2TP VPN"; + } +} + +if ($config['pptpd']['mode'] == "server") { + if (have_ruleint_access("pptp")) { + $iflist['pptp'] = "PPTP VPN"; + } +} + +if (is_array($config['pppoes']['pppoe'])) { + foreach ($config['pppoes']['pppoe'] as $pppoes) { + if (($pppoes['mode'] == 'server') && have_ruleint_access("pppoe")) { + $iflist['pppoe'] = "PPPoE Server"; + } + } +} + +/* add ipsec interfaces */ +if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enable'])) { + if (have_ruleint_access("enc0")) { + $iflist["enc0"] = "IPsec"; + } +} + +/* add openvpn/tun interfaces */ +if ($config['openvpn']["openvpn-server"] || $config['openvpn']["openvpn-client"]) { + $iflist["openvpn"] = "OpenVPN"; +} + +if (!$if || !isset($iflist[$if])) { + if ("any" == $if) { + $if = "FloatingRules"; + } else if ("FloatingRules" != $if) { + if (isset($iflist['wan'])) { + $if = "wan"; + } else { + $if = "FloatingRules"; + } + } +} + +if ($_POST) { + $pconfig = $_POST; + + if ($_POST['apply']) { + $retval = 0; + $retval = filter_configure(); + + clear_subsystem_dirty('filter'); + + $savemsg = sprintf(gettext("The settings have been applied. The firewall rules are now reloading in the background.<br />You can also %s monitor %s the reload progress"),"<a href='status_filter_reload.php'>","</a>"); + } +} + +if ($_GET['act'] == "del") { + if ($a_filter[$_GET['id']]) { + if (!empty($a_filter[$_GET['id']]['associated-rule-id'])) { + delete_nat_association($a_filter[$_GET['id']]['associated-rule-id']); + } + unset($a_filter[$_GET['id']]); + if (write_config()) { + mark_subsystem_dirty('filter'); + } + header("Location: firewall_rules.php?if=" . htmlspecialchars($if)); + exit; + } +} + +// Handle save msg if defined +if ($_REQUEST['savemsg']) { + $savemsg = htmlentities($_REQUEST['savemsg']); +} + +if ($_GET['act'] == "toggle") { + if ($a_filter[$_GET['id']]) { + if (isset($a_filter[$_GET['id']]['disabled'])) { + unset($a_filter[$_GET['id']]['disabled']); + } else { + $a_filter[$_GET['id']]['disabled'] = true; + } + if (write_config()) { + mark_subsystem_dirty('filter'); + } + header("Location: firewall_rules.php?if=" . htmlspecialchars($if)); + exit; + } +} else { + /* update rule order, POST[rule] is an array of ordered IDs */ + if (is_array($_POST['rule']) && !empty($_POST['rule'])) { + $a_filter_new = array(); + + // if a rule is not in POST[rule], it has been deleted by the user + foreach ($_POST['rule'] as $id) + $a_filter_new[] = $a_filter[$id]; + + $a_filter = $a_filter_new; + if (write_config()) { + mark_subsystem_dirty('filter'); + } + header("Location: firewall_rules.php?if=" . htmlspecialchars($if)); + exit; + } +} + +include("head.inc"); +$nrules = 0; + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('filter')) + print_info_box_np(gettext("The firewall rule configuration has been changed.") . "<br />" . gettext("You must apply the changes in order for them to take effect."), "apply", "", true); + +$tab_array = array(array(gettext("Floating"), ("FloatingRules" == $if), "firewall_rules.php?if=FloatingRules")); + +foreach ($iflist as $ifent => $ifname) + $tab_array[] = array($ifname, ($ifent == $if), "firewall_rules.php?if={$ifent}"); + +display_top_tabs($tab_array); + +?> +<form method="post"> + <div class="panel panel-default"> + <div class="panel-heading"><?=gettext("Rules (Drag to change order)")?></div> + <div id="mainarea" class="table-responsive panel-body"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><!-- status icons --></th> + <th><?=gettext("Proto");?></th> + <th><?=gettext("Source");?></th> + <th><?=gettext("Port");?></th> + <th><?=gettext("Destination");?></th> + <th><?=gettext("Port");?></th> + <th><?=gettext("Gateway");?></th> + <th><?=gettext("Queue");?></th> + <th><?=gettext("Schedule");?></th> + <th><?=gettext("Description");?></th> + <th><!-- buttons --></th> + </tr> + </thead> + <tbody> +<?php + // Show the anti-lockout rule if it's enabled, and we are on LAN with an if count > 1, or WAN with an if count of 1. + if (!isset($config['system']['webgui']['noantilockout']) && + (((count($config['interfaces']) > 1) && ($if == 'lan')) + || ((count($config['interfaces']) == 1) && ($if == 'wan')))): + $alports = implode('<br />', filter_get_antilockout_ports(true)); +?> + <tr id="antilockout"> + <td title="<?=gettext("traffic is passed")?>"><i class="icon icon-ok"></i></td> + <td>*</td> + <td>*</td> + <td>*</td> + <td><?=$iflist[$if];?> Address</td> + <td><?=$alports?></td> + <td>*</td> + <td>*</td> + <td></td> + <td><?=gettext("Anti-Lockout Rule");?></td> + <td> + <a href="system_advanced_admin.php" class="btn btn-xs btn-primary">edit</a> + </td> + </tr> +<?php endif;?> +<?php if (isset($config['interfaces'][$if]['blockpriv'])): ?> + <tr id="frrfc1918"> + <td title="<?=gettext("traffic is blocked")?>"><i class="icon icon-remove"></i></td> + <td>*</td> + <td><?=gettext("RFC 1918 networks");?></td> + <td>*</td> + <td>*</td> + <td>*</td> + <td>*</td> + <td>*</td> + <td></td> + <td><?=gettext("Block private networks");?></td> + <td> + <a href="system_advanced_admin.php" class="btn btn-xs btn-primary" title="<?=gettext("edit rule");?>">edit</a> + </td> + </tr> +<?php endif;?> +<?php if (isset($config['interfaces'][$if]['blockbogons'])): ?> + <tr id="frrfc1918"> + <td title="<?=gettext("traffic is blocked")?>"><i class="icon icon-remove"></i></td> + <td>*</td> + <td><?=gettext("Reserved/not assigned by IANA");?></td> + <td>*</td> + <td>*</td> + <td>*</td> + <td>*</td> + <td>*</td> + <td>*</td> + <td><?=gettext("Block bogon networks");?></td> + <td> + <a href="system_advanced_admin.php" class="btn btn-xs btn-primary">edit</a> + </td> + </tr> +<?php endif;?> + </tbody> + + <tbody class="user-entries"> +<?php for ($i = 0; isset($a_filter[$i]); $i++): + $filterent = $a_filter[$i]; + + if ($filterent['interface'] != $if && !isset($filterent['floating'])) + continue; + if (isset($filterent['floating']) && "FloatingRules" != $if) + continue; + + $nrules++; +?> + <tr<?=(isset($filterent['disabled']) ? ' class="disabled"' : '')?>> + <td title="<?=gettext("traffic is ").$filterent['type']."ed"?>"> + <input type="hidden" name="rule[]" value="<?=$i?>" /> + + <?php + if ($filterent['type'] == "block") + $iconfn = "remove"; + else if ($filterent['type'] == "reject") + $iconfn = "fire"; + else if ($filterent['type'] == "match") + $iconfn = "filter"; + else + $iconfn = "ok"; + ?> + <i class="icon icon-<?=$iconfn?>"></i> + <?php + $isadvset = firewall_check_for_advanced_options($filterent); + if ($isadvset) + print '<i class="icon icon-cog" title="'. gettext("advanced setting") .': '. $isadvset .'"></i>'; + + if (isset($filterent['log'])) + print '<i class="icon icon-tasks" title="'. gettext("traffic is logged") .'"></i>'; + ?> + </td> + <?php + $alias = rule_columns_with_alias( + $filterent['source']['address'], + pprint_port($filterent['source']['port']), + $filterent['destination']['address'], + pprint_port($filterent['destination']['port']) + ); + + //build Schedule popup box + $a_schedules = &$config['schedules']['schedule']; + $schedule_span_begin = ""; + $schedule_span_end = ""; + $sched_caption_escaped = ""; + $sched_content = ""; + $schedstatus = false; + $dayArray = array (gettext('Mon'),gettext('Tues'),gettext('Wed'),gettext('Thur'),gettext('Fri'),gettext('Sat'),gettext('Sun')); + $monthArray = array (gettext('January'),gettext('February'),gettext('March'),gettext('April'),gettext('May'),gettext('June'),gettext('July'),gettext('August'),gettext('September'),gettext('October'),gettext('November'),gettext('December')); + if ($config['schedules']['schedule'] != "" && is_array($config['schedules']['schedule'])) { + foreach ($a_schedules as $schedule) + { + if ($schedule['name'] == $filterent['sched'] ){ + $schedstatus = filter_get_time_based_rule_status($schedule); + + foreach($schedule['timerange'] as $timerange) { + $tempFriendlyTime = ""; + $tempID = ""; + $firstprint = false; + if ($timerange){ + $dayFriendly = ""; + $tempFriendlyTime = ""; + + //get hours + $temptimerange = $timerange['hour']; + $temptimeseparator = strrpos($temptimerange, "-"); + + $starttime = substr ($temptimerange, 0, $temptimeseparator); + $stoptime = substr ($temptimerange, $temptimeseparator+1); + + if ($timerange['month']){ + $tempmontharray = explode(",", $timerange['month']); + $tempdayarray = explode(",",$timerange['day']); + $arraycounter = 0; + $firstDayFound = false; + $firstPrint = false; + foreach ($tempmontharray as $monthtmp){ + $month = $tempmontharray[$arraycounter]; + $day = $tempdayarray[$arraycounter]; + + if (!$firstDayFound) + { + $firstDay = $day; + $firstmonth = $month; + $firstDayFound = true; + } + + $currentDay = $day; + $nextDay = $tempdayarray[$arraycounter+1]; + $currentDay++; + if (($currentDay != $nextDay) || ($tempmontharray[$arraycounter] != $tempmontharray[$arraycounter+1])){ + if ($firstPrint) + $dayFriendly .= ", "; + $currentDay--; + if ($currentDay != $firstDay) + $dayFriendly .= $monthArray[$firstmonth-1] . " " . $firstDay . " - " . $currentDay ; + else + $dayFriendly .= $monthArray[$month-1] . " " . $day; + $firstDayFound = false; + $firstPrint = true; + } + $arraycounter++; + } + } + else + { + $tempdayFriendly = $timerange['position']; + $firstDayFound = false; + $tempFriendlyDayArray = explode(",", $tempdayFriendly); + $currentDay = ""; + $firstDay = ""; + $nextDay = ""; + $counter = 0; + foreach ($tempFriendlyDayArray as $day){ + if ($day != ""){ + if (!$firstDayFound) + { + $firstDay = $tempFriendlyDayArray[$counter]; + $firstDayFound = true; + } + $currentDay =$tempFriendlyDayArray[$counter]; + //get next day + $nextDay = $tempFriendlyDayArray[$counter+1]; + $currentDay++; + if ($currentDay != $nextDay){ + if ($firstprint) + $dayFriendly .= ", "; + $currentDay--; + if ($currentDay != $firstDay) + $dayFriendly .= $dayArray[$firstDay-1] . " - " . $dayArray[$currentDay-1]; + else + $dayFriendly .= $dayArray[$firstDay-1]; + $firstDayFound = false; + $firstprint = true; + } + $counter++; + } + } + } + $timeFriendly = $starttime . " - " . $stoptime; + $description = $timerange['rangedescr']; + $sched_content .= $dayFriendly . "; " . $timeFriendly . "<br />"; + } + } + #FIXME + $sched_caption_escaped = str_replace("'", "\'", $schedule['descr']); + $schedule_span_begin = "<span style=\"cursor: help;\" onmouseover=\"domTT_activate(this, event, 'content', '<h1>{$sched_caption_escaped}</h1><p>{$sched_content}</p>', 'trail', true, 'delay', 0, 'fade', 'both', 'fadeMax', 93, 'styleClass', 'niceTitle');\" onmouseout=\"this.style.color = ''; domTT_mouseout(this, event);\"><u>"; + $schedule_span_end = "</u></span>"; + } + } + } + $printicon = false; + $alttext = ""; + $image = ""; + if (!isset($filterent['disabled'])) { + if ($schedstatus) { + if ($iconfn == "block" || $iconfn == "reject") { + $image = "icon_block"; + $alttext = gettext("Traffic matching this rule is currently being denied"); + } else { + $image = "icon_pass"; + $alttext = gettext("Traffic matching this rule is currently being allowed"); + } + $printicon = true; + } else if ($filterent['sched']) { + if ($iconfn == "block" || $iconfn == "reject") + $image = "icon_block_d"; + else + $image = "icon_block"; + $alttext = gettext("This rule is not currently active because its period has expired"); + $printicon = true; + } + } + ?> + <td> + <?php + if (isset($filterent['ipprotocol'])) { + switch($filterent['ipprotocol']) { + case "inet": + echo "IPv4 "; + break; + case "inet6": + echo "IPv6 "; + break; + case "inet46": + echo "IPv4+6 "; + break; + } + } else { + echo "IPv4 "; + } + + if (isset($filterent['protocol'])) { + echo strtoupper($filterent['protocol']); + + if (strtoupper($filterent['protocol']) == "ICMP" && !empty($filterent['icmptype'])) { + echo ' <span style="cursor: help;" title="ICMP type: ' . + ( $filterent['ipprotocol'] == "inet6" ? $icmp6types[$filterent['icmptype']] : $icmptypes[$filterent['icmptype']] ) . + '"><u>'; + echo $filterent['icmptype']; + echo '</u></span>'; + } + } else echo "*"; + ?> + </td> + <td> + <?php if (isset($alias['src'])): ?> + <a href="/firewall_aliases_edit.php?id=<?=$alias['src']?>" data-toggle="popover" data-trigger="hover focus" title="Alias details" data-content="<?=alias_info_popup($alias['src'])?>" data-html="true"> + <?php endif; ?> + <?=htmlspecialchars(pprint_address($filterent['source']))?> + <?php if (isset($alias['src'])): ?> + <i class='icon icon-pencil'></i></a> + <?php endif; ?> + </td> + <td> + <?php if (isset($alias['srcport'])): ?> + <a href="/firewall_aliases_edit.php?id=<?=$alias['srcport']?>" data-toggle="popover" data-trigger="hover focus" title="Alias details" data-content="<?=alias_info_popup($alias['srcport'])?>" data-html="true"> + <?php endif; ?> + <?=htmlspecialchars(pprint_port($filterent['source']['port']))?> + <?php if (isset($alias['srcport'])): ?> + <i class='icon icon-pencil'></i></a> + <?php endif; ?> + </td> + <td> + <?php if (isset($alias['dst'])): ?> + <a href="/firewall_aliases_edit.php?id=<?=$alias['dst']?>" data-toggle="popover" data-trigger="hover focus" title="Alias details" data-content="<?=alias_info_popup($alias['dstport'])?>" data-html="true"> + <?php endif; ?> + <?=htmlspecialchars(pprint_address($filterent['destination']))?> + <?php if (isset($alias['dst'])): ?> + <i class='icon icon-pencil'></i></a> + <?php endif; ?> + </td> + <td> + <?php if (isset($alias['dstport'])): ?> + <a href="/firewall_aliases_edit.php?id=<?=$alias['dstport']?>" data-toggle="popover" data-trigger="hover focus" title="Alias details" data-content="<?=alias_info_popup($alias['dstport'])?>" data-html="true"> + <?php endif; ?> + <?=htmlspecialchars(pprint_port($filterent['destination']['port']))?> + <?php if (isset($alias['dstport'])): ?> + <i class='icon icon-pencil'></i></a> + <?php endif; ?> + </td> + <td> + <?php if (isset($config['interfaces'][$filterent['gateway']]['descr'])):?> + <?=htmlspecialchars($config['interfaces'][$filterent['gateway']]['descr'])?> + <?php else: ?> + <?=htmlspecialchars(pprint_port($filterent['gateway']))?> + <?php endif; ?> + </td> + <td> + <?php + if (isset($filterent['ackqueue']) && isset($filterent['defaultqueue'])) { + $desc = $filterent['ackqueue'] ; + echo "<a href=\"firewall_shaper_queues.php?queue={$filterent['ackqueue']}&action=show\">{$desc}</a>"; + $desc = $filterent['defaultqueue']; + echo "/<a href=\"firewall_shaper_queues.php?queue={$filterent['defaultqueue']}&action=show\">{$desc}</a>"; + } else if (isset($filterent['defaultqueue'])) { + $desc = $filterent['defaultqueue']; + echo "<a href=\"firewall_shaper_queues.php?queue={$filterent['defaultqueue']}&action=show\">{$desc}</a>"; + } else + echo gettext("none"); + ?> + </td> + <td> + <?php if ($printicon) { ?><img src="./themes/<?= $g['theme'];?>/images/icons/<?=$image;?>.gif" title="<?=$alttext;?>" border="0" alt="icon" /><?php } ?> + <?=$schedule_span_begin;?><?=htmlspecialchars($filterent['sched']);?> <?=$schedule_span_end;?> + </td> + <td> + <?=htmlspecialchars($filterent['descr']);?> + </td> + <td> + <a href="firewall_rules_edit.php?id=<?=$i;?>" class="btn btn-xs btn-primary">edit</a> + <a href="firewall_rules_edit.php?dup=<?=$i;?>" class="btn btn-xs btn-default">copy</a> + <a href="?act=toggle&if=<?=htmlspecialchars($if);?>&id=<?=$i;?>" class="btn btn-xs btn-warning"><?=(isset($filterent['disabled']) ? 'enable' : 'disable')?></a> + <a href="?act=del&if=<?=htmlspecialchars($if);?>&id=<?=$i;?>" class="btn btn-xs btn-danger">delete</a> + </td> + </tr> +<?php + endfor; +?> + </tbody> + </table> + </div> + </div> + +<?php if ($nrules == 0): ?> + <div class="alert alert-warning" role="alert"> + <p> + <?php if ($_REQUEST['if'] == "FloatingRules"): ?> + <?=gettext("No floating rules are currently defined.");?> + <?php else: ?> + <?=gettext("No rules are currently defined for this interface");?><br /> + <?=gettext("All incoming connections on this interface will be blocked until you add pass rules.");?> + <?php endif;?> + <?=gettext("Click the button to add a new rule.");?> + </p> + </div> +<?php endif;?> + + <nav class="action-buttons"> + <input type="submit" id="order-store" class="btn btn-sm btn-primary" value="store changes" disabled="disabled" /> + <a href="firewall_rules_edit.php?if=<?=htmlspecialchars($if);?>" role="button" class="btn btn-sm btn-success"> + <?=gettext("add new");?> + </a> + </nav> +</form> +<!-- Legend --> +<div> + <dl class="dl-horizontal responsive"> + <dt><?=gettext('Legend')?></dt> <dd></dd> + <dt><i class="icon icon-ok"></i></dt> <dd><?=gettext("pass");?></dd> + <dt><i class="icon icon-filter"></i></dt> <dd><?=gettext("match");?></dd> + <dt><i class="icon icon-remove"></i></dt> <dd><?=gettext("block");?></dd> + <dt><i class="icon icon-fire"></i></dt> <dd><?=gettext("reject");?></dd> + <dt><i class="icon icon-tasks"></i></dt> <dd> <?=gettext("log");?></dd> + <dt><i class="icon icon-cog"></i></dt> <dd> <?=gettext("advanced filter");?></dd> + </dl> +</div> + +<?php +if ("FloatingRules" != $if) + print_info_box(gettext("Rules are evaluated on a first-match basis (i.e. " . + "the action of the first rule to match a packet will be executed). ") . '<br />' . + gettext("This means that if you use block rules, you'll have to pay attention " . + "to the rule order. Everything that isn't explicitly passed is blocked " . + "by default. ")); +else + print_info_box(gettext("Floating rules are evaluated on a first-match basis (i.e. " . + "the action of the first rule to match a packet will be executed) only " . + "if the 'quick' option is checked on a rule. Otherwise they will only apply if no " . + "other rules match. Pay close attention to the rule order and options " . + "chosen. If no rule here matches, the per-interface or default rules are used. ")); + +?> + +<script> +events.push(function() { + // Make rules sortable + $('table tbody.user-entries').sortable({ + cursor: 'grabbing', + update: function(event, ui) { + $('#order-store').removeAttr('disabled'); + } + }); + + // Replace direct delete with quicker front-end action +/*FIXME: event ordering + $('.btn-danger').on('click', function(e){ + $(this).parents('tr').remove(); + + $('#order-store').removeAttr('disabled'); + }); +*/}); +</script> +<?php include("foot.inc");?>
\ No newline at end of file diff --git a/src/usr/local/www/firewall_rules_edit.php b/src/usr/local/www/firewall_rules_edit.php new file mode 100644 index 0000000..1f6c1d1 --- /dev/null +++ b/src/usr/local/www/firewall_rules_edit.php @@ -0,0 +1,1949 @@ +<?php +/* $Id$ */ +/* + firewall_rules_edit.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004 Scott Ullrich + * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net> + * Originally part of pfSense (https://www.pfsense.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-firewall-rules-edit +##|*NAME=Firewall: Rules: Edit page +##|*DESCR=Allow access to the 'Firewall: Rules: Edit' page. +##|*MATCH=firewall_rules_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("filter.inc"); +require("shaper.inc"); + +if (isset($_POST['referer'])) { + $referer = $_POST['referer']; +} else { + $referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/firewall_rules.php'); +} + +function is_posnumericint($arg) { + // Note that to be safe we do not allow any leading zero - "01", "007" + return (is_numericint($arg) && $arg[0] != '0' && $arg > 0); +} + +function is_aoadv_used($rule_config) { + // Note that the user could set "tag" or "tagged" to the string "0", which is valid but empty(). + // And if the user enters "0" in other fields, we want to present an error message, and keep the Advanced Options section open. + if ((isset($rule_config['allowopts'])) || + (isset($rule_config['disablereplyto'])) || + ($rule_config['tag'] != "") || + ($rule_config['tagged'] != "") || + ($rule_config['max'] != "") || + ($rule_config['max-src-nodes'] != "") || + ($rule_config['max-src-conn'] != "") || + ($rule_config['max-src-states'] != "") || + ($rule_config['max-src-conn-rate'] != "") || + ($rule_config['max-src-conn-rates'] != "") || + ($rule_config['statetimeout'] != "")) { + return true; + } + return false; +} + +$ostypes = array(); +exec('/sbin/pfctl -s osfp | /usr/bin/tr \'\t\' \' \'', $ostypes); + +if (count($ostypes) > 2) { + // Remove header rows from pfctl output + array_shift($ostypes); + array_shift($ostypes); +} else { + // Fall back to a default list + $ostypes = array( + "AIX", + "Linux", + "FreeBSD", + "NetBSD", + "OpenBSD", + "Solaris", + "MacOS", + "Windows", + "Novell", + "NMAP" + ); +} + +$specialsrcdst = explode(" ", "any (self) pptp pppoe l2tp openvpn"); +$ifdisp = get_configured_interface_with_descr(); +foreach ($ifdisp as $kif => $kdescr) { + $specialsrcdst[] = "{$kif}"; + $specialsrcdst[] = "{$kif}ip"; +} + +if (!is_array($config['filter']['rule'])) { + $config['filter']['rule'] = array(); +} +filter_rules_sort(); +$a_filter = &$config['filter']['rule']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (is_numericint($_GET['after']) || $_GET['after'] == "-1") { + $after = $_GET['after']; +} +if (isset($_POST['after']) && (is_numericint($_POST['after']) || $_POST['after'] == "-1")) { + $after = $_POST['after']; +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + $id = $_GET['dup']; + $after = $_GET['dup']; +} + +if (isset($id) && $a_filter[$id]) { + $pconfig['interface'] = $a_filter[$id]['interface']; + + if (isset($a_filter[$id]['id'])) { + $pconfig['ruleid'] = $a_filter[$id]['id']; + } + + if (isset($a_filter[$id]['created']) && is_array($a_filter[$id]['created'])) { + $pconfig['created'] = $a_filter[$id]['created']; + } + + if (isset($a_filter[$id]['updated']) && is_array($a_filter[$id]['updated'])) { + $pconfig['updated'] = $a_filter[$id]['updated']; + } + + if (!isset($a_filter[$id]['type'])) { + $pconfig['type'] = "pass"; + } else { + $pconfig['type'] = $a_filter[$id]['type']; + } + + if (isset($a_filter[$id]['floating']) || $if == "FloatingRules") { + $pconfig['floating'] = $a_filter[$id]['floating']; + if (isset($a_filter[$id]['interface']) && $a_filter[$id]['interface'] <> "") { + $pconfig['interface'] = $a_filter[$id]['interface']; + } + } + + if (isset($a_filter['floating'])) { + $pconfig['floating'] = "yes"; + } + + if (isset($a_filter[$id]['direction'])) { + $pconfig['direction'] = $a_filter[$id]['direction']; + } + + if (isset($a_filter[$id]['ipprotocol'])) { + $pconfig['ipprotocol'] = $a_filter[$id]['ipprotocol']; + } + + if (isset($a_filter[$id]['protocol'])) { + $pconfig['proto'] = $a_filter[$id]['protocol']; + } else { + $pconfig['proto'] = "any"; + } + + if ($a_filter[$id]['protocol'] == "icmp") { + $pconfig['icmptype'] = $a_filter[$id]['icmptype']; + } + + address_to_pconfig($a_filter[$id]['source'], $pconfig['src'], + $pconfig['srcmask'], $pconfig['srcnot'], + $pconfig['srcbeginport'], $pconfig['srcendport']); + + if ($a_filter[$id]['os'] != "") { + $pconfig['os'] = $a_filter[$id]['os']; + } + + address_to_pconfig($a_filter[$id]['destination'], $pconfig['dst'], + $pconfig['dstmask'], $pconfig['dstnot'], + $pconfig['dstbeginport'], $pconfig['dstendport']); + + if ($a_filter[$id]['dscp'] <> "") { + $pconfig['dscp'] = $a_filter[$id]['dscp']; + } + + $pconfig['disabled'] = isset($a_filter[$id]['disabled']); + $pconfig['log'] = isset($a_filter[$id]['log']); + $pconfig['descr'] = $a_filter[$id]['descr']; + + if (isset($a_filter[$id]['tcpflags_any'])) { + $pconfig['tcpflags_any'] = true; + } else { + if (isset($a_filter[$id]['tcpflags1']) && $a_filter[$id]['tcpflags1'] <> "") { + $pconfig['tcpflags1'] = $a_filter[$id]['tcpflags1']; + } + if (isset($a_filter[$id]['tcpflags2']) && $a_filter[$id]['tcpflags2'] <> "") { + $pconfig['tcpflags2'] = $a_filter[$id]['tcpflags2']; + } + } + + if (isset($a_filter[$id]['tag']) && $a_filter[$id]['tag'] <> "") { + $pconfig['tag'] = $a_filter[$id]['tag']; + } + if (isset($a_filter[$id]['tagged']) && $a_filter[$id]['tagged'] <> "") { + $pconfig['tagged'] = $a_filter[$id]['tagged']; + } + if (isset($a_filter[$id]['quick']) && $a_filter[$id]['quick']) { + $pconfig['quick'] = $a_filter[$id]['quick']; + } + if (isset($a_filter[$id]['allowopts'])) { + $pconfig['allowopts'] = true; + } + if (isset($a_filter[$id]['disablereplyto'])) { + $pconfig['disablereplyto'] = true; + } + + /* advanced */ + $pconfig['max'] = $a_filter[$id]['max']; + $pconfig['max-src-nodes'] = $a_filter[$id]['max-src-nodes']; + $pconfig['max-src-conn'] = $a_filter[$id]['max-src-conn']; + $pconfig['max-src-states'] = $a_filter[$id]['max-src-states']; + $pconfig['statetype'] = $a_filter[$id]['statetype']; + $pconfig['statetimeout'] = $a_filter[$id]['statetimeout']; + $pconfig['nopfsync'] = isset($a_filter[$id]['nopfsync']); + + /* advanced - nosync */ + $pconfig['nosync'] = isset($a_filter[$id]['nosync']); + + /* advanced - new connection per second banning*/ + $pconfig['max-src-conn-rate'] = $a_filter[$id]['max-src-conn-rate']; + $pconfig['max-src-conn-rates'] = $a_filter[$id]['max-src-conn-rates']; + + /* Multi-WAN next-hop support */ + $pconfig['gateway'] = $a_filter[$id]['gateway']; + + /* Shaper support */ + $pconfig['defaultqueue'] = (($a_filter[$id]['ackqueue'] == "none") ? '' : $a_filter[$id]['defaultqueue']); + $pconfig['ackqueue'] = (($a_filter[$id]['ackqueue'] == "none") ? '' : $a_filter[$id]['ackqueue']); + $pconfig['dnpipe'] = (($a_filter[$id]['dnpipe'] == "none") ? '' : $a_filter[$id]['dnpipe']); + $pconfig['pdnpipe'] = (($a_filter[$id]['pdnpipe'] == "none") ? '' : $a_filter[$id]['pdnpipe']); + $pconfig['l7container'] = (($a_filter[$id]['l7container'] == "none") ? '' : $a_filter[$id]['l7container']); + + //schedule support + $pconfig['sched'] = (($a_filter[$id]['sched'] == "none") ? '' : $a_filter[$id]['sched']); + $pconfig['vlanprio'] = (($a_filter[$id]['vlanprio'] == "none") ? '' : $a_filter[$id]['vlanprio']); + $pconfig['vlanprioset'] = (($a_filter[$id]['vlanprioset'] == "none") ? '' : $a_filter[$id]['vlanprioset']); + if (!isset($_GET['dup']) || !is_numericint($_GET['dup'])) { + $pconfig['associated-rule-id'] = $a_filter[$id]['associated-rule-id']; + } + + $pconfig['tracker'] = $a_filter[$id]['tracker']; + +} else { + /* defaults */ + if ($_GET['if']) { + $pconfig['interface'] = $_GET['if']; + } + $pconfig['type'] = "pass"; + $pconfig['src'] = "any"; + $pconfig['dst'] = "any"; +} +/* Allow the FloatingRules to work */ +$if = $pconfig['interface']; + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + unset($id); +} + +read_altq_config(); /* XXX: */ +$qlist =& get_unique_queue_list(); +read_dummynet_config(); /* XXX: */ +$dnqlist =& get_unique_dnqueue_list(); +read_layer7_config(); +$l7clist =& get_l7_unique_list(); +$a_gatewaygroups = return_gateway_groups_array(); + +if ($_POST) { + unset($input_errors); + + if (isset($a_filter[$id]['associated-rule-id'])) { + $_POST['proto'] = $pconfig['proto']; + if ($pconfig['proto'] == "icmp") { + $_POST['icmptype'] = $pconfig['icmptype']; + } + } + + if (($_POST['ipprotocol'] <> "") && ($_POST['gateway'] <> "")) { + if (is_array($config['gateways']['gateway_group'])) { + foreach ($config['gateways']['gateway_group'] as $gw_group) { + if ($gw_group['name'] == $_POST['gateway']) { + $family = $a_gatewaygroups[$_POST['gateway']]['ipprotocol']; + if ($_POST['ipprotocol'] == $family) { + continue; + } + if (($_POST['ipprotocol'] == "inet46") && ($_POST['ipprotocol'] != $family)) { + $input_errors[] = gettext("You can not assign a gateway to a rule that applies to IPv4 and IPv6"); + } + if (($_POST['ipprotocol'] == "inet6") && ($_POST['ipprotocol'] != $family)) { + $input_errors[] = gettext("You can not assign an IPv4 gateway group on IPv6 Address Family rule"); + } + if (($_POST['ipprotocol'] == "inet") && ($_POST['ipprotocol'] != $family)) { + $input_errors[] = gettext("You can not assign an IPv6 gateway group on IPv4 Address Family rule"); + } + } + } + } + } + if (($_POST['ipprotocol'] <> "") && ($_POST['gateway'] <> "") && (is_ipaddr(lookup_gateway_ip_by_name($_POST['gateway'])))) { + if (($_POST['ipprotocol'] == "inet46") && ($_POST['gateway'] <> "")) { + $input_errors[] = gettext("You can not assign a gateway to a rule that applies to IPv4 and IPv6"); + } + if (($_POST['ipprotocol'] == "inet6") && (!is_ipaddrv6(lookup_gateway_ip_by_name($_POST['gateway'])))) { + $input_errors[] = gettext("You can not assign an IPv4 Gateway to an IPv6 Filter rule"); + } + if (($_POST['ipprotocol'] == "inet") && (!is_ipaddrv4(lookup_gateway_ip_by_name($_POST['gateway'])))) { + $input_errors[] = gettext("You can not assign an IPv6 Gateway to an IPv4 Filter rule"); + } + } + if (($_POST['proto'] == "icmp") && ($_POST['icmptype'] <> "")) { + if ($_POST['ipprotocol'] == "inet46") { + $input_errors[] = gettext("You can not assign a ICMP type to a rule that applies to IPv4 and IPv6"); + } + } + + if (($_POST['proto'] != "tcp") && ($_POST['proto'] != "udp") && ($_POST['proto'] != "tcp/udp")) { + $_POST['srcbeginport'] = 0; + $_POST['srcendport'] = 0; + $_POST['dstbeginport'] = 0; + $_POST['dstendport'] = 0; + } else { + if ($_POST['srcbeginport_cust'] && !$_POST['srcbeginport']) { + $_POST['srcbeginport'] = trim($_POST['srcbeginport_cust']); + } + if ($_POST['srcendport_cust'] && !$_POST['srcendport']) { + $_POST['srcendport'] = trim($_POST['srcendport_cust']); + } + if ($_POST['srcbeginport'] == "any") { + $_POST['srcbeginport'] = 0; + $_POST['srcendport'] = 0; + } else { + if (!$_POST['srcendport']) { + $_POST['srcendport'] = $_POST['srcbeginport']; + } + } + if ($_POST['srcendport'] == "any") { + $_POST['srcendport'] = $_POST['srcbeginport']; + } + + if ($_POST['dstbeginport_cust'] && !$_POST['dstbeginport']) { + $_POST['dstbeginport'] = trim($_POST['dstbeginport_cust']); + } + if ($_POST['dstendport_cust'] && !$_POST['dstendport']) { + $_POST['dstendport'] = trim($_POST['dstendport_cust']); + } + + if ($_POST['dstbeginport'] == "any") { + $_POST['dstbeginport'] = 0; + $_POST['dstendport'] = 0; + } else { + if (!$_POST['dstendport']) { + $_POST['dstendport'] = $_POST['dstbeginport']; + } + } + if ($_POST['dstendport'] == "any") { + $_POST['dstendport'] = $_POST['dstbeginport']; + } + } + + if (is_specialnet($_POST['srctype'])) { + $_POST['src'] = $_POST['srctype']; + $_POST['srcmask'] = 0; + } else if ($_POST['srctype'] == "single") { + if (is_ipaddrv6($_POST['src'])) { + $_POST['srcmask'] = 128; + } else { + $_POST['srcmask'] = 32; + } + } + if (is_specialnet($_POST['dsttype'])) { + $_POST['dst'] = $_POST['dsttype']; + $_POST['dstmask'] = 0; + } else if ($_POST['dsttype'] == "single") { + if (is_ipaddrv6($_POST['dst'])) { + $_POST['dstmask'] = 128; + } else { + $_POST['dstmask'] = 32; + } + } + + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "type proto"); + if (isset($a_filter[$id]['associated-rule-id']) === false) { + $reqdfields[] = "src"; + $reqdfields[] = "dst"; + } + $reqdfieldsn = explode(",", "Type,Protocol"); + if (isset($a_filter[$id]['associated-rule-id']) === false) { + $reqdfieldsn[] = "Source"; + $reqdfieldsn[] = "Destination"; + } + + if ($_POST['statetype'] == "modulate state" or $_POST['statetype'] == "synproxy state") { + if ($_POST['proto'] != "tcp") { + $input_errors[] = sprintf(gettext("%s is only valid with protocol TCP."), $_POST['statetype']); + } + if (($_POST['statetype'] == "synproxy state") && ($_POST['gateway'] != "")) { + $input_errors[] = sprintf(gettext("%s is only valid if the gateway is set to 'default'."), $_POST['statetype']); + } + } + + if (isset($a_filter[$id]['associated-rule-id']) === false && + (!(is_specialnet($_POST['srctype']) || ($_POST['srctype'] == "single")))) { + $reqdfields[] = "srcmask"; + $reqdfieldsn[] = "Source bit count"; + } + if (isset($a_filter[$id]['associated-rule-id']) === false && + (!(is_specialnet($_POST['dsttype']) || ($_POST['dsttype'] == "single")))) { + $reqdfields[] = "dstmask"; + $reqdfieldsn[] = gettext("Destination bit count"); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (!$_POST['srcbeginport']) { + $_POST['srcbeginport'] = 0; + $_POST['srcendport'] = 0; + } + if (!$_POST['dstbeginport']) { + $_POST['dstbeginport'] = 0; + $_POST['dstendport'] = 0; + } + + if ($_POST['srcbeginport'] && !is_portoralias($_POST['srcbeginport'])) { + $input_errors[] = sprintf(gettext("%s is not a valid start source port. It must be a port alias or integer between 1 and 65535."), $_POST['srcbeginposrt']); + } + if ($_POST['srcendport'] && !is_portoralias($_POST['srcendport'])) { + $input_errors[] = sprintf(gettext("%s is not a valid end source port. It must be a port alias or integer between 1 and 65535."), $_POST['srcendport']); + } + if ($_POST['dstbeginport'] && !is_portoralias($_POST['dstbeginport'])) { + $input_errors[] = sprintf(gettext("%s is not a valid start destination port. It must be a port alias or integer between 1 and 65535."), $_POST['dstbeginport']); + } + if ($_POST['dstendport'] && !is_portoralias($_POST['dstendport'])) { + $input_errors[] = sprintf(gettext("%s is not a valid end destination port. It must be a port alias or integer between 1 and 65535."), $_POST['dstendport']); + } + if (!$_POST['srcbeginport_cust'] && $_POST['srcendport_cust']) { + if (is_alias($_POST['srcendport_cust'])) { + $input_errors[] = 'If you put port alias in Source port range to: field you must put the same port alias in from: field'; + } + } + if ($_POST['srcbeginport_cust'] && $_POST['srcendport_cust']) { + if (is_alias($_POST['srcendport_cust']) && is_alias($_POST['srcendport_cust']) && $_POST['srcbeginport_cust'] != $_POST['srcendport_cust']) { + $input_errors[] = 'The same port alias must be used in Source port range from: and to: fields'; + } + if ((is_alias($_POST['srcbeginport_cust']) && (!is_alias($_POST['srcendport_cust']) && $_POST['srcendport_cust'] != '')) || + ((!is_alias($_POST['srcbeginport_cust']) && $_POST['srcbeginport_cust'] != '') && is_alias($_POST['srcendport_cust']))) { + $input_errors[] = 'You cannot specify numbers and port aliases at the same time in Source port range from: and to: field'; + } + } + if (!$_POST['dstbeginport_cust'] && $_POST['dstendport_cust']) { + if (is_alias($_POST['dstendport_cust'])) { + $input_errors[] = 'If you put port alias in Destination port range to: field you must put the same port alias in from: field'; + } + } + if ($_POST['dstbeginport_cust'] && $_POST['dstendport_cust']) { + if (is_alias($_POST['dstendport_cust']) && is_alias($_POST['dstendport_cust']) && $_POST['dstbeginport_cust'] != $_POST['dstendport_cust']) { + $input_errors[] = 'The same port alias must be used in Destination port range from: and to: fields'; + } + if ((is_alias($_POST['dstbeginport_cust']) && (!is_alias($_POST['dstendport_cust']) && $_POST['dstendport_cust'] != '')) || + ((!is_alias($_POST['dstbeginport_cust']) && $_POST['dstbeginport_cust'] != '') && is_alias($_POST['dstendport_cust']))) { + $input_errors[] = 'You cannot specify numbers and port aliases at the same time in Destination port range from: and to: field'; + } + } + + if ($_POST['src']) { + $_POST['src'] = trim($_POST['src']); + } + if ($_POST['dst']) { + $_POST['dst'] = trim($_POST['dst']); + } + + /* if user enters an alias and selects "network" then disallow. */ + if ($_POST['srctype'] == "network") { + if (is_alias($_POST['src'])) { + $input_errors[] = gettext("You must specify single host or alias for alias entries."); + } + } + if ($_POST['dsttype'] == "network") { + if (is_alias($_POST['dst'])) { + $input_errors[] = gettext("You must specify single host or alias for alias entries."); + } + } + + if (!is_specialnet($_POST['srctype'])) { + if (($_POST['src'] && !is_ipaddroralias($_POST['src']))) { + $input_errors[] = sprintf(gettext("%s is not a valid source IP address or alias."), $_POST['src']); + } + if (($_POST['srcmask'] && !is_numericint($_POST['srcmask']))) { + $input_errors[] = gettext("A valid source bit count must be specified."); + } + } + if (!is_specialnet($_POST['dsttype'])) { + if (($_POST['dst'] && !is_ipaddroralias($_POST['dst']))) { + $input_errors[] = sprintf(gettext("%s is not a valid destination IP address or alias."), $_POST['dst']); + } + if (($_POST['dstmask'] && !is_numericint($_POST['dstmask']))) { + $input_errors[] = gettext("A valid destination bit count must be specified."); + } + } + if ((is_ipaddr($_POST['src']) && is_ipaddr($_POST['dst']))) { + if (!validate_address_family($_POST['src'], $_POST['dst'])) { + $input_errors[] = sprintf(gettext("The Source IP address %s Address Family differs from the destination %s."), $_POST['src'], $_POST['dst']); + } + if ((is_ipaddrv6($_POST['src']) || is_ipaddrv6($_POST['dst'])) && ($_POST['ipprotocol'] == "inet")) { + $input_errors[] = gettext("You can not use IPv6 addresses in IPv4 rules."); + } + if ((is_ipaddrv4($_POST['src']) || is_ipaddrv4($_POST['dst'])) && ($_POST['ipprotocol'] == "inet6")) { + $input_errors[] = gettext("You can not use IPv4 addresses in IPv6 rules."); + } + } + + if ((is_ipaddr($_POST['src']) || is_ipaddr($_POST['dst'])) && ($_POST['ipprotocol'] == "inet46")) { + $input_errors[] = gettext("You can not use a IPv4 or IPv6 address in combined IPv4 + IPv6 rules."); + } + + if ($_POST['srcbeginport'] > $_POST['srcendport']) { + /* swap */ + $tmp = $_POST['srcendport']; + $_POST['srcendport'] = $_POST['srcbeginport']; + $_POST['srcbeginport'] = $tmp; + } + if ($_POST['dstbeginport'] > $_POST['dstendport']) { + /* swap */ + $tmp = $_POST['dstendport']; + $_POST['dstendport'] = $_POST['dstbeginport']; + $_POST['dstbeginport'] = $tmp; + } + if ($_POST['os']) { + if ($_POST['proto'] != "tcp") { + $input_errors[] = gettext("OS detection is only valid with protocol TCP."); + } + if (!in_array($_POST['os'], $ostypes)) { + $input_errors[] = gettext("Invalid OS detection selection. Please select a valid OS."); + } + } + + if ($_POST['ackqueue'] != "") { + if ($_POST['defaultqueue'] == "") { + $input_errors[] = gettext("You have to select a queue when you select an acknowledge queue too."); + } else if ($_POST['ackqueue'] == $_POST['defaultqueue']) { + $input_errors[] = gettext("Acknowledge queue and Queue cannot be the same."); + } + } + if (isset($_POST['floating']) && $_POST['pdnpipe'] != "" && (empty($_POST['direction']) || $_POST['direction'] == "any")) { + $input_errors[] = gettext("You can not use limiters in Floating rules without choosing a direction."); + } + if (isset($_POST['floating']) && $_POST['gateway'] != "" && (empty($_POST['direction']) || $_POST['direction'] == "any")) { + $input_errors[] = gettext("You can not use gateways in Floating rules without choosing a direction."); + } + if ($_POST['pdnpipe'] && $_POST['pdnpipe'] != "") { + if ($_POST['dnpipe'] == "") { + $input_errors[] = gettext("You must select a queue for the In direction before selecting one for Out too."); + } else if ($_POST['pdnpipe'] == $_POST['dnpipe']) { + $input_errors[] = gettext("In and Out Queue cannot be the same."); + } else if ($dnqlist[$_POST['pdnpipe']][0] == "?" && $dnqlist[$_POST['dnpipe']][0] <> "?") { + $input_errors[] = gettext("You cannot select one queue and one virtual interface for IN and Out. Both must be from the same type."); + } else if ($dnqlist[$_POST['dnpipe']][0] == "?" && $dnqlist[$_POST['pdnpipe']][0] <> "?") { + $input_errors[] = gettext("You cannot select one queue and one virtual interface for IN and Out. Both must be from the same type."); + } + if ($_POST['direction'] == "out" && empty($_POST['gateway'])) { + $input_errors[] = gettext("Please select a gateway, normally the interface selected gateway, so the limiters work correctly"); + } + } + if (!empty($_POST['ruleid']) && !ctype_digit($_POST['ruleid'])) { + $input_errors[] = gettext('ID must be an integer'); + } + if ($_POST['l7container'] && $_POST['l7container'] != "") { + if (!($_POST['proto'] == "tcp" || $_POST['proto'] == "udp" || $_POST['proto'] == "tcp/udp")) { + $input_errors[] = gettext("You can only select a layer7 container for TCP and/or UDP protocols"); + } + if ($_POST['type'] <> "pass") { + $input_errors[] = gettext("You can only select a layer7 container for Pass type rules."); + } + } + + if (!in_array($_POST['proto'], array("tcp", "tcp/udp"))) { + if (!empty($_POST['max-src-conn'])) { + $input_errors[] = gettext("You can only specify the maximum number of established connections per host (advanced option) for TCP protocol."); + } + if (!empty($_POST['max-src-conn-rate']) || !empty($_POST['max-src-conn-rates'])) { + $input_errors[] = gettext("You can only specify the maximum new connections per host / per second(s) (advanced option) for TCP protocol."); + } + if (!empty($_POST['statetimeout'])) { + $input_errors[] = gettext("You can only specify the state timeout (advanced option) for TCP protocol."); + } + } + + if ($_POST['type'] <> "pass") { + if (!empty($_POST['max'])) { + $input_errors[] = gettext("You can only specify the maximum state entries (advanced option) for Pass type rules."); + } + if (!empty($_POST['max-src-nodes'])) { + $input_errors[] = gettext("You can only specify the maximum number of unique source hosts (advanced option) for Pass type rules."); + } + if (!empty($_POST['max-src-conn'])) { + $input_errors[] = gettext("You can only specify the maximum number of established connections per host (advanced option) for Pass type rules."); + } + if (!empty($_POST['max-src-states'])) { + $input_errors[] = gettext("You can only specify the maximum state entries per host (advanced option) for Pass type rules."); + } + if (!empty($_POST['max-src-conn-rate']) || !empty($_POST['max-src-conn-rates'])) { + $input_errors[] = gettext("You can only specify the maximum new connections per host / per second(s) (advanced option) for Pass type rules."); + } + if (!empty($_POST['statetimeout'])) { + $input_errors[] = gettext("You can only specify the state timeout (advanced option) for Pass type rules."); + } + } + + if (($_POST['statetype'] == "none") && (empty($_POST['l7container']))) { + if (!empty($_POST['max'])) { + $input_errors[] = gettext("You cannot specify the maximum state entries (advanced option) if statetype is none and no L7 container is selected."); + } + if (!empty($_POST['max-src-nodes'])) { + $input_errors[] = gettext("You cannot specify the maximum number of unique source hosts (advanced option) if statetype is none and no L7 container is selected."); + } + if (!empty($_POST['max-src-conn'])) { + $input_errors[] = gettext("You cannot specify the maximum number of established connections per host (advanced option) if statetype is none and no L7 container is selected."); + } + if (!empty($_POST['max-src-states'])) { + $input_errors[] = gettext("You cannot specify the maximum state entries per host (advanced option) if statetype is none and no L7 container is selected."); + } + if (!empty($_POST['max-src-conn-rate']) || !empty($_POST['max-src-conn-rates'])) { + $input_errors[] = gettext("You cannot specify the maximum new connections per host / per second(s) (advanced option) if statetype is none and no L7 container is selected."); + } + if (!empty($_POST['statetimeout'])) { + $input_errors[] = gettext("You cannot specify the state timeout (advanced option) if statetype is none and no L7 container is selected."); + } + } + + if (($_POST['max'] != "") && !is_posnumericint($_POST['max'])) { + $input_errors[] = gettext("Maximum state entries (advanced option) must be a positive integer"); + } + + if (($_POST['max-src-nodes'] != "") && !is_posnumericint($_POST['max-src-nodes'])) { + $input_errors[] = gettext("Maximum number of unique source hosts (advanced option) must be a positive integer"); + } + + if (($_POST['max-src-conn'] != "") && !is_posnumericint($_POST['max-src-conn'])) { + $input_errors[] = gettext("Maximum number of established connections per host (advanced option) must be a positive integer"); + } + + if (($_POST['max-src-states'] != "") && !is_posnumericint($_POST['max-src-states'])) { + $input_errors[] = gettext("Maximum state entries per host (advanced option) must be a positive integer"); + } + + if (($_POST['max-src-conn-rate'] != "") && !is_posnumericint($_POST['max-src-conn-rate'])) { + $input_errors[] = gettext("Maximum new connections per host / per second(s) (advanced option) must be a positive integer"); + } + + if (($_POST['statetimeout'] != "") && !is_posnumericint($_POST['statetimeout'])) { + $input_errors[] = gettext("State timeout (advanced option) must be a positive integer"); + } + + if ((($_POST['max-src-conn-rate'] <> "" and $_POST['max-src-conn-rates'] == "")) || + (($_POST['max-src-conn-rate'] == "" and $_POST['max-src-conn-rates'] <> ""))) { + $input_errors[] = gettext("Both maximum new connections per host and the interval (per second(s)) must be specified"); + } + + if (!$_POST['tcpflags_any']) { + $settcpflags = array(); + $outoftcpflags = array(); + foreach ($tcpflags as $tcpflag) { + if ($_POST['tcpflags1_' . $tcpflag] == "on") { + $settcpflags[] = $tcpflag; + } + if ($_POST['tcpflags2_' . $tcpflag] == "on") { + $outoftcpflags[] = $tcpflag; + } + } + if (empty($outoftcpflags) && !empty($settcpflags)) { + $input_errors[] = gettext("If you specify TCP flags that should be set you should specify out of which flags as well."); + } + } + + // Allow extending of the firewall edit page and include custom input validation + pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/input_validation"); + + if (!$input_errors) { + $filterent = array(); + $filterent['id'] = $_POST['ruleid']>0?$_POST['ruleid']:''; + + $filterent['tracker'] = empty($_POST['tracker']) ? (int)microtime(true) : $_POST['tracker']; + + $filterent['type'] = $_POST['type']; + if (isset($_POST['interface'])) { + $filterent['interface'] = $_POST['interface']; + } + + if (isset($_POST['ipprotocol'])) { + $filterent['ipprotocol'] = $_POST['ipprotocol']; + } + + if ($_POST['tcpflags_any']) { + $filterent['tcpflags_any'] = true; + } else { + $settcpflags = array(); + $outoftcpflags = array(); + foreach ($tcpflags as $tcpflag) { + if ($_POST['tcpflags1_' . $tcpflag] == "on") { + $settcpflags[] = $tcpflag; + } + if ($_POST['tcpflags2_' . $tcpflag] == "on") { + $outoftcpflags[] = $tcpflag; + } + } + if (!empty($outoftcpflags)) { + $filterent['tcpflags2'] = join(",", $outoftcpflags); + if (!empty($settcpflags)) { + $filterent['tcpflags1'] = join(",", $settcpflags); + } + } + } + + if (isset($_POST['tag'])) { + $filterent['tag'] = $_POST['tag']; + } + if (isset($_POST['tagged'])) { + $filterent['tagged'] = $_POST['tagged']; + } + if ($if == "FloatingRules" || isset($_POST['floating'])) { + $filterent['direction'] = $_POST['direction']; + if (isset($_POST['quick']) && $_POST['quick'] <> "") { + $filterent['quick'] = $_POST['quick']; + } + $filterent['floating'] = "yes"; + if (isset($_POST['interface']) && count($_POST['interface']) > 0) { + $filterent['interface'] = implode(",", $_POST['interface']); + } + } + + /* Advanced options */ + if ($_POST['allowopts'] == "yes") { + $filterent['allowopts'] = true; + } else { + unset($filterent['allowopts']); + } + if ($_POST['disablereplyto'] == "yes") { + $filterent['disablereplyto'] = true; + } else { + unset($filterent['disablereplyto']); + } + $filterent['max'] = $_POST['max']; + $filterent['max-src-nodes'] = $_POST['max-src-nodes']; + $filterent['max-src-conn'] = $_POST['max-src-conn']; + $filterent['max-src-states'] = $_POST['max-src-states']; + $filterent['statetimeout'] = $_POST['statetimeout']; + $filterent['statetype'] = $_POST['statetype']; + $filterent['os'] = $_POST['os']; + if ($_POST['nopfsync'] <> "") { + $filterent['nopfsync'] = true; + } else { + unset($filterent['nopfsync']); + } + + /* Nosync directive - do not xmlrpc sync this item */ + if ($_POST['nosync'] <> "") { + $filterent['nosync'] = true; + } else { + unset($filterent['nosync']); + } + + /* unless both values are provided, unset the values - ticket #650 */ + if ($_POST['max-src-conn-rate'] <> "" and $_POST['max-src-conn-rates'] <> "") { + $filterent['max-src-conn-rate'] = $_POST['max-src-conn-rate']; + $filterent['max-src-conn-rates'] = $_POST['max-src-conn-rates']; + } else { + unset($filterent['max-src-conn-rate']); + unset($filterent['max-src-conn-rates']); + } + + if ($_POST['proto'] != "any") { + $filterent['protocol'] = $_POST['proto']; + } else { + unset($filterent['protocol']); + } + + if ($_POST['proto'] == "icmp") { + if ($filterent['ipprotocol'] == 'inet6' && $_POST['icmp6type']) { + $filterent['icmptype'] = $_POST['icmp6type']; + } else if ($filterent['ipprotocol'] != 'inet6' && $_POST['icmptype']) { + $filterent['icmptype'] = $_POST['icmptype']; + } else { + unset($filterent['icmptype']); + } + } else { + unset($filterent['icmptype']); + } + + pconfig_to_address($filterent['source'], $_POST['src'], + $_POST['srcmask'], $_POST['srcnot'], + $_POST['srcbeginport'], $_POST['srcendport']); + + pconfig_to_address($filterent['destination'], $_POST['dst'], + $_POST['dstmask'], $_POST['dstnot'], + $_POST['dstbeginport'], $_POST['dstendport']); + + if ($_POST['disabled']) { + $filterent['disabled'] = true; + } else { + unset($filterent['disabled']); + } + + if ($_POST['dscp']) { + $filterent['dscp'] = $_POST['dscp']; + } + + if ($_POST['log']) { + $filterent['log'] = true; + } else { + unset($filterent['log']); + } + strncpy($filterent['descr'], $_POST['descr'], 52); + + if ($_POST['gateway'] != "") { + $filterent['gateway'] = $_POST['gateway']; + } + + if ($_POST['defaultqueue'] != "") { + $filterent['defaultqueue'] = $_POST['defaultqueue']; + if ($_POST['ackqueue'] != "") { + $filterent['ackqueue'] = $_POST['ackqueue']; + } + } + + if ($_POST['dnpipe'] != "") { + $filterent['dnpipe'] = $_POST['dnpipe']; + if ($_POST['pdnpipe'] != "") { + $filterent['pdnpipe'] = $_POST['pdnpipe']; + } + } + + if ($_POST['l7container'] != "") { + $filterent['l7container'] = $_POST['l7container']; + } + + if ($_POST['sched'] != "") { + $filterent['sched'] = $_POST['sched']; + } + + if ($_POST['vlanprio'] != "") { + $filterent['vlanprio'] = $_POST['vlanprio']; + } + if ($_POST['vlanprioset'] != "") { + $filterent['vlanprioset'] = $_POST['vlanprioset']; + } + + // If we have an associated nat rule, make sure the source and destination doesn't change + if (isset($a_filter[$id]['associated-rule-id'])) { + $filterent['interface'] = $a_filter[$id]['interface']; + if (isset($a_filter[$id]['protocol'])) { + $filterent['protocol'] = $a_filter[$id]['protocol']; + } else if (isset($filterent['protocol'])) { + unset($filterent['protocol']); + } + if ($a_filter[$id]['protocol'] == "icmp" && $a_filter[$id]['icmptype']) { + $filterent['icmptype'] = $a_filter[$id]['icmptype']; + } else if (isset($filterent['icmptype'])) { + unset($filterent['icmptype']); + } + + $filterent['source'] = $a_filter[$id]['source']; + $filterent['destination'] = $a_filter[$id]['destination']; + $filterent['associated-rule-id'] = $a_filter[$id]['associated-rule-id']; + } + + if (isset($a_filter[$id]['created']) && is_array($a_filter[$id]['created'])) { + $filterent['created'] = $a_filter[$id]['created']; + } + + $filterent['updated'] = make_config_revision_entry(); + + // Allow extending of the firewall edit page and include custom input validation + pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/pre_write_config"); + + if (isset($id) && $a_filter[$id]) { + $a_filter[$id] = $filterent; + } else { + $filterent['created'] = make_config_revision_entry(); + if (is_numeric($after)) { + array_splice($a_filter, $after+1, 0, array($filterent)); + } else { + $a_filter[] = $filterent; + } + } + + filter_rules_sort(); + + if (write_config()) { + mark_subsystem_dirty('filter'); + } + + if (isset($_POST['floating'])) { + header("Location: firewall_rules.php?if=FloatingRules"); + } else { + header("Location: firewall_rules.php?if=" . htmlspecialchars($_POST['interface'])); + } + exit; + } +} + +function build_flag_table() { + global $pconfig, $tcpflags; + + $flagtable = '<table class="table table-condensed table-flags" style="width: auto;">'; + + $setflags = explode(",", $pconfig['tcpflags1']); + $outofflags = explode(",", $pconfig['tcpflags2']); + $header = "<td></td>"; + $tcpflags1 = "<td>set</td>"; + $tcpflags2 = "<td>out of</td>"; + + foreach ($tcpflags as $tcpflag) { + $header .= "<td><strong>" . strtoupper($tcpflag) . "</strong></td>\n"; + $tcpflags1 .= "<td> <input type='checkbox' name='tcpflags1_{$tcpflag}' value='on' "; + + if (array_search($tcpflag, $setflags) !== false) { + $tcpflags1 .= "checked=\"checked\""; + } + + $tcpflags1 .= " /></td>\n"; + $tcpflags2 .= "<td> <input type='checkbox' name='tcpflags2_{$tcpflag}' value='on' "; + + if (array_search($tcpflag, $outofflags) !== false) { + $tcpflags2 .= "checked=\"checked\""; + } + + $tcpflags2 .= " /></td>\n"; + } + + $flagtable .= "<tr id='tcpheader'>{$header}</tr>\n"; + $flagtable .= "<tr id='tcpflags1'>{$tcpflags1}</tr>\n"; + $flagtable .= "<tr id='tcpflags2'>{$tcpflags2}</tr>\n"; + $flagtable .= "</table>"; + + $flagtable .= '<input type="checkbox" name="tcpflags_any" id="tcpflags_any" value="on"'; + $flagtable .= $pconfig['tcpflags_any'] ? 'checked="checked"':'' . '/>'; + $flagtable .= '<strong>' . gettext(" Any flags.") . '</strong>'; + + return($flagtable); +} + +$pgtitle = array(gettext("Firewall"), gettext("Rules"), gettext("Edit")); +$shortcut_section = "firewall"; + +$closehead = false; + +$page_filename = "firewall_rules_edit.php"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); +$form = new Form; +$section = new Form_Section('Edit Firewall rule'); + +if (isset($id)) +{ + $form->addGlobal(new Form_Input( + 'id', + 'ID', + 'hidden', + $id + )); +} + +if (isset($a_filter[$id])) +{ + $form->addGlobal(new Form_Input( + 'tracker', + 'Tracker', + 'hidden', + $pconfig['tracker'] + )); +} + +$form->addGlobal(new Form_Input( + 'after', + 'After', + 'hidden', + $after +)); + +$form->addGlobal(new Form_Input( + 'ruleid', + 'Ruleid', + 'hidden', + $pconfig['ruleid'] +)); + +// Allow extending of the firewall edit page and include custom input validation +pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/htmlphpearly"); + +$values = array( + 'pass' => 'Pass', + 'block' => 'Block', + 'reject' => 'Reject', +); + +if ($if == "FloatingRules" || isset($pconfig['floating'])) + $values['match'] = 'Match'; + +$section->addInput(new Form_Select( + 'type', + 'Action', + $pconfig['type'], + $values +))->setHelp('Choose what to do with packets that match the criteria specified '. + 'below.<br/>Hint: the difference between block and reject is that with '. + 'reject, a packet (TCP RST or ICMP port unreachable for UDP) is returned '. + 'to the sender, whereas with block the packet is dropped silently. In '. + 'either case, the original packet is discarded.'); + +$section->addInput(new Form_Checkbox( + 'disabled', + 'Disabled', + 'Disable this rule', + $pconfig['disabled'] +))->setHelp('Set this option to disable this rule without removing it from the '. + 'list.'); + +if ($if == "FloatingRules" || isset($pconfig['floating'])) +{ + $section->addInput(new Form_Checkbox( + 'floating', + 'Quick', + 'Apply the action immediately on match.', + $pconfig['quick'] + ))->setHelp('Set this option if you need to apply this action to traffic that '. + 'matches this rule immediately.'); +} + +$edit_disabled = isset($pconfig['associated-rule-id']); + +if ($edit_disabled) +{ + $extra = ''; + foreach( $config['nat']['rule'] as $index => $nat_rule ) + { + if ($nat_rule['associated-rule-id'] === $pconfig['associated-rule-id'] ) + $extra = '<br/><a href="firewall_nat_edit.php?id='. $index .'">'. gettext('View the NAT rule') .'</a>'; + } + + $section->add(new Form_Group( + 'Associated filter rule' + ))->setHelp('Note: This is associated to a NAT rule.<br/>You cannot edit '. + 'the interface, protocol, source, or destination of associated filter '. + 'rules.'. $extra); + + $section->addGlobal(new Form_Input( + 'associated-rule-id', + 'Associated Rule ID', + 'hidden', + $pconfig['associated-rule-id'] + )); + + if (!empty($pconfig['interface'])) + { + $section->addInput(new Form_Input( + 'interface', + 'Interface', + 'hidden', + $pconfig['interface'] + )); + } +} + +$interfaces = array(); + +/* add group interfaces */ +if (is_array($config['ifgroups']['ifgroupentry'])) + foreach ($config['ifgroups']['ifgroupentry'] as $ifgen) + if (have_ruleint_access($ifgen['ifname'])) + $interfaces[$ifgen['ifname']] = $ifgen['ifname']; + +foreach (get_configured_interface_with_descr() as $ifent => $ifdesc) +{ + if (have_ruleint_access($ifent)) + $interfaces[$ifent] = $ifdesc; +} + +if ($config['l2tp']['mode'] == "server" && have_ruleint_access("l2tp")) + $interfaces['l2tp'] = 'L2TP VPN'; + +if ($config['pptpd']['mode'] == "server" && have_ruleint_access("pptp")) + $interfaces['pptp'] = 'PPTP VPN'; + +if (is_pppoe_server_enabled() && have_ruleint_access("pppoe")) + $interfaces['pppoe'] = "PPPoE Server"; + +/* add ipsec interfaces */ +if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enable']) && have_ruleint_access("enc0")) + $interfaces["enc0"] = "IPsec"; + +/* add openvpn/tun interfaces */ +if ($config['openvpn']["openvpn-server"] || $config['openvpn']["openvpn-client"]) + $interfaces["openvpn"] = "OpenVPN"; + +$section->addInput($input = new Form_Select( + 'interface', + 'Interface', + $pconfig['interface'], + $interfaces, + ($if == "FloatingRules" || isset($pconfig['floating'])) +))->setHelp('Choose on which interface packets must come in to match this '. + 'rule.'); + +if ($if == "FloatingRules" || isset($pconfig['floating'])) + $input->setHelp('Choose the interface(s) for this rule.'); + +if ($if == "FloatingRules" || isset($pconfig['floating'])) +{ + $section->addInput(new Form_Select( + 'direction', + 'Direction', + $pconfig['direction'], + array( + 'any' => 'any', + 'in' => 'in', + 'out' => 'out', + ) + )); + + $section->addInput(new Form_Input( + 'floating', + 'Floating', + 'hidden', + 'floating' + )); +} + +$section->addInput(new Form_Select( + 'ipprotocol', + 'TCP/IP Version', + $pconfig['ipprotocol'], + array( + 'inet' => 'IPv4', + 'inet6' => 'IPv6', + 'inet46' => 'IPv4+IPv6', + ) +))->setHelp('Select the Internet Protocol version this rule applies to'); + +$section->addInput(new Form_Select( + 'proto', + 'Protocol', + $pconfig['ipprotocol'], + array( + 'tcp' => 'TCP', + 'udp' => 'UDP', + 'tcp/udp' => 'TCP/UDP', + 'icmp' => 'ICMP', + 'esp' => 'ESP', + 'ah' => 'AH', + 'gre' => 'GRE', + 'ipv6' => 'IPV6', + 'igmp' => 'IGMP', + 'pim' => 'PIM', + 'ospf' => 'OSPF', + 'sctp' => 'SCTP', + 'any' => 'any', + 'carp' => 'CARP', + 'pfsync' => 'PFSYNC', + ) +))->setHelp('Choose which IP protocol this rule should match. In most cases, you should specify TCP here.'); + +$section->addInput(new Form_Select( + 'icmptype', + 'ICMP type', + $pconfig['icmptype'], + $icmptypes +))->setHelp('If you selected ICMP for the protocol above, you may specify an ICMP type here.'); + +$section->addInput(new Form_Select( + 'icmp6type', + 'ICMPv6 type', + $pconfig['icmptype'], + $icmp6types +))->setHelp('If you selected ICMP for the protocol above, you may specify an ICMP type here.'); + +$form->add($section); + +// Source and destination share a lot of logic. Loop over the two +foreach (['src' => 'Source', 'dst' => 'Destination'] as $type => $name) { + $section = new Form_Section($name); + + $group = new Form_Group($name); + $group->add(new Form_Checkbox( + $type .'not', + $name .' not', + 'Invert match.', + $pconfig[$type.'not'] + ))->setWidth(2); + + $ruleType = $pconfig[$type]; + if (is_specialnet($pconfig[$type])) + $ruleType = 'network'; + elseif ((is_ipaddrv6($pconfig[$type]) && $pconfig[$type.'mask'] == 128) || + (is_ipaddrv4($pconfig[$type]) && $pconfig[$type.'mask'] == 32) || + (is_alias($pconfig[$type]))) + $ruleType = 'single'; + + $ruleValues = array( + 'any' => 'any', + 'single' => 'Single host or alias', + 'network' => 'Network', + ); + if (isset($a_filter[$id]['floating']) || $if == "FloatingRules") + $ruleValues['(self)'] = 'This Firewall (self)'; + if (have_ruleint_access("pptp")) + $ruleValues['pptp'] = 'PPTP clients'; + if (have_ruleint_access("pppoe")) + $ruleValues['pppoe'] = 'PPPoE clients'; + if (have_ruleint_access("l2tp")) + $ruleValues['l2tp'] = 'L2TP clients'; + + foreach ($ifdisp as $ifent => $ifdesc) + { + if (!have_ruleint_access($ifent)) + continue; + + $ruleValues[$ifent] = $ifdesc.' net'; + $ruleValues[$ifent.'ip'] = $ifdesc.' address'; + } + + $group->add(new Form_Select( + $type . 'type', + $name .' Type', + $type == 'src' ? $pconfig['src']:$pconfig['dst'], + $ruleValues + )); + + $group->add(new Form_IpAddress( + $type, + $name .' Address', + $pconfig[$type] + ))->addMask($type .'mask', $pconfig[$type.'mask']); + + $section->add($group); + + if($type == 'src') { + $section->addInput(new Form_Button( + 'btnsrcadv', + 'Show advanced' + ))->removeClass('btn-primary'); + } + + $portValues = ['any' => 'any', '' => '(other)']; + + foreach ($wkports as $port => $portName) + $portValues[$port] = $portName.' ('. $port .')'; + + $group = new Form_Group($name .' port range'); + $group->addClass('srcportrange'); + + $group->add(new Form_Select( + $type .'beginport', + $name .' port begin', + $pconfig[$type .'beginport'], + $portValues + ))->setHelp('From'); + + $group->add(new Form_Input( + $type .'beginport_cust', + null,//$name .' port begin custom', + 'number', + (isset($portValues[ $pconfig[$type .'beginport'] ]) ? null : $pconfig[$type .'beginport']), + ['min' => 1, 'max' => 65535] + ))->setHelp('Custom');; + + $group->add(new Form_Select( + $type .'endport', + $name .' port end', + $pconfig[$type .'endport'], + $portValues + ))->setHelp('To'); + + $group->add(new Form_Input( + $type .'endport_cust', + null,//$name .' port end custom', + 'number', + (isset($portValues[ $pconfig[$type .'endport'] ]) ? null : $pconfig[$type .'endport']), + ['min' => 1, 'max' => 65535] + ))->setHelp('Custom'); + + + if ($type == 'src') + $group->setHelp('Specify the source port or port range for this rule. This is '. + 'usually random and almost never equal to the destination port range (and '. + 'should usually be <b>any.</b> You can leave the "To" field '. + 'empty if you only want to filter a single port.'); + else + $group->setHelp('Specify the destination port or port range for this rule. ' . + 'You can leave the "To" field empty if you only want to filter a '. + 'single port.'); + + $group->addClass( ($type == 'src') ? 'srcprtr':'dstprtr'); + $section->add($group); + $form->add($section); +} + +$section = new Form_Section('Extra options'); +$section->addInput(new Form_Checkbox( + 'log', + 'Log', + 'Log packets that are handled by this rule', + $pconfig['log'] +))->setHelp('Hint: the firewall has limited local log space. Don\'t turn on logging '. + 'for everything. If you want to do a lot of logging, consider using a remote '. + 'syslog server (see the <a href="diag_logs_settings.php">Diagnostics: System logs: '. + 'Settings</a> page).'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference.'); + +$btnadvanced = new Form_Button( + 'toggle-advanced', + 'Advanced options' +); + +$btnadvanced->removeClass('btn-primary')->addClass('btn-info'); + +$section->addInput(new Form_StaticText( + null, + $btnadvanced +)); + +$form->add($section); +$section = new Form_Section('Advanced options'); +$section->addClass('advanced-options'); + +$section->addInput(new Form_Select( + 'os', + 'Source OS', + (empty($pconfig['os']) ? '':$pconfig['os']), + ['' => 'Any'] + array_combine($ostypes, $ostypes) +))->setHelp('Note: this only works for TCP rules. General OS choice matches all subtypes.'); + +$section->addInput(new Form_Select( + 'dscp', + 'Diffserv Code Point', + $pconfig['dscp'], + ["" => ''] + array_combine($firewall_rules_dscp_types, $firewall_rules_dscp_types) +)); + +$section->addInput(new Form_Checkbox( + 'allowopts', + 'Allow IP options', + 'Allow packets with IP options to pass. Otherwise they are blocked by '. + 'default. This is usually only seen with multicast traffic.', + $pconfig['allowopts'] +)); + +$section->addInput(new Form_Checkbox( + 'disablereplyto', + 'Disable reply-to', + 'Disable auto generated reply-to for this rule.', + $pconfig['disablereplyto'] +)); + +$section->addInput(new Form_Input( + 'tag', + 'Tag', + 'text', + $pconfig['tag'] +))->setHelp('You can mark a packet matching this rule and use this mark to match '. + 'on other NAT/filter rules. It is called <b>Policy filtering</b>.'); + +$section->addInput(new Form_Input( + 'tagged', + 'Tagged', + 'text', + $pconfig['tagged'] +))->setHelp('You can match packet on a mark placed before on another rule.'); + +$section->addInput(new Form_Input( + 'max', + 'Max. states', + 'number', + $pconfig['max'] +))->setHelp('Maximum state entries this rule can create.'); + +$section->addInput(new Form_Input( + 'max-src-nodes', + 'Max. src nodes', + 'number', + $pconfig['max-src-nodes'] +))->setHelp('Maximum number of unique source hosts.'); + +$section->addInput(new Form_Input( + 'max-src-conn', + 'Max. connections', + 'number', + $pconfig['max-src-conn'] +))->setHelp('Maximum number of established connections per host (TCP only).'); + +$section->addInput(new Form_Input( + 'max-src-states', + 'Max. src. states', + 'number', + $pconfig['max-src-states'] +))->setHelp('Maximum state entries per host.'); + +$section->addInput(new Form_Input( + 'max-src-conn-rate', + 'Max. src. conn. Rate', + 'number', + $pconfig['max-src-conn-rate'] +))->setHelp('Maximum state entries per host'); + +$section->addInput(new Form_Input( + 'max-src-conn-rates', + 'Max. src. conn. Rates', + 'number', + $pconfig['max-src-conn-rates'], + ['min' => 1, 'max' => 255] +))->setHelp('Maximum new connections per host / per second(s) (TCP only)'); + +$section->addInput(new Form_Input( + 'statetimeout', + 'State timeout', + 'number', + $pconfig['statetimeout'], + ['min' => 1, 'max' => 3600] +))->setHelp('State Timeout in seconds (TCP only)'); + +//$form->add($section); +//$section = new Form_Section('TCP Flags'); +//$section->addClass('tcpflags'); + +$section->addInput(new Form_StaticText( + 'TCP Flags', + build_flag_table() +))->setHelp('Use this to choose TCP flags that must be set or cleared for this rule to match.'); + +// $form->add($section); +// $section = new Form_Section('State Type'); + +$section->addInput(new Form_Checkbox( + 'nopfsync', + 'No pfSync', + 'Prevent states created by this rule to be sync\'ed over pfsync.', + $pconfig['nopfsync'] +)); + +$section->addInput(new Form_Select( + 'statetype', + 'State type', + $pconfig['statetype'], + array( + 'keep state' => 'Keep: works with all IP protocols', + 'sloppy state' => 'Sloppy: works with all IP protocols', + 'synproxy state' => 'Synproxy: proxies incoming TCP connections to help protect servers from spoofed TCP SYN floods. This option includes the functionality of keep state and modulate state combined', + 'none' => 'None: Do not use state mechanisms to keep track. This is only useful if you\'re doing advanced queueing in certain situations', + ) +))->setHelp('Select which type of state tracking mechanism you would like to use. If in doubt, use keep state.'); + +$section->addInput(new Form_Checkbox( + 'nosync', + 'No XMLRPC Sync', + 'Prevent the rule on Master from automatically syncing to other CARP members', + $pconfig['nosync'] +))->setHelp('This does NOT prevent the rule from being overwritten on Slave.'); + +$vlanprio = array("none", "be", "bk", "ee", "ca", "vi", "vo", "ic", "nc"); +$section->addInput(new Form_Select( + 'vlanprio', + 'VLAN Prio', + $pconfig['vlanprio'], + $vlanprio +))->setHelp('Choose 802.1p priority to match on'); + +$section->addInput(new Form_Select( + 'vlanprioset', + 'VLAN Prio Set', + $pconfig['vlanprioset'], + $vlanprio +))->setHelp('Choose 802.1p priority to apply'); + +$schedules = array('none'); //leave none to leave rule enabled all the time +foreach ((array)$config['schedules']['schedule'] as $schedule) +{ + if ($schedule['name'] != "") + $schedules[] = $schedule['name']; +} + +$section->addInput(new Form_Select( + 'sched', + 'Schedule', + $pconfig['sched'], + $schedules +))->setHelp('Leave as \'none\' to leave the rule enabled all the time'); + +$gateways = array("" => 'default'); +foreach (return_gateways_array() as $gwname => $gw) +{ + if (($pconfig['ipprotocol'] == "inet46")) + continue; + if (($pconfig['ipprotocol'] == "inet6") && !(($gw['ipprotocol'] == "inet6") || (is_ipaddrv6($gw['gateway'])))) + continue; + if (($pconfig['ipprotocol'] == "inet") && !(($gw['ipprotocol'] == "inet") || (is_ipaddrv4($gw['gateway'])))) + continue; + if ($gw == "") + continue; + + $gateways[ $gwname ] = $gw['name'] . (empty($gw['gateway'])? '' : ' - '. $gateway_addr_str); +} + +foreach ((array)$a_gatewaygroups as $gwg_name => $gwg_data) +{ + if ((empty($pconfig['ipprotocol'])) || ($pconfig['ipprotocol'] == $gwg_data['ipprotocol'])) + $gateways[ $gwg_name ] = $gwg_name; +} + +$section->addInput(new Form_Select( + 'gateway', + 'Gateway', + $pconfig['gateway'], + $gateways +))->setHelp('Leave as \'default\' to use the system routing table. Or choose a '. + 'gateway to utilize policy based routing.'); + +$group = new Form_Group('In / Out pipe'); + +$group->add(new Form_Select( + 'dnpipe', + 'DNpipe', + $pconfig['dnpipe'], + array('' => 'none') + array_keys($dnqlist) +)); + +$group->add(new Form_Select( + 'pdnpipe', + 'PDNpipe', + $pconfig['pdnpipe'], + array('' => 'none') + array_keys($dnqlist) +)); + +$section->add($group)->setHelp('Choose the Out queue/Virtual interface only if '. + 'you have also selected In. The Out selection is applied to traffic leaving '. + 'the interface where the rule is created, In is applied to traffic coming '. + 'into the chosen interface.<br />If you are creating a floating rule, if the '. + 'direction is In then the same rules apply, if the direction is out the '. + 'selections are reverted Out is for incoming and In is for outgoing.' +); + +$group = new Form_Group('Ackqueue / Queue'); + +$qlist = array_keys($qlist); +foreach ($qlist as $idx => $q) +{ + if (isset($ifdisp[$q])) + $qlist[$idx] = $ifdisp[$q]; +} + +$group->add(new Form_Select( + 'ackqueue', + 'Ackqueue', + $pconfig['ackqueue'], + $qlist +)); + +$group->add(new Form_Select( + 'defaultqueue', + 'Default Queue', + $pconfig['defaultqueue'], + $qlist +)); + +$section->add($group)->setHelp('Choose the Acknowledge Queue only if you have '. + 'selected Queue.' +); + +$section->addInput(new Form_Select( + 'l7container', + 'Layer7', + $pconfig['l7container'], + array_keys($l7clist) +))->setHelp('Choose a Layer7 container to apply application protocol inspection '. + 'rules. These are valid for TCP and UDP protocols only.'); + +$has_created_time = (isset($a_filter[$id]['created']) && is_array($a_filter[$id]['created'])); +$has_updated_time = (isset($a_filter[$id]['updated']) && is_array($a_filter[$id]['updated'])); + + +if ($has_created_time || $has_updated_time) +{ + $form->add($section); + $section = new Form_Section('Rule Information'); + + if ($has_created_time) + { + $section->addInput(new Form_StaticText( + 'Created', + date('n/j/y H:i:s', $a_filter[$id]['created']['time']) . gettext(' by ') .'<b>'. $a_filter[$id]['created']['username'] .'</b>' + )); + } + + if ($has_updated_time) + { + $section->addInput(new Form_StaticText( + 'Updated', + date('n/j/y H:i:s', $a_filter[$id]['updated']['time']) . gettext(' by ') .'<b>'. $a_filter[$id]['updated']['username'] .'</b>' + )); + } +} + +$form->add($section); +echo $form; +?> + +<script> +//<![CDATA[ +events.push(function(){ + + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified group input element lives so that the input, + // its label and help text are hidden + function hideGroupInput(id, hide) { + if(hide) + $('#' + id).parent('div').addClass('hidden'); + else + $('#' + id).parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified checkbox lives so that the checkbox, its label and help text are hidden + function hideCheckbox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // Hides all elements of the specified class. This will usually be a section + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // Hides all elements of the specified class assigned to a group. This will usually be a group + function hideGroupClass(s_class, hide) { + if(hide) + $('.' + s_class).parent().parent().parent().hide(); + else + $('.' + s_class).parent().parent().parent().show(); + } + + var portsenabled = 1; + var editenabled = 1; + var optionsvisible = 0; + + function ext_change() { + if (($('#srcbeginport').find(":selected").index() == 0) && portsenabled && editenabled) { + disableInput('srcbeginport_cust', false); + } else { + if (editenabled) + $('#srcbeginport_cust').val(""); + + disableInput('srcbeginport_cust', true); + } + + if (($('#srcendport').find(":selected").index() == 0) && portsenabled && editenabled) { + disableInput('srcendport_cust', false); + } else { + if (editenabled) + $('#srcendport_cust').val(""); + + disableInput('srcendport_cust', true); + } + + if (($('#dstbeginport').find(":selected").index() == 0) && portsenabled && editenabled) { + disableInput('dstbeginport_cust', false); + } else { + if (editenabled) + $('#dstbeginport_cust').val(""); + + disableInput('dstbeginport_cust', true); + } + + if (($('#dstendport').find(":selected").index() == 0) && portsenabled && editenabled) { + disableInput('dstendport_cust', false); + } else { + if (editenabled) + $('#dstendport_cust').val(""); + + disableInput('dstendport_cust', true); + } + + if (!portsenabled) { + disableInput('srcbeginport', true); + disableInput('srcendport', true); + disableInput('dstbeginport', true); + disableInput('dstendport', true); + } else { + if( editenabled ) { + disableInput('srcbeginport', false); + disableInput('srcendport', false); + disableInput('dstbeginport', false); + disableInput('dstendport', false); + } + } + } + + function show_source_port_range() { + if (portsenabled) { + hideClass('srcprtr', false); + } + } + + function typesel_change() { + if( editenabled ) { + switch ($('#srctype').find(":selected").index()) { + case 1: // single + disableInput('src', false); + $('#srcmask').val(""); + disableInput('srcmask', true); + break; + case 2: // network + disableInput('src', false); + disableInput('srcmask', false); + break; + default: + $('#src').val(""); + disableInput('src', true); + $('#srcmask').val(""); + disableInput('srcmask', true); + break; + } + switch ($('#dsttype').find(":selected").index()) { + case 1: // single + disableInput('dst', false); + $('#dstmask').val(""); + disableInput('dstmask', true); + break; + case 2: // network + disableInput('dst', false); + disableInput('dstmask', false); + break; + default: + $('#dst').val(""); + disableInput('dst', true); + $('#dstmask').val(""); + disableInput('dstmask', true); + break; + } + } + } + + function proto_change() { + if ($('#proto').find(":selected").index() < 3) { + portsenabled = 1; + hideClass('tcpflags', false); + } else { + portsenabled = 0; + hideClass('tcpflags', true); + } + + // Disable OS knob if the proto is not TCP. + if ($('#proto').find(":selected").index() < 1) { + disableInput('os', false); + } else { + disableInput('os', true); + } + + if ($('#proto').find(":selected").index() == 3) { + disableInput('icmptype', false); + disableInput('icmp6type', false); + } else { + disableInput('icmptype', true); + disableInput('icmp6type', true); + } + + ext_change(); + + if($('#proto').find(":selected").index() == 3 || $('#proto').find(":selected").index() == 4) { + if($('#ipprotocol').find(":selected").index() == 0) { // IPv4 + hideInput('icmptype', false); + hideInput('icmp6type', true); + } else if($('#ipprotocol').find(":selected").index() == 1) { // IPv6 + hideInput('icmptype', true); + hideInput('icmp6type', false); + } else { // IPv4 + IPv6 + hideInput('icmptype', true); + hideInput('icmp6type', true); + } + } else { + hideInput('icmptype', true); + hideInput('icmp6type', true); + } + + if($('#proto').find(":selected").index() >= 0 && $('#proto').find(":selected").index() <= 2) { + hideClass('dstprtr', false); + hideClass('srcprtr', false); + } else { + hideClass('srcprtr', true); + hideClass('dstprtr', true); + } + } + + function src_rep_change() { + $('#srcendport').prop("selectedIndex", $('#srcbeginport').find(":selected").index()); + } + + function dst_rep_change() { + $('#dstendport').prop("selectedIndex", $('#dstbeginport').find(":selected").index()); + } + + // On initial page load + ext_change(); + typesel_change(); + proto_change(); + hideClass('advanced-options', true); + hideClass('srcportrange', true); + + <?php if ((!empty($pconfig['srcbeginport']) && $pconfig['srcbeginport'] != "any") || (!empty($pconfig['srcendport']) && $pconfig['srcendport'] != "any")): ?> + show_source_port_range(); + <?php endif; ?> + + // Make it a regular button, not a submit + $('#toggle-advanced').prop('type','button'); + $("#btnsrcadv").prop('type','button'); + + // on click . . + $('#srcbeginport').on('change', function() { + src_rep_change(); + ext_change(); + }); + + $('#btnsrcadv').click(function() { + hideClass('srcportrange', false); + hideInput('btnsrcadv', true); + }); + + $('#srcendport').on('change', function() { + ext_change(); + }); + + $('#dstbeginport').on('change', function() { + dst_rep_change(); + ext_change(); + }); + + $('#dstendport').on('change', function() { + ext_change(); + }); + + $('#srctype').on('change', function() { + typesel_change(); + }); + + $('#dsttype').on('change', function() { + typesel_change(); + }); + + $('#proto').on('change', function() { + proto_change(); + }); + + $('#ipprotocol').on('change', function() { + proto_change(); + }); + + + $('#toggle-advanced').click(function() { + optionsvisible = 1; + hideClass('advanced-options', false); + if($('#tcpflags_any').prop('checked')) + $('.table-flags').addClass('hidden'); + }); + + $('#tcpflags_any').click(function () { + if(this.checked) + $('.table-flags').addClass('hidden'); + else + $('.table-flags').removeClass('hidden'); + }); +}); +//]]> +</script> + +<?php +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_schedule.php b/src/usr/local/www/firewall_schedule.php new file mode 100644 index 0000000..f3e2062 --- /dev/null +++ b/src/usr/local/www/firewall_schedule.php @@ -0,0 +1,282 @@ +<?php +/* + firewall_schedule.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004 Scott Ullrich + * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net> + * Originally part of pfSense (https://www.pfsense.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: schedules +*/ +##|+PRIV +##|*IDENT=page-firewall-schedules +##|*NAME=Firewall: Schedules page +##|*DESCR=Allow access to the 'Firewall: Schedules' page. +##|*MATCH=firewall_schedule.php* +##|-PRIV + +define('CLOCK', '🕐'); + +$dayArray = array (gettext('Mon'), gettext('Tues'), gettext('Wed'), gettext('Thur'), gettext('Fri'), gettext('Sat'), gettext('Sun')); +$monthArray = array (gettext('January'), gettext('February'), gettext('March'), gettext('April'), gettext('May'), gettext('June'), gettext('July'), gettext('August'), gettext('September'), gettext('October'), gettext('November'), gettext('December')); + +require("guiconfig.inc"); +require_once("filter.inc"); +require("shaper.inc"); + +$pgtitle = array(gettext("Firewall"), gettext("Schedules")); + +if (!is_array($config['schedules']['schedule'])) { + $config['schedules']['schedule'] = array(); +} + +$a_schedules = &$config['schedules']['schedule']; + +if ($_GET['act'] == "del") { + if ($a_schedules[$_GET['id']]) { + /* make sure rule is not being referenced by any nat or filter rules */ + $is_schedule_referenced = false; + $referenced_by = false; + $schedule_name = $a_schedules[$_GET['id']]['name']; + + if (is_array($config['filter']['rule'])) { + foreach ($config['filter']['rule'] as $rule) { + //check for this later once this is established + if ($rule['sched'] == $schedule_name) { + $referenced_by = $rule['descr']; + $is_schedule_referenced = true; + break; + } + } + } + + if ($is_schedule_referenced == true) { + $savemsg = sprintf(gettext("Cannot delete Schedule. Currently in use by %s"), $referenced_by); + } else { + unset($a_schedules[$_GET['id']]); + write_config(); + header("Location: firewall_schedule.php"); + exit; + } + } +} + +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg, 'success'); +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Schedules')?></h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><!--"Active" indicator--></th> + <th><?=gettext("Name")?></th> + <th><?=gettext("Range: Date / Times / Name")?></th> + <th><?=gettext("Description")?></th> + <th><!--Buttons--></th> + </tr> + </thead> + <tbody> +<?php +$i = 0; +foreach ($a_schedules as $schedule): + $schedstatus = filter_get_time_based_rule_status($schedule); +?> + <tr> + <td> + <?=($schedstatus) ? '<a title="' . gettext("Schedule is currently active") . '">' . CLOCK . '</a>':''?> + </td> + <td> + <?=htmlspecialchars($schedule['name'])?> + </td> + <td> +<?php + $first = true; + foreach($schedule['timerange'] as $timerange) { + $tempFriendlyTime = ""; + $tempID = ""; + $firstprint = false; + + if ($timerange) { + $dayFriendly = ""; + $tempFriendlyTime = ""; + + //get hours + $temptimerange = $timerange['hour']; + $temptimeseparator = strrpos($temptimerange, "-"); + + $starttime = substr ($temptimerange, 0, $temptimeseparator); + $stoptime = substr ($temptimerange, $temptimeseparator+1); + + if ($timerange['month']) { + $tempmontharray = explode(",", $timerange['month']); + $tempdayarray = explode(",",$timerange['day']); + $arraycounter = 0; + $firstDayFound = false; + $firstPrint = false; + foreach ($tempmontharray as $monthtmp){ + $month = $tempmontharray[$arraycounter]; + $day = $tempdayarray[$arraycounter]; + + if (!$firstDayFound) { + $firstDay = $day; + $firstmonth = $month; + $firstDayFound = true; + } + + $currentDay = $day; + $nextDay = $tempdayarray[$arraycounter+1]; + $currentDay++; + + if (($currentDay != $nextDay) || ($tempmontharray[$arraycounter] != $tempmontharray[$arraycounter+1])){ + if ($firstPrint) + $dayFriendly .= "<br />"; + + $currentDay--; + + if ($currentDay != $firstDay) + $dayFriendly .= $monthArray[$firstmonth-1] . " " . $firstDay . " - " . $currentDay ; + else + $dayFriendly .= $monthArray[$month-1] . " " . $day; + + $firstDayFound = false; + $firstPrint = true; + } + $arraycounter++; + } + } + else { + $tempdayFriendly = $timerange['position']; + $firstDayFound = false; + $tempFriendlyDayArray = explode(",", $tempdayFriendly); + $currentDay = ""; + $firstDay = ""; + $nextDay = ""; + $counter = 0; + + foreach ($tempFriendlyDayArray as $day){ + if ($day != ""){ + if (!$firstDayFound) + { + $firstDay = $tempFriendlyDayArray[$counter]; + $firstDayFound = true; + } + + $currentDay =$tempFriendlyDayArray[$counter]; + //get next day + $nextDay = $tempFriendlyDayArray[$counter+1]; + $currentDay++; + + if ($currentDay != $nextDay){ + if ($firstprint) + $dayFriendly .= "<br />"; + + $currentDay--; + + if ($currentDay != $firstDay) + $dayFriendly .= $dayArray[$firstDay-1] . " - " . $dayArray[$currentDay-1]; + else + $dayFriendly .= $dayArray[$firstDay-1]; + + $firstDayFound = false; + $firstprint = true; + } + $counter++; + } + } + } + + $timeFriendly = $starttime . "-" . $stoptime; + $description = $timerange['rangedescr']; + + print(($first ? '':'<br />') . $dayFriendly . ' / ' . $timeFriendly . ' / ' . $description); + } + $first = false; + } +?> + </td> + + <td> + <?=htmlspecialchars($schedule['descr'])?> + </td> + + <td> + <a href="firewall_schedule_edit.php?id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext("Edit")?></a> + <a href="firewall_schedule.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger"><?=gettext("Delete")?></a> + + </td> + </tr> +<?php + $i++; +endforeach; +?> + </tbody> + </table> + </div> +</div> + +<?=($i > 0) ? gettext(CLOCK . ' Indicates that the scedule is currently active.'):''?> + +<nav class="action-buttons"> + <a href="firewall_schedule_edit.php" class="btn btn-sm btn-success"><?=gettext("Add new schedule")?></a> +</nav> + +<?php + +print_info_box(gettext('Schedules act as placeholders for time ranges to be used in Firewall Rules.')); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_schedule_edit.php b/src/usr/local/www/firewall_schedule_edit.php new file mode 100644 index 0000000..bec4029 --- /dev/null +++ b/src/usr/local/www/firewall_schedule_edit.php @@ -0,0 +1,1225 @@ +<?php +/* + firewall_schedule_edit.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004 Scott Ullrich + * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net> + * Originally part of pfSense (https://www.pfsense.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: schedules +*/ + +##|+PRIV +##|*IDENT=page-firewall-schedules-edit +##|*NAME=Firewall: Schedules: Edit page +##|*DESCR=Allow access to the 'Firewall: Schedules: Edit' page. +##|*MATCH=firewall_schedule_edit.php* +##|-PRIV + +function schedulecmp($a, $b) { + return strcmp($a['name'], $b['name']); +} + +function schedule_sort(){ + global $g, $config; + + if (!is_array($config['schedules']['schedule'])) + return; + + usort($config['schedules']['schedule'], "schedulecmp"); +} + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +$pgtitle = array(gettext("Firewall"),gettext("Schedules"),gettext("Edit")); + +$referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/firewall_schedule.php'); + +$dayArray = array (gettext('Mon'),gettext('Tues'),gettext('Wed'),gettext('Thur'),gettext('Fri'),gettext('Sat'),gettext('Sun')); +$monthArray = array (gettext('January'),gettext('February'),gettext('March'),gettext('April'),gettext('May'),gettext('June'),gettext('July'),gettext('August'),gettext('September'),gettext('October'),gettext('November'),gettext('December')); + +if (!is_array($config['schedules']['schedule'])) + $config['schedules']['schedule'] = array(); + +$a_schedules = &$config['schedules']['schedule']; + +if (is_numericint($_GET['id'])) + $id = $_GET['id']; + +if (isset($_POST['id']) && is_numericint($_POST['id'])) + $id = $_POST['id']; + +if (isset($id) && $a_schedules[$id]) { + $pconfig['name'] = $a_schedules[$id]['name']; + $pconfig['descr'] = html_entity_decode($a_schedules[$id]['descr']); + $pconfig['timerange'] = $a_schedules[$id]['timerange']; + $pconfig['schedlabel'] = $a_schedules[$id]['schedlabel']; + $getSchedule = true; +} + +if ($_POST) { + + if(strtolower($_POST['name']) == "lan") + $input_errors[] = gettext("Schedule may not be named LAN."); + + if(strtolower($_POST['name']) == "wan") + $input_errors[] = gettext("Schedule may not be named WAN."); + + if(strtolower($_POST['name']) == "") + $input_errors[] = gettext("Schedule name cannot be blank."); + + $x = is_validaliasname($_POST['name']); + if (!isset($x)) { + $input_errors[] = gettext("Reserved word used for schedule name."); + } else { + if (is_validaliasname($_POST['name']) == false) + $input_errors[] = gettext("The schedule name may only consist of the characters a-z, A-Z, 0-9"); + } + + /* check for name conflicts */ + foreach ($a_schedules as $schedule) { + if (isset($id) && ($a_schedules[$id]) && ($a_schedules[$id] === $schedule)) + continue; + + if ($schedule['name'] == $_POST['name']) { + $input_errors[] = gettext("A Schedule with this name already exists."); + break; + } + } + + $schedule = array(); + + $schedule['name'] = $_POST['name']; + $schedule['descr'] = htmlentities($_POST['descr'], ENT_QUOTES, 'UTF-8'); + + $timerangeFound = false; + + for ($x=0; $x<99; $x++){ + if($_POST['schedule' . $x]) { + if (!preg_match('/^[0-9]+:[0-9]+$/', $_POST['starttime' . $x])) { + $input_errors[] = sprintf(gettext("Invalid start time - '%s'"), $_POST['starttime' . $x]); + continue; + } + + if (!preg_match('/^[0-9]+:[0-9]+$/', $_POST['stoptime' . $x])) { + $input_errors[] = sprintf(gettext("Invalid stop time - '%s'"), $_POST['stoptime' . $x]); + continue; + } + + $timerangeFound = true; + $timeparts = array(); + $firstprint = false; + $timestr = $_POST['schedule' . $x]; + $timehourstr = $_POST['starttime' . $x]; + $timehourstr .= "-"; + $timehourstr .= $_POST['stoptime' . $x]; + $timedescrstr = htmlentities($_POST['timedescr' . $x], ENT_QUOTES, 'UTF-8'); + $dashpos = strpos($timestr, '-'); + + if ($dashpos === false) + { + $timeparts['position'] = $timestr; + } + else + { + $tempindarray = array(); + $monthstr = ""; + $daystr = ""; + $tempindarray = explode(",", $timestr); + foreach ($tempindarray as $currentselection) + { + if ($currentselection){ + if ($firstprint) + { + $monthstr .= ","; + $daystr .= ","; + } + $tempstr = ""; + $monthpos = strpos($currentselection, "m"); + $daypos = strpos($currentselection, "d"); + $monthstr .= substr($currentselection, $monthpos+1, $daypos-$monthpos-1); + $daystr .= substr($currentselection, $daypos+1); + $firstprint = true; + } + } + + $timeparts['month'] = $monthstr; + $timeparts['day'] = $daystr; + } + + $timeparts['hour'] = $timehourstr; + $timeparts['rangedescr'] = $timedescrstr; + $schedule['timerange'][$x] = $timeparts; + } + } + + if (!$timerangeFound) + $input_errors[] = gettext("The schedule must have at least one time range configured."); + + if (!$input_errors) { + + if (!empty($pconfig['schedlabel'])) + $schedule['schedlabel'] = $pconfig['schedlabel']; + else + $schedule['schedlabel'] = uniqid(); + + if (isset($id) && $a_schedules[$id]){ + $a_schedules[$id] = $schedule; + } + else{ + $a_schedules[] = $schedule; + } + + schedule_sort(); + + if (write_config()) + filter_configure(); + + header("Location: firewall_schedule.php"); + exit; + + } + //we received input errors, copy data to prevent retype + else + { + if (!$_POST['schedule0']) + $getSchedule = false; + else + $getSchedule = true; + + $pconfig['name'] = $schedule['name']; + $pconfig['descr'] = $schedule['descr']; + $pconfig['timerange'] = $schedule['timerange']; + } + +} + +include("head.inc"); + +// Returns a string containg the HTML to display a calendar table +function build_date_table() { + $tblstr = ""; + + $firstmonth = TRUE; + $monthcounter = date("n"); + $yearcounter = date("Y"); + + for ($k=0; $k<12; $k++){ + $firstdayofmonth = date("w", mktime(0, 0, 0, date($monthcounter), 1, date($yearcounter))); + + if ($firstdayofmonth == 0) + $firstdayofmonth = 7; + + $daycounter = 1; + //number of day in month + $numberofdays = date("t", mktime(0, 0, 0, date($monthcounter), 1, date($yearcounter))); + $firstdayprinted = FALSE; + $lasttr = FALSE; + $positioncounter = 1;//7 for Sun, 1 for Mon, 2 for Tues, etc + + $mostr = '<div id="' . date("F_y", mktime(0, 0, 0, date($monthcounter), 1, date($yearcounter))) ; + $mostr .= '" style="position:relative; display:'; + + if($firstmonth) + $mostr .= "block"; + else + $mostr .= "none"; + + $mostr .= '" class="col-md-6">'; + + $mostr .= + '<table class="table table-condensed" border="1" cellspacing="1" cellpadding="1" id="calTable' . $monthcounter . $yearcounter . '" > + <thead><tr class="info"><td colspan="7" align="center" ><b>' . date("F_Y", mktime(0, 0, 0, date($monthcounter), 1, date($yearcounter))) . '</b></td> + </tr> + <tr> + <th class="text-center" style="cursor: pointer;" onclick="daytoggle(\'w1p1\');">' . gettext("Mon") . '</th> + <th class="text-center" style="cursor: pointer;" onclick="daytoggle(\'w1p2\');">' . gettext("Tue") . '</th> + <th class="text-center" style="cursor: pointer;" onclick="daytoggle(\'w1p3\');">' . gettext("Wed") . '</th> + <th class="text-center" style="cursor: pointer;" onclick="daytoggle(\'w1p4\');">' . gettext("Thu") . '</th> + <th class="text-center" style="cursor: pointer;" onclick="daytoggle(\'w1p5\');">' . gettext("Fri") . '</th> + <th class="text-center" style="cursor: pointer;" onclick="daytoggle(\'w1p6\');">' . gettext("Sat") . '</th> + <th class="text-center" style="cursor: pointer;" onclick="daytoggle(\'w1p7\');">' . gettext("Sun") . '</th> + </tr> + </thead> + <tbody>' . "\r\n"; + + $firstmonth = FALSE; + + while ($daycounter<=$numberofdays) { + $weekcounter = date("W", mktime(0, 0, 0, date($monthcounter), date($daycounter), date($yearcounter))); + $weekcounter = ltrim($weekcounter, "0"); + if ($positioncounter == 1) + { + $mostr .= "<tr>"; + } + + if ($firstdayofmonth == $positioncounter){ + + $mostr .= '<td class="text-center" style="cursor: pointer;" id="w' . $weekcounter . 'p' . $positioncounter . '" onclick="daytoggle(\'w' . $weekcounter . 'p' . $positioncounter . '-m' . $monthcounter . 'd' . $daycounter . '\');">' . $daycounter . "\r\n"; + + $daycounter++; + $firstdayprinted = TRUE; + $mostr .= "</td>"; + } + elseif ($firstdayprinted == TRUE && $daycounter <= $numberofdays) { + $mostr .= '<td class="text-center" style="cursor: pointer;" id="w' . $weekcounter . 'p' . $positioncounter . '" onclick="daytoggle(\'w' . $weekcounter . 'p' . $positioncounter . '-m' . $monthcounter . 'd' . $daycounter . '\');">' . $daycounter . "\r\n"; + $daycounter++; + $mostr .= "</td>"; + } + else + { + $mostr .= '<td class="text-center"></td>'; + } + + if ($positioncounter == 7 || $daycounter > $numberofdays){ + $positioncounter = 1; + $mostr .= "</tr>"; + } + else{ + $positioncounter++; + } + + } + + $mostr .= '</tbody></table>'; + $mostr .= gettext('Click individual date to select that date only. Click the appropriate weekday Header to select all occurrences of that weekday. '); + $mostr .= '</div>'; + + if ($monthcounter == 12) + { + $monthcounter = 1; + $yearcounter++; + } + else + { + $monthcounter++; + } + + $tblstr .= $mostr; + } //end for loop + + return($tblstr); +} + +function build_month_list() { + + $list = array(); + + $monthcounter = date("n"); + $monthlimit = $monthcounter + 12; + $yearcounter = date("Y"); + + for ($k=0; $k<12; $k++){ + $list[$monthcounter] = date("F_y", mktime(0, 0, 0, date($monthcounter), 1, date($yearcounter))); + + if ($monthcounter == 12) { + $monthcounter = 1; + $yearcounter++; + } + else + { + $monthcounter++; + } + } + + return($list); +} + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Schedule information'); + +$input = new Form_Input( + 'name', + 'Schedule Name', + 'text', + $pconfig['name'] +); + +$input->setHelp((is_schedule_inuse($pconfig['name']) != true) ? 'The name of the alias may only consist of the characters a-z, A-Z and 0-9': + 'This schedule is in use so the name may not be modified!'); + +if(is_schedule_inuse($pconfig['name']) == true) + $input->setReadonly(); + +$section->addInput($input); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed). '); + +$section->addInput(new Form_Select( + 'monthsel', + 'Month', + null, + build_month_list() +)); + +$section->addInput(new Form_StaticText( + 'Date', + build_date_table() +)); + +$group = new Form_Group('Time'); + +$group->add(new Form_Select( + 'starttimehour', + null, + null, + array_combine(range(0, 23, 1), range(0, 23, 1)) +))->setHelp('Start Hrs'); + +$group->add(new Form_Select( + 'starttimemin', + null, + null, + array('00' => '00', '15' => '15', '30' => '30', '59' => '59') +))->setHelp('Start Mins'); + +$group->add(new Form_Select( + 'stoptimehour', + null, + '23', + array_combine(range(0, 23, 1), range(0, 23, 1)) +))->setHelp('Stop Hrs'); + +$group->add(new Form_Select( + 'stoptimemin', + null, + '59', + array('00' => '00', '15' => '15', '30' => '30', '59' => '59') +))->setHelp('Stop Mins'); + +$group->setHelp('Select the time range for the day(s) selected on the Month(s) above. A full day is 0:00-23:59.'); + +$section->add($group); + +$section->addInput(new Form_Input( + 'timerangedescr', + 'Time range description', + 'text', + $pconfig['timerangedescr'] +))->setHelp('You may enter a description here for your reference (not parsed). '); + +$group = new Form_Group(null); + +$group->add(new Form_Button( + 'btnaddtime', + 'Add Time' +))->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$group->add(new Form_Button( + 'btnclrsel', + 'Clear selection' +))->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->add($group); + +if (isset($id) && $a_schedules[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); + +$section = new Form_Section('Configured ranges'); +$counter = 0; + +if ($getSchedule){ + $maxrows = count($pconfig['timerange']) -1; + + foreach($pconfig['timerange'] as $timerange) { + $tempFriendlyTime = ""; + $tempID = ""; + if ($timerange){ + $dayFriendly = ""; + $tempFriendlyTime = ""; + $timedescr = $timerange['rangedescr']; + + //get hours + $temptimerange = $timerange['hour']; + $temptimeseparator = strrpos($temptimerange, "-"); + + $starttime = substr ($temptimerange, 0, $temptimeseparator); + $stoptime = substr ($temptimerange, $temptimeseparator+1); + $currentDay = ""; + $firstDay = ""; + $nextDay = ""; + $foundEnd = false; + $firstDayFound = false; + $firstPrint = false; + $firstprint2 = false; + + if ($timerange['month']){ + $tempmontharray = explode(",", $timerange['month']); + $tempdayarray = explode(",",$timerange['day']); + $arraycounter = 0; + foreach ($tempmontharray as $monthtmp){ + $month = $tempmontharray[$arraycounter]; + $day = $tempdayarray[$arraycounter]; + $daypos = date("w", mktime(0, 0, 0, date($month), date($day), date("Y"))); + //if sunday, set position to 7 to get correct week number. This is due to php limitations on ISO-8601. When we move to php5.1 we can change this. + if ($daypos == 0){ + $daypos = 7; + } + + $weeknumber = date("W", mktime(0, 0, 0, date($month), date($day), date("Y"))); + $weeknumber = ltrim($weeknumber, "0"); + + if ($firstPrint) + { + $tempID .= ","; + } + + $tempID .= "w" . $weeknumber . "p" . $daypos . "-m" . $month . "d" . $day; + $firstPrint = true; + + if (!$firstDayFound) + { + $firstDay = $day; + $firstmonth = $month; + $firstDayFound = true; + } + + $currentDay = $day; + $nextDay = $tempdayarray[$arraycounter+1]; + $currentDay++; + if (($currentDay != $nextDay) || ($tempmontharray[$arraycounter] != $tempmontharray[$arraycounter+1])){ + if ($firstprint2) + $tempFriendlyTime .= ", "; + + $currentDay--; + + if ($currentDay != $firstDay) + $tempFriendlyTime .= $monthArray[$firstmonth-1] . " " . $firstDay . " - " . $currentDay ; + else + $tempFriendlyTime .= $monthArray[$month-1] . " " . $day; + + $firstDayFound = false; + $firstprint2 = true; + } + $arraycounter++; + } + + } + else + { + $dayFriendly = $timerange['position']; + $tempID = $dayFriendly; + } + + $tempTime = $tempID . "||" . $starttime . "-" . $stoptime . "||" . $timedescr; + + //following code makes the days friendly appearing, IE instead of Mon, Tues, Wed it will show Mon - Wed + $foundEnd = false; + $firstDayFound = false; + $firstprint = false; + $tempFriendlyDayArray = explode(",", $dayFriendly); + $currentDay = ""; + $firstDay = ""; + $nextDay = ""; + $i = 0; + + if (!$timerange['month']){ + foreach ($tempFriendlyDayArray as $day){ + if ($day != ""){ + if (!$firstDayFound) + { + $firstDay = $tempFriendlyDayArray[$i]; + $firstDayFound = true; + } + + $currentDay =$tempFriendlyDayArray[$i]; + //get next day + $nextDay = $tempFriendlyDayArray[$i+1]; + $currentDay++; + + if ($currentDay != $nextDay){ + if ($firstprint) + $tempFriendlyTime .= ", "; + + $currentDay--; + + if ($currentDay != $firstDay) + $tempFriendlyTime .= $dayArray[$firstDay-1] . " - " . $dayArray[$currentDay-1]; + else + $tempFriendlyTime .= $dayArray[$firstDay-1]; + + $firstDayFound = false; + $firstprint = true; + } + $i++; + } + } + } + + $group = new Form_Group(''); + $group->add(new Form_Input( + 'tempFriendlyTime', + null, + 'readonly', + $tempFriendlyTime + ))->setWidth(2)->setHelp($counter == $maxrows ? 'Day(s)':''); + + $group->add(new Form_Input( + 'starttime' . $counter, + null, + 'readonly', + $starttime + ))->setWidth(2)->setHelp($counter == $maxrows ? 'Start time':''); + + $group->add(new Form_Input( + 'stoptime' . $counter, + null, + 'readonly', + $stoptime + ))->setWidth(2)->setHelp($counter == $maxrows ? 'Stop time':''); + + $group->add(new Form_Input( + 'timedescr' . $counter, + null, + 'readonly', + $timedescr + ))->setWidth(2)->setHelp($counter == $maxrows ? 'Description':''); + + $group->add(new Form_Button( + 'Delete' . $counter, + 'Delete' + ))->removeClass('btn-primary')->addClass('btn-xs btn-warning'); + + $group->add(new Form_Input( + 'schedule' . $counter, + null, + 'hidden', + $tempID + )); + + $group->addClass('schedulegrp' . $counter); + + $counter++; + $section->add($group); + } + } +} + +// This is just a marker that the javascript can use to insertBefore() when adding new rows +$section->addInput(new Form_Input( + 'marker', + null, + 'hidden' +))->addClass('noranges'); + +$form->add($section); + +print($form); +?> + +<script type="text/javascript"> +//<![CDATA[ +events.push(function(){ + + //Update the calendar when a new month is selected + $('#monthsel').on('change', function() { + update_month(); + }); + + // Make the ‘clear’ button a plain button, not a submit button + $('#btnclrsel').prop('type', 'button'); + + $('#btnclrsel').click(function() { + clearCalendar(); + clearTime(); + clearDescr(); + }); + + // Make the ‘Add time’ button a plain button, not a submit button + $('#btnaddtime').prop('type', 'button'); + + $('#btnaddtime').click(function() { + processEntries(); + }); + + $('[id^=Delete]').prop('type', 'button'); + + $('[id^=Delete]').click(function(event) { + delete_row(event.target.id.slice(6)); + }); +}); +//]]> +</script> + +<script type="text/javascript"> +//<![CDATA[ +var daysSelected = ""; +var month_array = ['January','February','March','April','May','June','July','August','September','October','November','December']; +var day_array = ['Mon','Tues','Wed','Thur','Fri','Sat','Sun']; +var schCounter = 0; + +function rgb2hex(rgb) { + var parts = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); + + if (parts == null) + return; + + function hex(x) { + return ("0" + parseInt(x).toString(16)).slice(-2); + } + + return ("#" + hex(parts[1]) + hex(parts[2]) + hex(parts[3])).toUpperCase(); +} + +function repeatExistingDays(){ + var tempstr, tempstrdaypos, week, daypos, dayposdone = ""; + + var dayarray = daysSelected.split(","); + + for (i=0; i<=dayarray.length; i++){ + tempstr = dayarray[i]; + tempstrdaypos = tempstr.search("p"); + week = tempstr.substring(1,tempstrdaypos); + week = parseInt(week); + dashpos = tempstr.search("-"); + daypos = tempstr.substring(tempstrdaypos+1, dashpos); + daypos = parseInt(daypos); + + daydone = dayposdone.search(daypos); + tempstr = 'w' + week + 'p' + daypos; + daycell = eval('document.getElementById(tempstr)'); + if (daydone == "-1"){ + if (rgb2hex(daycell.style.backgroundColor) == "#F08080") // lightcoral + daytogglerepeating(week,daypos,true); + else + daytogglerepeating(week,daypos,false); + + dayposdone += daypos + ","; + } + } +} + +function daytogglerepeating(week,daypos,bExists){ + var tempstr, daycell, dayoriginal = ""; + + for (j=1; j<=53; j++) { + tempstr = 'w' + j + 'p' + daypos; + daycell = eval('document.getElementById(tempstr)'); + dayoriginalpos = daysSelected.indexOf(tempstr); + + //if bExists set to true, means cell is already select it + //unselect it and remove original day from daysSelected string + + if (daycell != null) { + if (bExists){ + daycell.style.backgroundColor = "#FFFFFF"; // white + } + else + { + daycell.style.backgroundColor = "#F08080"; // lightcoral + } + + if (dayoriginalpos != "-1") { + dayoriginalend = daysSelected.indexOf(',', dayoriginalpos); + tempstr = daysSelected.substring(dayoriginalpos, dayoriginalend+1); + daysSelected = daysSelected.replace(tempstr, ""); + } + } + } +} + +function daytoggle(id) { + var runrepeat, tempstr = ""; + var bFoundValid = false; + + iddashpos = id.search("-"); + + var tempstrdaypos = id.search("p"); + var week = id.substring(1,tempstrdaypos); + + week = parseInt(week); + + if (iddashpos == "-1") { + idmod = id; + runrepeat = true; + var daypos = id.substr(tempstrdaypos+1); + } + else + { + idmod = id.substring(0,iddashpos); + var daypos = id.substring(tempstrdaypos+1,iddashpos); + } + + daypos = parseInt(daypos); + + while (!bFoundValid){ + var daycell = document.getElementById(idmod); + + if (daycell != null){ + if (rgb2hex(daycell.style.backgroundColor) == "#FF0000"){ // red + daycell.style.backgroundColor = "#FFFFFF"; // white + str = id + ","; + daysSelected = daysSelected.replace(str, ""); + } + else if (rgb2hex(daycell.style.backgroundColor) == "#F08080") // lightcoral + { + daytogglerepeating(week,daypos,true); + } + else //color is white cell + { + if (!runrepeat) + { + daycell.style.backgroundColor = "#FF0000"; // red + } + else + { + daycell.style.backgroundColor = "#F08080"; // lightcoral + daytogglerepeating(week,daypos,false); + } + daysSelected += id + ","; + } + bFoundValid = true; + } + else + { + //we found an invalid cell when column was clicked, move up to the next week + week++; + tempstr = "w" + week + "p" + daypos; + idmod = tempstr; + } + } +} + +function update_month(){ + var indexNum = document.forms[0].monthsel.selectedIndex; + var selected = document.forms[0].monthsel.options[indexNum].text; + + for (i=0; i<=11; i++){ + option = document.forms[0].monthsel.options[i].text; + document.popupMonthLayer = eval('document.getElementById (option)'); + + if(selected == option) + document.popupMonthLayer.style.display="block"; + else + document.popupMonthLayer.style.display="none"; + } +} + +function checkForRanges(){ + if (daysSelected != "") + { + alert("You have not saved the specified time range. Please click 'Add Time' button to save the time range."); + return false; + } + else + { + return true; + } +} + +function processEntries(){ + var tempstr, starttimehour, starttimemin, stoptimehour, stoptimemin, errors = ""; + var passedValidiation = true; + + //get time specified + starttimehour = parseInt(document.getElementById("starttimehour").value); + starttimemin = parseInt(document.getElementById("starttimemin").value); + stoptimehour = parseInt(document.getElementById("stoptimehour").value); + stoptimemin = parseInt(document.getElementById("stoptimemin").value); + + //do time checks + if (starttimehour > stoptimehour) + { + errors = "Error: Start Hour cannot be greater than Stop Hour."; + passedValidiation = false; + + } + else if (starttimehour == stoptimehour) + { + if (starttimemin > stoptimemin){ + errors = "Error: Start Minute cannot be greater than Stop Minute."; + passedValidiation = false; + } + } + + if (passedValidiation){ + addTimeRange(); + } + else { + if (errors != "") + alert(errors); + } +} + +function addTimeRange(){ + var tempdayarray = daysSelected.split(","); + var tempstr, tempFriendlyDay, starttimehour, starttimemin, stoptimehour, nrtempFriendlyTime, rtempFriendlyTime, nrtempID, rtempID = ""; + var stoptimemin, timeRange, tempstrdaypos, week, daypos, day, month, dashpos, nrtempTime, rtempTime, monthstr, daystr = ""; + rtempFriendlyTime = ""; + nrtempFriendlyTime = ""; + nrtempID = ""; + rtempID = ""; + nrtempTime = ""; + rtempTime = ""; + tempdayarray.sort(); + rtempFriendlyDay = ""; + monthstr = ""; + daystr = ""; + + //check for existing entries + var findCurrentCounter; + + for (u=0; u<99; u++){ + findCurrentCounter = document.getElementById("schedule" + u); + if (!findCurrentCounter) + { + schCounter = u; + break; + } + } + + if (daysSelected != ""){ + //get days selected + + for (i=0; i<tempdayarray.length; i++) + { + tempstr = tempdayarray[i]; + if (tempstr != "") + { + tempstrdaypos = tempstr.search("p"); + week = tempstr.substring(1,tempstrdaypos); + week = parseInt(week); + dashpos = tempstr.search("-"); + + if (dashpos != "-1") + { + var nonrepeatingfound = true; + daypos = tempstr.substring(tempstrdaypos+1, dashpos); + daypos = parseInt(daypos); + monthpos = tempstr.search("m"); + tempstrdaypos = tempstr.search("d"); + month = tempstr.substring(monthpos+1, tempstrdaypos); + month = parseInt(month); + day = tempstr.substring(tempstrdaypos+1); + day = parseInt(day); + monthstr += month + ","; + daystr += day + ","; + nrtempID += tempstr + ","; + } + else + { + var repeatingfound = true; + daypos = tempstr.substr(tempstrdaypos+1); + daypos = parseInt(daypos); + rtempFriendlyDay += daypos + ","; + rtempID += daypos + ","; + } + } + } + + //code below spits out friendly look format for nonrepeating schedules + var foundEnd = false; + var firstDayFound = false; + var firstprint = false; + var tempFriendlyMonthArray = monthstr.split(","); + var tempFriendlyDayArray = daystr.split(","); + var currentDay, firstDay, nextDay, currentMonth, nextMonth, firstDay, firstMonth = ""; + + for (k=0; k<tempFriendlyMonthArray.length; k++){ + tempstr = tempFriendlyMonthArray[k]; + if (tempstr != ""){ + if (!firstDayFound) + { + firstDay = tempFriendlyDayArray[k]; + firstDay = parseInt(firstDay); + firstMonth = tempFriendlyMonthArray[k]; + firstMonth = parseInt(firstMonth); + firstDayFound = true; + } + + currentDay = tempFriendlyDayArray[k]; + currentDay = parseInt(currentDay); + //get next day + nextDay = tempFriendlyDayArray[k+1]; + nextDay = parseInt(nextDay); + //get next month + + currentDay++; + if ((currentDay != nextDay) || (tempFriendlyMonthArray[k] != tempFriendlyMonthArray[k+1])){ + if (firstprint) + nrtempFriendlyTime += ", "; + + currentDay--; + + if (currentDay != firstDay) + nrtempFriendlyTime += month_array[firstMonth-1] + " " + firstDay + "-" + currentDay; + else + nrtempFriendlyTime += month_array[firstMonth-1] + " " + currentDay; + + firstDayFound = false; + firstprint = true; + } + } + } + + //code below spits out friendly look format for repeating schedules + foundEnd = false; + firstDayFound = false; + firstprint = false; + tempFriendlyDayArray = rtempFriendlyDay.split(","); + tempFriendlyDayArray.sort(); + currentDay, firstDay, nextDay = ""; + + for (k=0; k<tempFriendlyDayArray.length; k++){ + tempstr = tempFriendlyDayArray[k]; + if (tempstr != ""){ + if (!firstDayFound) + { + firstDay = tempFriendlyDayArray[k]; + firstDay = parseInt(firstDay); + firstDayFound = true; + } + + currentDay = tempFriendlyDayArray[k]; + currentDay = parseInt(currentDay); + //get next day + nextDay = tempFriendlyDayArray[k+1]; + nextDay = parseInt(nextDay); + currentDay++; + + if (currentDay != nextDay){ + if (firstprint) + rtempFriendlyTime += ", "; + + currentDay--; + + if (currentDay != firstDay) + rtempFriendlyTime += day_array[firstDay-1] + " - " + day_array[currentDay-1]; + else + rtempFriendlyTime += day_array[firstDay-1]; + + firstDayFound = false; + firstprint = true; + } + } + } + + //sort the tempID + var tempsortArray = rtempID.split(","); + var isFirstdone = false; + + tempsortArray.sort(); + + //clear tempID + rtempID = ""; + for (t=0; t<tempsortArray.length; t++) + { + if (tempsortArray[t] != ""){ + if (!isFirstdone){ + rtempID += tempsortArray[t]; + isFirstdone = true; + } + else + rtempID += "," + tempsortArray[t]; + } + } + + //get time specified + starttimehour = document.getElementById("starttimehour").value + starttimemin = document.getElementById("starttimemin").value; + stoptimehour = document.getElementById("stoptimehour").value; + stoptimemin = document.getElementById("stoptimemin").value; + + timeRange = "||" + starttimehour + ":"; + timeRange += starttimemin + "-"; + timeRange += stoptimehour + ":"; + timeRange += stoptimemin; + + //get description for time range + var tempdescr = document.getElementById("timerangedescr").value + + if (nonrepeatingfound){ + nrtempTime += nrtempID; + //add time ranges + nrtempTime += timeRange; + //add description + nrtempTime += "||" + tempdescr; + insertElements(nrtempFriendlyTime, starttimehour, starttimemin, stoptimehour, stoptimemin, tempdescr, nrtempTime, nrtempID); + } + + if (repeatingfound){ + rtempTime += rtempID; + //add time ranges + rtempTime += timeRange; + //add description + rtempTime += "||" + tempdescr; + insertElements(rtempFriendlyTime, starttimehour, starttimemin, stoptimehour, stoptimemin, tempdescr, rtempTime, rtempID); + } + + } + else + { + //no days were selected, alert user + alert ("You must select at least 1 day before adding time"); + } +} + +function clearCalendar(){ + var tempstr, daycell = ""; + //clear days selected + daysSelected = ""; + //loop through all 52 weeks + for (j=1; j<=53; j++) + { + //loop through all 7 days + for (k=1; k<8; k++){ + tempstr = 'w' + j + 'p' + k; + daycell = eval('document.getElementById(tempstr)'); + if (daycell != null){ + daycell.style.backgroundColor = "#FFFFFF"; // white + } + } + } +} + +function clearTime(){ + document.getElementById("starttimehour").value = "0"; + document.getElementById("starttimemin").value = "00"; + document.getElementById("stoptimehour").value = "23"; + document.getElementById("stoptimemin").value = "59"; +} + +function clearDescr(){ + document.getElementById("timerangedescr").value = ""; +} + +var counter = -1; + +// Using an HTML template, build a time-range row and add it after the last row in the display +function insertElements(tempFriendlyTime, starttimehour, starttimemin, stoptimehour, stoptimemin, tempdescr, tempTime, tempID) { + var rowhtml; + + if(counter < 0) { + counter = <?php if(!isset($counter)) echo '0'; else echo $counter ?>; + rows_displayed = counter; + } + + // Template for the schedule deifinition. '@' will be replaced with the row number using .replace() + rowhtml = + '<div class="form-group schedulegrp' + counter + '">' + + '<label for="tempFriendlyTime" class="col-sm-2 control-label"></label>' + + '<div class="col-sm-2">' + + '<input class="form-control" name="tempFriendlyTime" id="tempFriendlyTime" type="readonly" value="' + tempFriendlyTime + '"/>' + + '<span class="help-block">Day(s)</span>' + + '</div>' + + '<div class="col-sm-2">' + + '<input class="form-control" name="starttime@" id="starttime@" type="readonly" value="' + starttimehour + ':' + starttimemin + '"/>' + + '<span class="help-block">Start time</span>' + + '</div>' + + '<div class="col-sm-2">' + + '<input class="form-control" name="stoptime@" id="stoptime@" type="readonly" value="' + stoptimehour + ':' + stoptimemin + '"/>' + + '<span class="help-block">Stop time</span>' + + '</div>' + + '<div class="col-sm-2">' + + '<input class="form-control" name="timedescr@" id="timedescr@" type="readonly" value="' + tempdescr + '"/>' + + '<span class="help-block">Description</span>' + + '</div>' + + '<div class="col-sm-2">' + + '<input class="form-control" name="schedule@" id="schedule@" type="hidden" value="' + tempID + '"/>' + + '</div>' + + '<div class="col-sm-2">' + + '<a class="btn btn-xs btn-warning" name="delete@" id="delete@" type="button" value="@">Delete</a>' + + '</div>' + + '</div>'; + + $('.help-block').hide(); + + var node = $('.noranges').parent().parent(); + $(rowhtml.replace(/@/g, counter)).insertBefore(node); + + $('[id^=delete]').click(function(event) { + delete_row(event.target.id.slice(6)); + }); + + counter++; + + schCounter++; + + //reset calendar and time and descr + clearCalendar(); + clearTime(); + clearDescr(); +} + +// If only everything were this simple +function delete_row(row) { + $('.schedulegrp' + row).remove(); +} +//]]> +</script> + +<?php + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_shaper.php b/src/usr/local/www/firewall_shaper.php new file mode 100644 index 0000000..5050f9a --- /dev/null +++ b/src/usr/local/www/firewall_shaper.php @@ -0,0 +1,509 @@ +<?php +/* $Id$ */ +/* + firewall_shaper.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * Copyright (c) 2008 Ermal Luçi + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /usr/bin/killall + pfSense_MODULE: shaper +*/ + +##|+PRIV +##|*IDENT=page-firewall-trafficshaper +##|*NAME=Firewall: Traffic Shaper page +##|*DESCR=Allow access to the 'Firewall: Traffic Shaper' page. +##|*MATCH=firewall_shaper.php* +##|-PRIV + +require('classes/Form.class.php'); + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("rrd.inc"); + +if($_GET['reset'] != "") { + /* XXX: Huh, why are we killing php? */ + mwexec("killall -9 pfctl php"); + exit; +} + +$pgtitle = array(gettext("Firewall"), gettext("Traffic Shaper")); +$shortcut_section = "trafficshaper"; + +$shaperIFlist = get_configured_interface_with_descr(); +read_altq_config(); +/* + * The whole logic in these code maybe can be specified. + * If you find a better way contact me :). + */ + +if ($_GET) { + if ($_GET['queue']) { + $qname = htmlspecialchars(trim($_GET['queue'])); + } + if ($_GET['interface']) { + $interface = htmlspecialchars(trim($_GET['interface'])); + } + if ($_GET['action']) { + $action = htmlspecialchars($_GET['action']); + } +} + +if ($_POST) { + if ($_POST['name']) { + $qname = htmlspecialchars(trim($_POST['name'])); + } + if ($_POST['interface']) { + $interface = htmlspecialchars(trim($_POST['interface'])); + } + if ($_POST['parentqueue']) { + $parentqueue = htmlspecialchars(trim($_POST['parentqueue'])); + } +} + +if ($interface) { + $altq = $altq_list_queues[$interface]; + + if ($altq) { + $queue =& $altq->find_queue($interface, $qname); + } else { + $addnewaltq = true; + } +} + +$dontshow = false; +$newqueue = false; +$output_form = ""; +$dfltmsg = false; + +if ($_GET) { + switch ($action) { + case "delete": + if ($queue) { + $queue->delete_queue(); + if (write_config()) { + mark_subsystem_dirty('shaper'); + } + } + + header("Location: firewall_shaper.php"); + exit; + break; + case "resetall": + foreach ($altq_list_queues as $altq) { + $altq->delete_all(); + } + unset($altq_list_queues); + $altq_list_queues = array(); + $tree = "<ul class=\"tree\" >"; + $tree .= get_interface_list_to_show(); + $tree .= "</ul>"; + unset($config['shaper']['queue']); + unset($queue); + unset($altq); + $can_add = false; + $can_enable = false; + $dontshow = true; + foreach ($config['filter']['rule'] as $key => $rule) { + if (isset($rule['wizard']) && $rule['wizard'] == "yes") { + unset($config['filter']['rule'][$key]); + } + } + + if (write_config()) { + $retval = 0; + $retval |= filter_configure(); + $savemsg = get_std_save_message($retval); + + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message($retval); + } else { + $savemsg = $retval; + } + } else { + $savemsg = gettext("Unable to write config.xml (Access Denied?)"); + } + + $dfltmsg = true; + + + break; + + case "add": + /* XXX: Find better way because we shouldn't know about this */ + if ($altq) { + + switch ($altq->GetScheduler()) { + case "PRIQ": + $q = new priq_queue(); + break; + case "FAIRQ": + $q = new fairq_queue(); + break; + case "HFSC": + $q = new hfsc_queue(); + break; + case "CBQ": + $q = new cbq_queue(); + break; + default: + /* XXX: Happens when sched==NONE?! */ + $q = new altq_root_queue(); + break; + } + } else if ($addnewaltq) { + $q = new altq_root_queue(); + } else { + $input_errors[] = gettext("Could not create new queue/discipline!"); + } + + if ($q) { + $q->SetInterface($interface); + $sform = $q->build_form(); + $newjavascript = $q->build_javascript(); + unset($q); + $newqueue = true; + } + break; + case "show": + if ($queue) { + $sform = $queue->build_form(); + //$output_form .= $queue->build_form(); + } + else + $input_errors[] = gettext("Queue not found!"); + break; + case "enable": + if ($queue) { + $queue->SetEnabled("on"); + $output_form .= $queue->build_form(); + if (write_config()) { + mark_subsystem_dirty('shaper'); + } + } else { + $input_errors[] = gettext("Queue not found!"); + } + break; + case "disable": + if ($queue) { + $queue->SetEnabled(""); + $output_form .= $queue->build_form(); + if (write_config()) { + mark_subsystem_dirty('shaper'); + } + } else { + $input_errors[] = gettext("Queue not found!"); + } + break; + default: + $dfltmsg = true; + $dontshow = true; + break; + } +} + +if ($_POST) { + unset($input_errors); + + if ($addnewaltq) { + $altq =& new altq_root_queue(); + $altq->SetInterface($interface); + + switch ($altq->GetBwscale()) { + case "Mb": + $factor = 1000 * 1000; + break; + case "Kb": + $factor = 1000; + break; + case "b": + $factor = 1; + break; + case "Gb": + $factor = 1000 * 1000 * 1000; + break; + case "%": /* We don't use it for root_XXX queues. */ + default: /* XXX assume Kb by default. */ + $factor = 1000; + break; + } + + $altq->SetAvailableBandwidth($altq->GetBandwidth() * $factor); + $altq->ReadConfig($_POST); + $altq->validate_input($_POST, $input_errors); + if (!$input_errors) { + unset($tmppath); + $tmppath[] = $altq->GetInterface(); + $altq->SetLink($tmppath); + $altq->wconfig(); + if (write_config()) { + mark_subsystem_dirty('shaper'); + } + $can_enable = true; + $can_add = true; + } + + read_altq_config(); + $output_form .= $altq->build_form(); + + } else if ($parentqueue) { /* Add a new queue */ + $qtmp =& $altq->find_queue($interface, $parentqueue); + if ($qtmp) { + $tmppath =& $qtmp->GetLink(); + array_push($tmppath, $qname); + $tmp =& $qtmp->add_queue($interface, $_POST, $tmppath, $input_errors); + if (!$input_errors) { + array_pop($tmppath); + $tmp->wconfig(); + $can_enable = true; + if ($tmp->CanHaveChildren() && $can_enable) { + if ($tmp->GetDefault() <> "") { + $can_add = false; + } else { + $can_add = true; + } + } else { + $can_add = false; + } + if (write_config()) { + mark_subsystem_dirty('shaper'); + } + $can_enable = true; + if ($altq->GetScheduler() != "PRIQ") { /* XXX */ + if ($tmp->GetDefault() <> "") { + $can_add = false; + } else { + $can_add = true; + } + } + } + read_altq_config(); + $output_form .= $tmp->build_form(); + } else { + $input_errors[] = gettext("Could not add new queue."); + } + } else if ($_POST['apply']) { + write_config(); + + $retval = 0; + $retval = filter_configure(); + $savemsg = get_std_save_message($retval); + + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message($retval); + } else { + $savemsg = $retval; + } + + /* reset rrd queues */ + system("rm -f /var/db/rrd/*queuedrops.rrd"); + system("rm -f /var/db/rrd/*queues.rrd"); + enable_rrd_graphing(); + + clear_subsystem_dirty('shaper'); + + if ($queue) { + $output_form .= $queue->build_form(); + $dontshow = false; + } else { + $output_form .= $default_shaper_message; + $dontshow = true; + } + } else if ($queue) { + $queue->validate_input($_POST, $input_errors); + if (!$input_errors) { + $queue->update_altq_queue_data($_POST); + $queue->wconfig(); + if (write_config()) { + mark_subsystem_dirty('shaper'); + } + $dontshow = false; + } + read_altq_config(); + $output_form .= $queue->build_form(); + } else { + $dfltmsg = true; + $dontshow = true; + } + mwexec("killall qstats"); +} + +if(!$_POST && !$_GET){ + $dfltmsg = true; + $dontshow = true; +} + +if ($queue) { + if ($queue->GetEnabled()) { + $can_enable = true; + } else { + $can_enable = false; + } + if ($queue->CanHaveChildren() && $can_enable) { + if ($altq->GetQname() <> $queue->GetQname() && $queue->GetDefault() <> "") { + $can_add = false; + } else { + $can_add = true; + } + } else { + $can_add = false; + } +} + +//$pgtitle = "Firewall: Shaper: By Interface View"; +$closehead = false; +include("head.inc"); + +$tree = '<ul class="tree" >'; +if (is_array($altq_list_queues)) { + foreach ($altq_list_queues as $tmpaltq) { + $tree .= $tmpaltq->build_tree(); + } + $tree .= get_interface_list_to_show(); +} + +$tree .= "</ul>"; + +if ($queue) + print($queue->build_javascript()); + +print($newjavascript); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('shaper')) + print_info_box_np(gettext("The traffic shaper configuration has been changed. You must apply the changes in order for them to take effect.")); + +$tab_array = array(); +$tab_array[] = array(gettext("By Interface"), true, "firewall_shaper.php"); +$tab_array[] = array(gettext("By Queue"), false, "firewall_shaper_queues.php"); +$tab_array[] = array(gettext("Limiter"), false, "firewall_shaper_vinterface.php"); +$tab_array[] = array(gettext("Layer7"), false, "firewall_shaper_layer7.php"); +$tab_array[] = array(gettext("Wizards"), false, "firewall_shaper_wizards.php"); +display_top_tabs($tab_array); + +?> +<link rel="stylesheet" type="text/css" media="all" href="./tree/tree.css" /> +<script type="text/javascript" src="./tree/tree.js"></script> + +<div class="table-responsive"> + <table class="table"> + <tbody> + <tr class="tabcont"> + <td class="col-md-1"> +<?php +// Display the shaper tree +print($tree); + +if (count($altq_list_queues) > 0) { +?> + <a href="firewall_shaper.php?action=resetall" class="btn btn-sm btn-danger"/> + <?=gettext('Remove Shaper')?> + </a> +<?php +} +?> + </td> + <td> +<?php + +if($dfltmsg) + print_info_box($default_shaper_msg); +else { + // Add global buttons + if (!$dontshow || $newqueue) { + if ($can_add || $addnewaltq) { + if($queue) + $url = 'firewall_shaper.php?interface='. $interface . '&queue=' . $queue->GetQname() . '&action=add'; + else + $url = 'firewall_shaper.php?interface='. $interface . '&action=add'; + + $sform->addGlobal(new Form_Button( + 'add', + 'Add new Queue', + $url + ))->removeClass('btn-default')->addClass('btn-success'); + } + + if($queue) + $url = 'firewall_shaper.php?interface='. $interface . '&queue=' . $queue->GetQname() . '&action=delete'; + else + $url = 'firewall_shaper.php?interface='. $interface . '&action=delete'; + + $sform->addGlobal(new Form_Button( + 'delete', + $queue ? 'Delete this queue':'Disable shaper on interface', + $url + ))->removeClass('btn-default')->addClass('btn-danger'); + } + + // Print the form + print($sform); +} +?> + </td> + </tr> + </tbody> + </table> +</div> + +<?php +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_shaper_layer7.php b/src/usr/local/www/firewall_shaper_layer7.php new file mode 100644 index 0000000..db350bd --- /dev/null +++ b/src/usr/local/www/firewall_shaper_layer7.php @@ -0,0 +1,614 @@ +<?php +/* $Id$ */ +/* + firewall_shaper_layer7.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2008 Helder Pereira, André Ribeiro + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /usr/bin/killall + pfSense_MODULE: shaper +*/ + +##|+PRIV +##|*IDENT=page-firewall-trafficshaper-layer7 +##|*NAME=Firewall: Traffic Shaper: Layer7 page +##|*DESCR=Allow access to the 'Firewall: Traffic Shaper: Layer7' page. +##|*MATCH=firewall_shaper_layer7.php* +##|-PRIV + +require("guiconfig.inc"); +require('classes/Form.class.php'); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +$dfltmsg = false; + +// Variables protocols (dynamic) and structures (static) +$avail_protos =& generate_protocols_array(); +$avail_structures = array("action", "queue", "limiter"); + +// Available behaviours +$avail_behaviours_action = array("block"); +read_altq_config(); +$avail_behaviours_altq = get_altq_name_list(); +read_dummynet_config(); +$avail_behaviours_limiter = get_dummynet_name_list(); +$show_proto_form = false; + +//More variables +$pgtitle = array(gettext("Firewall"), gettext("Traffic Shaper"), gettext("Layer7")); +$shortcut_section = "trafficshaper"; + +$default_layer7shaper_msg = '<br />' . + gettext('You can add new layer7 protocol patterns by simply uploading the file') . + ' <a href="diag_patterns.php">' . gettext('here') . '</a>'; + +read_layer7_config(); + +$sform = new Form(false); + +if($_GET['reset'] != "") { + // kill all ipfw-classifyd processes + mwexec("killall -9 ipfw-classifyd"); + exit; +} + +if ($_GET) { + if ($_GET['container']) { + $name = htmlspecialchars(trim($_GET['container'])); + } + if ($_GET['action']) { + $action = htmlspecialchars($_GET['action']); + } +} + +if ($_POST) { + if ($_POST['container']) { + $name = htmlspecialchars(trim($_POST['container'])); + } +} + +if ($name) { + //Get the object from the 7rules list + $container = $layer7_rules_list[$name]; +} + +if ($_GET) { + switch ($action) { + case "add": + $show_proto_form = true; + $container = new layer7(); + $sform = $container->build_form(); //constructs the graphical interface on the right side + unset($container); + break; + case "show": + $show_proto_form = true; + if($container) { + $sform = $container->build_form(); + } + else { + $show_proto_form = false; + $input_errors[] = gettext("Layer7 Rules Container not found!"); + } + break; + default: + echo log_error("Get default"); + $show_proto_form = false; + $dfltmsg = true; + break; + } +} + +//add a new l7rules container +if ($_POST) { + $show_proto_form = true; + unset($input_errors); + + if($_POST['Submit']) { + + if (isset($layer7_rules_list[$name])) { + $l7r = $layer7_rules_list[$name]; + $_POST['divert_port'] = $l7r->GetRPort(); + } else { + $l7r =& new layer7(); + $_POST['divert_port'] = $l7r->gen_divert_port(); + } + for ($i = 0; $_POST['protocol'][$i] <> ""; $i++) { + $_POST['l7rules'][$i]['protocol'] = $_POST['protocol'][$i]; + $_POST['l7rules'][$i]['structure'] = $_POST['structure'][$i]; + $_POST['l7rules'][$i]['behaviour'] = $_POST['behaviour'][$i]; + } + $l7r->validate_input($_POST, $input_errors); + $l7r->ReadConfig($_POST['container'], $_POST); + //Before writing the results, we need to test for repeated protocols + $non_dupes = array(); + $dupes = array(); + for ($j = 0; $j < $i; $j++) { + if (!$non_dupes[$_POST['protocol'][$j]]) { + $non_dupes[$_POST['protocol'][$j]] = true; + } else { + $dupes[] = $_POST['protocol'][$j]; + } + } + + unset($non_dupes); + if (sizeof($dupes) == 0 && !$input_errors) { + $l7r->wconfig(); + if (write_config()) { + mark_subsystem_dirty('shaper'); + } + + read_layer7_config(); + } else { + if (sizeof($dupes) > 0) { + $dupe_error = gettext("Found the following repeated protocol definitions") . ": "; + foreach ($dupes as $dupe) { + $dupe_error .= "$dupe "; + } + $input_errors[] .= $dupe_error; + } + } + + unset($dupes); + unset($dupe_error); + //Even if there are repeated protocols, we won't lose any previous values + //The user will be able to solve the situation + $sform = $l7r->build_form(); + //Necessary to correctly build the proto form + $container = $layer7_rules_list[$name]; + if ($input_errors) { + $container =& $l7r; + } + } else if ($_POST['apply']) { + write_config(); + + $retval = 0; + $retval = filter_configure(); + $savemsg = get_std_save_message($retval); + + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message($retval); + } else { + $savemsg = $retval; + } + + clear_subsystem_dirty('shaper'); + + if($container) { + $sform = $container->build_form(); + } else { + $show_proto_form = false; + $dfltmsg = true; + } + } else if ($_POST['delete']) { + $container->delete_l7c(); + if (write_config()) { + mark_subsystem_dirty('shaper'); + } + unset($container); + + header("Location: firewall_shaper_layer7.php"); + exit; + } else { + $show_proto_form = false; + } +} + +if(!$_GET && !$_POST) { + $show_proto_form = false; + $dfltmsg = true; +} + +// Builds the left tree +$tree = "<ul class=\"tree\" >"; +if (is_array($layer7_rules_list)) { + foreach ($layer7_rules_list as $tmpl7) { + $tree .= $tmpl7->build_tree(); + } +} + +$tree .= "</ul>"; + +include("head.inc"); +?> + +<link rel="stylesheet" type="text/css" media="all" href="./tree/tree.css" /> +<script type="text/javascript" src="./tree/tree.js"></script> + +<script type="text/javascript"> +//<![CDATA[ +var initial_count = new Array(); +var rows_limit = 0; // Set to 0 to disable limitation + +/* Build the behaviours arrays in javascript */ +var js_behaviours_action = ['block']; //static + +var js_behaviours_altq = new Array(); +js_behaviours_altq = array_altq(js_behaviours_altq); + +var js_behaviours_limiter = new Array(); +js_behaviours_limiter = array_limiter(js_behaviours_limiter); + +function array_altq(a_behav) { + var index; + <?php if (!empty($avail_behaviours_altq)) { + foreach ($avail_behaviours_altq as $key => $queue) { ?> + name = "<?= $queue; ?>"; + index = <?= $key; ?>; + a_behav[index] = name; + <?php } + } ?> + return a_behav; +} + +function array_limiter(a_behav) { + var index; + <?php + if (!empty($avail_behaviours_limiter)) { + foreach ($avail_behaviours_limiter as $key => $limiter) { ?> + name = "<?= $limiter; ?>"; + index = <?= $key; ?>; + a_behav[index] = name; + <?php + } + } ?> + return a_behav; +} + +/* Fill the variables with available protocols, structures and behaviours */ +function fillProtocol() { + var protocol = '<select class="form-control" name="protocol[]">'; + var name; + + <?php foreach ($avail_protos as $key => $proto) { ?> + name = "<?= $proto; ?>"; + protocol += "<option value=" + name + ">" + name + "<\/option>"; + <?php } ?> + protocol += "<\/select>"; + + return protocol; +} + +function fillStructure() { + var structure = '<select class="form-control" name="structure[]" onchange="changeBehaviourValues(this.parentNode.parentNode);">'; + var name; + <?php foreach ($avail_structures as $key => $struct) { ?> + name = "<?= $struct; ?>"; + if(name == "queue") { + if(js_behaviours_altq != "") { structure += "<option value=" + name + ">" + name + "<\/option>";} + } + else { + if(name == "limiter") { + if(js_behaviours_limiter != "") { structure += "<option value=" + name + ">" + name + "<\/option>";} + } + else structure += "<option value=" + name + ">" + name + "<\/option>"; //action + } + <?php } ?> + structure += "<\/select>"; + + return structure; +} + +//Used by default to fill the values when inserting a new row. +function fillBehaviour() { + var behaviour = '<select class="form-control" name="behaviour[]">'; + var name; + <?php foreach ($avail_behaviours_action as $key => $behav) { ?> + name = "<?= $behav; ?>"; + behaviour += "<option value=" + name + ">" + name + "<\/option>"; + <?php } ?> + behaviour += "<\/select>"; + + return behaviour; +} + +/* Change the values on behaviours select when changing the structure row */ +function changeBehaviourValues(row) { + + var selectedRow = row.rowIndex - 1; // The header is counted as the first row + var structureSelected = document.getElementsByName("structure[]")[selectedRow].value; + + //Select the behaviours values to array a_behav + var a_behav = new Array(); + + if (structureSelected == "action") { + a_behav = js_behaviours_action; //static + } else { + if (structureSelected == "queue") { + a_behav = js_behaviours_altq; + } else { + a_behav = js_behaviours_limiter; + } + } + + + //Build the html statement with the array values previously selected + var new_behav; + var name; + for (i = 0; i < a_behav.length; i++) { + new_behav += "<option value=" + a_behav[i] + ">" + a_behav[i] + "<\/option>"; + } + + document.getElementsByName("behaviour[]")[selectedRow].innerHTML = new_behav; +} + +/* Add row to the table */ +function addRow(table_id) { + var tbl = document.getElementById(table_id); + + // counting rows in table + var rows_count = tbl.rows.length; + if (initial_count[table_id] == undefined) { + // if it is first adding in this table setting initial rows count + initial_count[table_id] = rows_count; + } + // determining real count of added fields + var tFielsNum = rows_count - initial_count[table_id]; + if (rows_limit!=0 && tFielsNum >= rows_limit) return false; + + var remove = '<a class="btn btn-default" onclick="removeRow(\''+table_id+'\',this.parentNode.parentNode)">Remove<\/a>'; + + try { + var newRow = tbl.insertRow(rows_count); + var newCell = newRow.insertCell(0); + newCell.innerHTML = fillProtocol(); + var newCell = newRow.insertCell(1); + newCell.innerHTML = fillStructure(); + var newCell = newRow.insertCell(2); + newCell.innerHTML = fillBehaviour(); + var newCell = newRow.insertCell(3); + newCell.innerHTML = remove; + } + catch (ex) { + //if exception occurs + alert(ex); + } +} + +/* Remove row from the table */ +function removeRow(tbl, row) { + var table = document.getElementById(tbl); + try { + table.deleteRow(row.rowIndex); + } catch (ex) { + alert(ex); + } +} +//]]> +</script> + +<?php +// This function creates a table of rule selectors which are then inserted into the form +// using a StaticText class. While not pretty this maintains compatibility with all of +// the above javascript + +function build_l7table() { + global $container, $avail_protos, $avail_structures, $avail_behaviours_altq, $avail_behaviours_limiter, + $avail_behaviours_action; + + $tbl = '<table id="newtbl" class="table table-hover table-condensed">'; // No stripes for this table + $tbl .= '<thead><tr><th>Protocol</th><th>Structure</th><th>Behavior</th></tr></thead>'; + $tbl .= '<tbody>'; + + if($container) { + foreach($container->rsets as $l7rule) { + + $tbl .= '<tr><td>'; + $tbl .= '<select name="protocol[]" class="form-control">'; + + foreach($avail_protos as $proto): + $tbl .= '<option value="' . $proto . '"'; + + if ($proto == $l7rule->GetRProtocol()) + $tbl .= ' selected="selected"'; + + $tbl .= '>' . $proto . '</option>'; + + endforeach; + + $tbl .= '</select></td><td>'; + $tbl .= '<select name="structure[]" class="form-control" onchange="changeBehaviourValues(this.parentNode.parentNode);">'; + + foreach($avail_structures as $struct) { + if($struct == "queue") { + if(!empty($avail_behaviours_altq)) { + $tbl .= '<option value="' . $struct . '"'; + if ($struct == $l7rule->GetRStructure()) + $tbl .= ' selected="selected"'; + + $tbl .= '>' . $struct . '</option>'; + } + } + else { + if($struct == "limiter") { + if(!empty($avail_behaviours_limiter)) { + $tbl .= '<option value="' . $struct . '"'; + if ($struct == $l7rule->GetRStructure()) + $tbl .= ' selected="selected"'; + + $tbl .= '>' . $struct . '</option>'; + } + } + else { + if($struct == "action") { + $tbl .= '<option value="' . $struct . '"'; + if ($struct == $l7rule->GetRStructure()) + $tbl .= ' selected="selected"'; + + $tbl .= '>' . $struct . '</option>'; + } + } + } + } + + $tbl .= '</select></td><td>'; + + $tbl .= '<select name="behaviour[]" class="form-control">'; + + if($l7rule->GetRStructure() == "action"): + foreach($avail_behaviours_action as $behaviour): + $tbl .= '<option value="' . $behaviour . '"'; + if ($behaviour == $l7rule->GetRBehaviour()) + $tbl .= ' selected="selected"'; + + $tbl .= '>' . $behaviour . '</option>'; + + endforeach; + + $tbl .= '</select>'; + + endif; + + if($l7rule->GetRStructure() == "queue"): + foreach($avail_behaviours_altq as $behaviour): + + $tbl .= '<option value="' . $behaviour . '"'; + if ($behaviour == $l7rule->GetRBehaviour()) + $tbl .= ' selected="selected"'; + + $tbl .= '>' . $behaviour . '</option>'; + + endforeach; + + $tbl .= '</select>'; + + endif; + + if($l7rule->GetRStructure() == "limiter"): + foreach($avail_behaviours_limiter as $behaviour): + $tbl .= '<option value="' . $behaviour . '"'; + if ($behaviour == $l7rule->GetRBehaviour()) + $tbl .= ' selected="selected"'; + + $tbl .= '>' . $behaviour . '</option>'; + + endforeach; + + $tbl .= '</select>'; + + endif; + + $tbl .= '</td><td>'; + $tbl .= '<a type="button" class="btn btn-default" onclick="removeRow(\'newtbl\',this.parentNode.parentNode); return false;" href="#">'; + $tbl .= gettext('Remove') . '</a>'; + $tbl .= '</td></tr>'; + + + } //end foreach + } //end if + + $tbl .= '</tbody></table>'; + + $tbl .= '<a id="addrow" type="button" onclick="javascript:addRow(\'newtbl\'); return false;" href="#" class="btn btn-sm btn-success">' . gettext('Add row') . + '</a>'; + + return($tbl); +} + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('shaper')) + print_info_box_np(gettext("The traffic shaper configuration has been changed") . ".<br />" . gettext("You must apply the changes in order for them to take effect.")); + +$tab_array = array(); +$tab_array[] = array(gettext("By Interface"), false, "firewall_shaper.php"); +$tab_array[] = array(gettext("By Queue"), false, "firewall_shaper_queues.php"); +$tab_array[] = array(gettext("Limiter"), false, "firewall_shaper_vinterface.php"); +$tab_array[] = array(gettext("Layer7"), true, "firewall_shaper_layer7.php"); +$tab_array[] = array(gettext("Wizards"), false, "firewall_shaper_wizards.php"); +display_top_tabs($tab_array); + +// Create a StaticText control and populate it with the rules table +if(!$dfltmsg) { + $section = new Form_Section('Add one (or more) rules'); + + $section->addInput(new Form_StaticText( + 'Rule(s)', + build_l7table() + )); + + $sform->add($section); +} +?> + + <div class="panel panel-default"> + <div class="panel-heading" align="center"><h2 class="panel-title">Layer 7</h2></div> + <div class="panel-body"> + <div class="form-group"> + <div class="col-sm-2 "> + <?=$tree?> + <br /> + <a href="firewall_shaper_layer7.php?action=add" class="btn btn-sm btn-success"> + <?=gettext("Create new L7<br />rule group")?> + </a> + </div> + <div class="col-sm-10"> +<?php +if($dfltmsg) + print_info_box($output_form = $dn_default_shaper_msg . $default_layer7shaper_msg); +else + print($sform); +?> + </div> + </div> + </div> + </div> + + +<?php +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_shaper_queues.php b/src/usr/local/www/firewall_shaper_queues.php new file mode 100644 index 0000000..5d1bbb7 --- /dev/null +++ b/src/usr/local/www/firewall_shaper_queues.php @@ -0,0 +1,271 @@ +<?php +/* $Id$ */ +/* NEW + firewall_shaper_queues.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * Copyright (c) 2008 Ermal Luçi + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /usr/bin/killall + pfSense_MODULE: shaper +*/ + +##|+PRIV +##|*IDENT=page-firewall-trafficshaper-queues +##|*NAME=Firewall: Traffic Shaper: Queues page +##|*DESCR=Allow access to the 'Firewall: Traffic Shaper: Queues' page. +##|*MATCH=firewall_shaper_queues.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("rrd.inc"); + +if($_GET['reset'] != "") { + mwexec("killall -9 pfctl"); + exit; +} + +$qname = gettext("No queue configured/selected"); + +$shaperIFlist = get_configured_interface_with_descr(); +read_altq_config(); +$qlist =& get_unique_queue_list(); + +if (!is_array($qlist)) { + $qlist = array(); +} + +$tree = "<ul class=\"tree\" >"; +foreach ($qlist as $queue => $qkey) { + $tree .= "<li><a href=\"firewall_shaper_queues.php?queue={$queue}&action=show\" >"; + if (isset($shaperIFlist[$queue])) { + $tree .= $shaperIFlist[$queue] . "</a></li>"; + } else { + $tree .= $queue . "</a></li>"; + } +} +$tree .= "</ul>"; + +if ($_GET) { + if ($_GET['queue']) { + $qname = htmlspecialchars(trim($_GET['queue'])); + } + + if ($_GET['interface']) { + $interface = htmlspecialchars(trim($_GET['interface'])); + } + + if ($_GET['action']) { + $action = htmlspecialchars($_GET['action']); + } + + switch ($action) { + case "delete": + $altq =& $altq_list_queues[$interface]; + $qtmp =& $altq->find_queue("", $qname); + if ($qtmp) { + $qtmp->delete_queue(); + if (write_config()) { + mark_subsystem_dirty('shaper'); + } + } + header("Location: firewall_shaper_queues.php"); + exit; + break; + case "add": + /* + * XXX: WARNING: This returns the first it finds. + * Maybe the user expects something else?! + */ + foreach ($altq_list_queues as $altq) { + $qtmp =& $altq->find_queue("", $qname); + + if ($qtmp) { + $copycfg = array(); + $qtmp->copy_queue($interface, $copycfg); + $aq =& $altq_list_queues[$interface]; + + if ($qname == $qtmp->GetInterface()) { + $config['shaper']['queue'][] = $copycfg; + } else if ($aq) { + $tmp1 =& $qtmp->find_parentqueue($interface, $qname); + if ($tmp1) { + $tmp =& $aq->find_queue($interface, $tmp1->GetQname()); + } + + if ($tmp) { + $link =& get_reference_to_me_in_config($tmp->GetLink()); + } else { + $link =& get_reference_to_me_in_config($aq->GetLink()); + } + + $link['queue'][] = $copycfg; + } else { + $newroot = array(); + $newroot['name'] = $interface; + $newroot['interface'] = $interface; + $newroot['scheduler'] = $altq->GetScheduler(); + $newroot['queue'] = array(); + $newroot['queue'][] = $copycfg; + $config['shaper']['queue'][] = $newroot; + } + + if (write_config()) + mark_subsystem_dirty('shaper'); + + break; + } + } + + header("Location: firewall_shaper_queues.php?queue=".$qname."&action=show"); + exit; + break; + case "show": + foreach ($config['interfaces'] as $if => $ifdesc) { + $altq = $altq_list_queues[$if]; + + if ($altq) { + $qtmp =& $altq->find_queue("", $qname); + + if ($qtmp) + $output .= $qtmp->build_shortform(); + else + $output .= build_iface_without_this_queue($if, $qname); + + } else { + if (!is_altq_capable($ifdesc['if'])) + continue; + + if (!isset($ifdesc['enable']) && $if != "lan" && $if != "wan") + continue; + + $output .= build_iface_without_this_queue($if, $qname); + } + } + break; + } +} + +if ($_POST['apply']) { + write_config(); + + $retval = 0; + /* Setup pf rules since the user may have changed the optimization value */ + $retval = filter_configure(); + $savemsg = get_std_save_message($retval); + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message($retval); + } else { + $savemsg = $retval; + } + + /* reset rrd queues */ + system("rm -f /var/db/rrd/*queuedrops.rrd"); + system("rm -f /var/db/rrd/*queues.rrd"); + enable_rrd_graphing(); + + clear_subsystem_dirty('shaper'); +} + +$pgtitle = gettext("Firewall: Shaper: By Queues View"); +$shortcut_section = "trafficshaper"; +$closehead = false; + +include("head.inc"); +?> + +<link rel="stylesheet" type="text/css" media="all" href="./tree/tree.css" /> +<script type="text/javascript" src="./tree/tree.js"></script> + +<?php +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg); + +if (is_subsystem_dirty('shaper')) + print_info_box_np(gettext("The traffic shaper configuration has been changed. You must apply the changes in order for them to take effect.")); + +$tab_array = array(); +$tab_array[] = array(gettext("By Interface"), false, "firewall_shaper.php"); +$tab_array[] = array(gettext("By Queue"), true, "firewall_shaper_queues.php"); +$tab_array[] = array(gettext("Limiter"), false, "firewall_shaper_vinterface.php"); +$tab_array[] = array(gettext("Layer7"), false, "firewall_shaper_layer7.php"); +$tab_array[] = array(gettext("Wizards"), false, "firewall_shaper_wizards.php"); +display_top_tabs($tab_array); + +?> + +<form action="firewall_shaper_queues.php" method="post" name="iform" id="iform"> + <div class="panel panel-default"> + <div class="panel-heading" align="center"><h2 class="panel-title"><?=$qname?></h2></div> + <div class="panel-body"> + <div class="form-group"> + <div class="col-sm-2 "> + <?=$tree?> + </div> + <div class="col-sm-10"> + <?=$output?> + </div> + </div> + </div> + </div> +</form> + +<?php + +include("foot.inc"); diff --git a/src/usr/local/www/firewall_shaper_vinterface.php b/src/usr/local/www/firewall_shaper_vinterface.php new file mode 100644 index 0000000..da5ae24 --- /dev/null +++ b/src/usr/local/www/firewall_shaper_vinterface.php @@ -0,0 +1,489 @@ +<?php +/* $Id$ */ +/* + firewall_shaper_vinterface.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * Copyright (c) 2008 Ermal Luçi + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /usr/bin/killall + pfSense_MODULE: shaper +*/ + +##|+PRIV +##|*IDENT=page-firewall-trafficshaper-limiter +##|*NAME=Firewall: Traffic Shaper: Limiter page +##|*DESCR=Allow access to the 'Firewall: Traffic Shaper: Limiter' page. +##|*MATCH=firewall_shaper_vinterface.php* +##|-PRIV + +require('classes/Form.class.php'); +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +if($_GET['reset'] != "") { + mwexec("/usr/bin/killall -9 pfctl"); + exit; +} + +$pgtitle = array(gettext("Firewall"), gettext("Traffic Shaper"), gettext("Limiter")); +$shortcut_section = "trafficshaper-limiters"; +$dfltmsg = false; + +read_dummynet_config(); +/* + * The whole logic in these code maybe can be specified. + * If you find a better way contact me :). + */ + +if ($_GET) { + if ($_GET['queue']) { + $qname = htmlspecialchars(trim($_GET['queue'])); + } + if ($_GET['pipe']) { + $pipe = htmlspecialchars(trim($_GET['pipe'])); + } + if ($_GET['action']) { + $action = htmlspecialchars($_GET['action']); + } +} + +if ($_POST) { + if ($_POST['name']) { + $qname = htmlspecialchars(trim($_POST['name'])); + } else if ($_POST['newname']) { + $qname = htmlspecialchars(trim($_POST['newname'])); + } + if ($_POST['pipe']) { + $pipe = htmlspecialchars(trim($_POST['pipe'])); + } else { + $pipe = htmlspecialchars(trim($qname)); + } + if ($_POST['parentqueue']) { + $parentqueue = htmlspecialchars(trim($_POST['parentqueue'])); + } +} + +if ($pipe) { + $dnpipe = $dummynet_pipe_list[$pipe]; + if ($dnpipe) { + $queue =& $dnpipe->find_queue($pipe, $qname); + } else { + $addnewpipe = true; + } +} + +$dontshow = false; +$newqueue = false; +$output_form = ""; + +if ($_GET) { + switch ($action) { + case "delete": + if ($queue) { + if (is_array($config['filter']['rule'])) { + foreach ($config['filter']['rule'] as $rule) { + if ($rule['dnpipe'] == $queue->GetQname() || $rule['pdnpipe'] == $queue->GetQname()) { + $input_errors[] = gettext("This pipe/queue is referenced in filter rules, please remove references from there before deleting."); + } + } + } + if (!$input_errors) { + $queue->delete_queue(); + if (write_config()) { + mark_subsystem_dirty('shaper'); + } + header("Location: firewall_shaper_vinterface.php"); + exit; + } + $output_form .= $queue->build_form(); + } else { + $input_errors[] = sprintf(gettext("No queue with name %s was found!"), $qname); + $output_form .= $dn_default_shaper_msg; + $dontshow = true; + } + break; + case "resetall": + foreach ($dummynet_pipe_list as $dn) { + $dn->delete_queue(); + } + unset($dummynet_pipe_list); + $dummynet_pipe_list = array(); + unset($config['dnshaper']['queue']); + unset($queue); + unset($pipe); + $can_add = false; + $can_enable = false; + $dontshow = true; + foreach ($config['filter']['rule'] as $key => $rule) { + if (isset($rule['dnpipe'])) { + unset($config['filter']['rule'][$key]['dnpipe']); + } + if (isset($rule['pdnpipe'])) { + unset($config['filter']['rule'][$key]['pdnpipe']); + } + } + if (write_config()) { + $retval = 0; + $retval = filter_configure(); + $savemsg = get_std_save_message($retval); + + if (stristr($retval, "error") != true) + $savemsg = get_std_save_message($retval); + else + $savemsg = $retval; + + } else + $savemsg = gettext("Unable to write config.xml (Access Denied?)"); + + $dfltmsg = true; + + break; + case "add": + if ($dnpipe) { + $q = new dnqueue_class(); + $q->SetPipe($pipe); + $output_form .= "<input type=\"hidden\" name=\"parentqueue\" id=\"parentqueue\""; + $output_form .= " value=\"".$pipe."\" />"; + } else if ($addnewpipe) { + $q = new dnpipe_class(); + $q->SetQname($pipe); + } else + $input_errors[] = gettext("Could not create new queue/discipline!"); + + if ($q) { + $sform = $q->build_form(); + $newjavascript = $q->build_javascript(); + unset($q); + $newqueue = true; + } + break; + case "show": + if ($queue) + $sform = $queue->build_form(); + else + $input_errors[] = gettext("Queue not found!"); + break; + case "enable": + if ($queue) { + $queue->SetEnabled("on"); + $sform = $queue->build_form(); + $queue->wconfig(); + if (write_config()) + mark_subsystem_dirty('shaper'); + } else + $input_errors[] = gettext("Queue not found!"); + break; + case "disable": + if ($queue) { + $queue->SetEnabled(""); + $sform = $queue->build_form(); + $queue->wconfig(); + if (write_config()) + mark_subsystem_dirty('shaper'); + } else + $input_errors[] = gettext("Queue not found!"); + break; + default: + $dfltmsg = true; + $dontshow = true; + break; + } +} + +if ($_POST) { + unset($input_errors); + + if ($addnewpipe) { + if (!empty($dummynet_pipe_list[$qname])) { + $input_errors[] = gettext("You cannot name a child queue with the same name as a parent limiter"); + } else { + $dnpipe =& new dnpipe_class(); + + $dnpipe->ReadConfig($_POST); + $dnpipe->validate_input($_POST, $input_errors); + if (!$input_errors) { + $number = dnpipe_find_nextnumber(); + $dnpipe->SetNumber($number); + unset($tmppath); + $tmppath[] = $dnpipe->GetQname(); + $dnpipe->SetLink($tmppath); + $dnpipe->wconfig(); + if (write_config()) { + mark_subsystem_dirty('shaper'); + } + $can_enable = true; + $can_add = true; + } + + read_dummynet_config(); + $sform = $dnpipe->build_form(); + $newjavascript = $dnpipe->build_javascript(); + } + } else if ($parentqueue) { /* Add a new queue */ + if (!empty($dummynet_pipe_list[$qname])) { + $input_errors[] = gettext("You cannot name a child queue with the same name as a parent limiter"); + } else if ($dnpipe) { + $tmppath =& $dnpipe->GetLink(); + array_push($tmppath, $qname); + $tmp =& $dnpipe->add_queue($pipe, $_POST, $tmppath, $input_errors); + if (!$input_errors) { + array_pop($tmppath); + $tmp->wconfig(); + if (write_config()) { + $can_enable = true; + $can_add = false; + mark_subsystem_dirty('shaper'); + } + } + read_dummynet_config(); + $sform = $tmp->build_form(); + } else + $input_errors[] = gettext("Could not add new queue."); + } else if ($_POST['apply']) { + write_config(); + + $retval = 0; + $retval = filter_configure(); + $savemsg = get_std_save_message($retval); + + if (stristr($retval, "error") != true) + $savemsg = get_std_save_message($retval); + else + $savemsg = $retval; + + /* XXX: TODO Make dummynet pretty graphs */ + // enable_rrd_graphing(); + + clear_subsystem_dirty('shaper'); + + if ($queue) { + $sform = $queue->build_form(); + $dontshow = false; + } + else { + $output_form .= $dn_default_shaper_message; + $dontshow = true; + } + + } else if ($queue) { + $queue->validate_input($_POST, $input_errors); + if (!$input_errors) { + $queue->update_dn_data($_POST); + $queue->wconfig(); + if (write_config()) + mark_subsystem_dirty('shaper'); + $dontshow = false; + } + read_dummynet_config(); + $sform = $queue->build_form(); + } else { + $dfltmsg = true; + $dontshow = true; + } +} + +if(!$_POST && !$_GET) { + $dfltmsg = true; + $dontshow = true; +} + +if ($queue) { + if ($queue->GetEnabled()) + $can_enable = true; + else + $can_enable = false; + if ($queue->CanHaveChildren()) { + $can_add = true; + } else + $can_add = false; +} + +$tree = "<ul class=\"tree\" >"; +if (is_array($dummynet_pipe_list)) { + foreach ($dummynet_pipe_list as $tmpdn) { + $tree .= $tmpdn->build_tree(); + } +} +$tree .= "</ul>"; + +$output = "<table summary=\"output form\">"; +$output .= $output_form; +$closehead = false; +include("head.inc"); +?> +<link rel="stylesheet" type="text/css" media="all" href="./tree/tree.css" /> +<script type="text/javascript" src="./tree/tree.js"></script> + +<script type="text/javascript"> +//<![CDATA[ +function show_source_port_range() { + document.getElementById("sprtable").style.display = ''; + document.getElementById("sprtable1").style.display = ''; + document.getElementById("sprtable2").style.display = ''; + document.getElementById("sprtable5").style.display = ''; + document.getElementById("sprtable4").style.display = 'none'; + document.getElementById("showadvancedboxspr").innerHTML=''; +} +//]]> +</script> + +<?php +if ($queue) { + echo $queue->build_javascript(); +} else { + echo $newjavascript; +} + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('shaper')) + print_info_box_np(gettext("The traffic shaper configuration has been changed. You must apply the changes in order for them to take effect.")); + +$tab_array = array(); +$tab_array[] = array(gettext("By Interface"), false, "firewall_shaper.php"); +$tab_array[] = array(gettext("By Queue"), false, "firewall_shaper_queues.php"); +$tab_array[] = array(gettext("Limiter"), true, "firewall_shaper_vinterface.php"); +$tab_array[] = array(gettext("Layer7"), false, "firewall_shaper_layer7.php"); +$tab_array[] = array(gettext("Wizards"), false, "firewall_shaper_wizards.php"); +display_top_tabs($tab_array); +?> + +<div class="table-responsive"> + <table class="table"> + <tbody> + <tr class="tabcont"> + <td class="col-md-1"> + <?=$tree?> + <a href="firewall_shaper_vinterface.php?pipe=new&action=add" class="btn btn-sm btn-success"/> + <?=gettext('New Limiter')?> + </a> + </td> + <td> +<?php + +if($dfltmsg) + print_info_box($dn_default_shaper_msg); +else { + // Add global buttons + if (!$dontshow || $newqueue) { + if ($can_add || $addnewaltq) { + if($queue) + $url = 'href="firewall_shaper_vinterface.php?pipe=' . $pipe . '&queue=' . $queue->GetQname() . '&action=add'; + else + $url = 'firewall_shaper.php?pipe='. $pipe . '&action=add'; + + $sform->addGlobal(new Form_Button( + 'add', + 'Add new Queue', + $url + ))->removeClass('btn-default')->addClass('btn-success'); + } + + if($queue) + $url = 'firewall_shaper_vinterface.php?pipe='. $pipe . '&queue=' . $queue->GetQname() . '&action=delete'; + else + $url = 'firewall_shaper_vinterface.php?pipe='. $pipe . '&action=delete'; + + $sform->addGlobal(new Form_Button( + 'delete', + $queue ? 'Delete this queue':'Delete', + $url + ))->removeClass('btn-default')->addClass('btn-danger'); + } + + // Print the form + print($sform); + +} +?> + </td> + </tr> + </tbody> + </table> +</div> + +<script> +//<![CDATA[ +events.push(function(){ + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + function change_masks() { + disableInput('maskbits', ($('#scheduler').val() == 'none')); + disableInput('maskbitsv6', ($('#scheduler').val() == 'none')); + } + + // On page load . . + change_masks(); + + // On click . . + $('#scheduler').on('change', function() { + change_masks(); + }); +}); +//]]> +</script> + + +<?php +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_shaper_wizards.php b/src/usr/local/www/firewall_shaper_wizards.php new file mode 100644 index 0000000..18cc97e --- /dev/null +++ b/src/usr/local/www/firewall_shaper_wizards.php @@ -0,0 +1,151 @@ +<?php +/* $Id$ */ +/* + firewall_shaper_wizards.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * Copyright (c) 2008 Ermal Luçi + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /usr/bin/killall + pfSense_MODULE: shaper +*/ + +##|+PRIV +##|*IDENT=page-firewall-trafficshaper-wizard +##|*NAME=Firewall: Traffic Shaper: Wizard page +##|*DESCR=Allow access to the 'Firewall: Traffic Shaper: Wizard' page. +##|*MATCH=firewall_shaper_wizards.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("util.inc"); + +if($_GET['reset'] != "") { + sigkillbyname('pfctl', SIGKILL); + exit; +} + +if ($_POST['apply']) { + write_config(); + + $retval = 0; + /* Setup pf rules since the user may have changed the optimization value */ + $retval = filter_configure(); + $savemsg = get_std_save_message($retval); + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message($retval); + } else { + $savemsg = $retval; + } + + /* reset rrd queues */ + unlink_if_exists("/var/db/rrd/*queuedrops.rrd"); + unlink_if_exists("/var/db/rrd/*queues.rrd"); + enable_rrd_graphing(); + + clear_subsystem_dirty('shaper'); +} + +$pgtitle = array(gettext("Firewall"), gettext("Traffic Shaper"), gettext("Wizards")); +$shortcut_section = "trafficshaper"; + +$wizards = array( + gettext("Multiple Lan/Wan") => "traffic_shaper_wizard_multi_all.xml", + gettext("Dedicated Links") => "traffic_shaper_wizard_dedicated.xml", +); + +$closehead = false; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +$tab_array = array(); +$tab_array[] = array(gettext("By Interface"), false, "firewall_shaper.php"); +$tab_array[] = array(gettext("By Queue"), false, "firewall_shaper_queues.php"); +$tab_array[] = array(gettext("Limiter"), false, "firewall_shaper_vinterface.php"); +$tab_array[] = array(gettext("Layer7"), false, "firewall_shaper_layer7.php"); +$tab_array[] = array(gettext("Wizards"), true, "firewall_shaper_wizards.php"); +display_top_tabs($tab_array); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('shaper')) + print_info_box_np(gettext("The traffic shaper configuration has been changed.") . "<br />" . gettext("You must apply the changes in order for them to take effect.")); + +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Traffic Shaper Wizards')?></h2></div> + <div class="panel-body"> + <dl class="dl-horizontal responsive"> +<?php +foreach ($wizards as $key => $wizard): +?> + <dt> + <?=$key?> + </dt> + <dd> + <?='<a href="wizard.php?xml=' . $wizard . '">' . $wizard . '</a>'?> + </dd> +<?php +endforeach; +?> + </dl> + </div> +</div> +<?php +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_virtual_ip.php b/src/usr/local/www/firewall_virtual_ip.php new file mode 100644 index 0000000..e321603 --- /dev/null +++ b/src/usr/local/www/firewall_virtual_ip.php @@ -0,0 +1,367 @@ +<?php +/* $Id$ */ +/* + firewall_virtual_ip.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * Copyright (c) 22003-2005 Manuel Kasper <mk@neon1.net> + * part of pfSense (https://www.pfsense.org/) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-firewall-virtualipaddresses +##|*NAME=Firewall: Virtual IP Addresses page +##|*DESCR=Allow access to the 'Firewall: Virtual IP Addresses' page. +##|*MATCH=firewall_virtual_ip.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +if (!is_array($config['virtualip']['vip'])) { + $config['virtualip']['vip'] = array(); +} + +$a_vip = &$config['virtualip']['vip']; + +if ($_POST) { + $pconfig = $_POST; + + if ($_POST['apply']) { + $check_carp = false; + if (file_exists("{$g['tmp_path']}/.firewall_virtual_ip.apply")) { + $toapplylist = unserialize(file_get_contents("{$g['tmp_path']}/.firewall_virtual_ip.apply")); + foreach ($toapplylist as $vid => $ovip) { + if (!empty($ovip)) { + interface_vip_bring_down($ovip); + } + if ($a_vip[$vid]) { + switch ($a_vip[$vid]['mode']) { + case "ipalias": + interface_ipalias_configure($a_vip[$vid]); + break; + case "proxyarp": + interface_proxyarp_configure($a_vip[$vid]['interface']); + break; + case "carp": + $check_carp = true; + interface_carp_configure($a_vip[$vid]); + break; + default: + break; + } + } + } + @unlink("{$g['tmp_path']}/.firewall_virtual_ip.apply"); + } + /* Before changing check #4633 */ + if ($check_carp === true && !get_carp_status()) { + set_single_sysctl("net.inet.carp.allow", "1"); + } + + $retval = 0; + $retval |= filter_configure(); + $savemsg = get_std_save_message($retval); + + clear_subsystem_dirty('vip'); + } +} + +if ($_GET['act'] == "del") { + if ($a_vip[$_GET['id']]) { + /* make sure no inbound NAT mappings reference this entry */ + if (is_array($config['nat']['rule'])) { + foreach ($config['nat']['rule'] as $rule) { + if($rule['destination']['address'] != "") { + if ($rule['destination']['address'] == $a_vip[$_GET['id']]['subnet']) { + $input_errors[] = gettext("This entry cannot be deleted because it is still referenced by at least one NAT mapping."); + break; + } + } + } + } + + /* make sure no OpenVPN server or client references this entry */ + $openvpn_types_a = array("openvpn-server" => gettext("server"), "openvpn-client" => gettext("client")); + foreach ($openvpn_types_a as $openvpn_type => $openvpn_type_text) { + if (is_array($config['openvpn'][$openvpn_type])) { + foreach ($config['openvpn'][$openvpn_type] as $openvpn) { + if ($openvpn['ipaddr'] <> "") { + if ($openvpn['ipaddr'] == $a_vip[$_GET['id']]['subnet']) { + if (strlen($openvpn['description'])) { + $openvpn_desc = $openvpn['description']; + } else { + $openvpn_desc = $openvpn['ipaddr'] . ":" . $openvpn['local_port']; + } + $input_errors[] = sprintf(gettext("This entry cannot be deleted because it is still referenced by OpenVPN %s %s."), $openvpn_type_text, $openvpn_desc); + break; + } + } + } + } + } + + if (is_ipaddrv6($a_vip[$_GET['id']]['subnet'])) { + $is_ipv6 = true; + $subnet = gen_subnetv6($a_vip[$_GET['id']]['subnet'], $a_vip[$_GET['id']]['subnet_bits']); + $if_subnet_bits = get_interface_subnetv6($a_vip[$_GET['id']]['interface']); + $if_subnet = gen_subnetv6(get_interface_ipv6($a_vip[$_GET['id']]['interface']), $if_subnet_bits); + } else { + $is_ipv6 = false; + $subnet = gen_subnet($a_vip[$_GET['id']]['subnet'], $a_vip[$_GET['id']]['subnet_bits']); + $if_subnet_bits = get_interface_subnet($a_vip[$_GET['id']]['interface']); + $if_subnet = gen_subnet(get_interface_ip($a_vip[$_GET['id']]['interface']), $if_subnet_bits); + } + + $subnet .= "/" . $a_vip[$_GET['id']]['subnet_bits']; + $if_subnet .= "/" . $if_subnet_bits; + + if (is_array($config['gateways']['gateway_item'])) { + foreach ($config['gateways']['gateway_item'] as $gateway) { + if ($a_vip[$_GET['id']]['interface'] != $gateway['interface']) { + continue; + } + if ($is_ipv6 && $gateway['ipprotocol'] == 'inet') { + continue; + } + if (!$is_ipv6 && $gateway['ipprotocol'] == 'inet6') { + continue; + } + if (ip_in_subnet($gateway['gateway'], $if_subnet)) { + continue; + } + + + if (ip_in_subnet($gateway['gateway'], $subnet)) { + $input_errors[] = gettext("This entry cannot be deleted because it is still referenced by at least one Gateway."); + break; + } + } + } + + if ($a_vip[$_GET['id']]['mode'] == "ipalias") { + $subnet = gen_subnet($a_vip[$_GET['id']]['subnet'], $a_vip[$_GET['id']]['subnet_bits']) . "/" . $a_vip[$_GET['id']]['subnet_bits']; + $found_if = false; + $found_carp = false; + $found_other_alias = false; + + if ($subnet == $if_subnet) { + $found_if = true; + } + + $vipiface = $a_vip[$_GET['id']]['interface']; + + foreach ($a_vip as $vip_id => $vip) { + if ($vip_id == $_GET['id']) { + continue; + } + + if ($vip['interface'] == $vipiface && ip_in_subnet($vip['subnet'], $subnet)) { + if ($vip['mode'] == "carp") { + $found_carp = true; + } else if ($vip['mode'] == "ipalias") { + $found_other_alias = true; + } + } + } + + if ($found_carp === true && $found_other_alias === false && $found_if === false) { + $input_errors[] = gettext("This entry cannot be deleted because it is still referenced by a CARP IP with the description") . " {$vip['descr']}."; + } + } else if ($a_vip[$_GET['id']]['mode'] == "carp") { + $vipiface = "{$a_vip[$_GET['id']]['interface']}_vip{$a_vip[$_GET['id']]['vhid']}"; + foreach ($a_vip as $vip) { + if ($vipiface == $vip['interface'] && $vip['mode'] == "ipalias") { + $input_errors[] = gettext("This entry cannot be deleted because it is still referenced by an IP alias entry with the description") . " {$vip['descr']}."; + } + } + } + + if (!$input_errors) { + if (!session_id()) { + session_start(); + } + $user = getUserEntry($_SESSION['Username']); + + if (is_array($user) && userHasPrivilege($user, "user-config-readonly")) { + header("Location: firewall_virtual_ip.php"); + exit; + } + + session_commit(); + + // Special case since every proxyarp vip is handled by the same daemon. + if ($a_vip[$_GET['id']]['mode'] == "proxyarp") { + $viface = $a_vip[$_GET['id']]['interface']; + unset($a_vip[$_GET['id']]); + interface_proxyarp_configure($viface); + } else { + interface_vip_bring_down($a_vip[$_GET['id']]); + unset($a_vip[$_GET['id']]); + } + if (count($config['virtualip']['vip']) == 0) { + unset($config['virtualip']['vip']); + } + write_config(); + header("Location: firewall_virtual_ip.php"); + exit; + } + } +} else if ($_GET['changes'] == "mods" && is_numericint($_GET['id'])) { + $id = $_GET['id']; +} + +$types = array('proxyarp' => 'Proxy ARP', + 'carp' => 'CARP', + 'other' => 'Other', + 'ipalias' => 'IP Alias' + ); + +$pgtitle = array(gettext("Firewall"),gettext("Virtual IP Addresses")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +else if ($savemsg) + print_info_box($savemsg, 'success'); +else if (is_subsystem_dirty('vip')) + print_info_box_np(gettext("The VIP configuration has been changed.")."<br />".gettext("You must apply the changes in order for them to take effect.")); + +/* active tabs */ +$tab_array = array(); +$tab_array[] = array(gettext("Virtual IPs"), true, "firewall_virtual_ip.php"); +$tab_array[] = array(gettext("CARP Settings"), false, "system_hasync.php"); +display_top_tabs($tab_array); + +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Virtual IP Address')?></h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Virtual IP address")?></th> + <th><?=gettext("Interface")?></th> + <th><?=gettext("Type")?></th> + <th><?=gettext("Description")?></th> + <th><!--Buttons--></th> + </tr> + </thead> + <tbody> +<?php +$interfaces = get_configured_interface_with_descr(false, true); +$carplist = get_configured_carp_interface_list(); + +foreach ($carplist as $cif => $carpip) + $interfaces[$cif] = $carpip." (".get_vip_descr($carpip).")"; + +$interfaces['lo0'] = "Localhost"; + +$i = 0; +foreach ($a_vip as $vipent): + if( $vipent['subnet'] != "" or $vipent['range'] != "" or + $vipent['subnet_bits'] != "" or (isset($vipent['range']['from']) && $vipent['range']['from'] != "")): +?> + <tr> + <td> +<?php + if (($vipent['type'] == "single") || ($vipent['type'] == "network")) + if($vipent['subnet_bits']) + print("{$vipent['subnet']}/{$vipent['subnet_bits']}"); + + if ($vipent['type'] == "range") + print("{$vipent['range']['from']}-{$vipent['range']['to']}"); + + if($vipent['mode'] == "carp") + print(" (vhid: {$vipent['vhid']})"); +?> + </td> + <td> + <?=htmlspecialchars($interfaces[$vipent['interface']])?> + </td> + <td> + <?=$types[$vipent['mode']]?> + </td> + <td> + <?=htmlspecialchars($vipent['descr'])?> + </td> + <td> + <a href="firewall_virtual_ip_edit.php?id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext('Edit')?></a> + <a href="firewall_virtual_ip.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger"><?=gettext('Delete')?></a> + </td> + </tr> +<?php + endif; + $i++; +endforeach; +?> + </tbody> + </table> + </div> +</div> + +<nav class="action-buttons"> + <a href="firewall_virtual_ip_edit.php" class="btn btn-sm btn-success"><?=gettext('Add Virtual IP')?></a> +</nav> + +<?php + +print_info_box(gettext('The virtual IP addresses defined on this page may be used in ') . '<a href="firewall_nat.php">' . gettext('NAT') . '</a>' . gettext(' mappings.') . '<br />' . + gettext('You can check the status of your CARP Virtual IPs and interfaces ') . '<a href="carp_status.php">' . gettext('here') . '</a>'); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/firewall_virtual_ip_edit.php b/src/usr/local/www/firewall_virtual_ip_edit.php new file mode 100644 index 0000000..caac6d3 --- /dev/null +++ b/src/usr/local/www/firewall_virtual_ip_edit.php @@ -0,0 +1,561 @@ +<?php +/* $Id$ */ +/* + + firewall_virtual_ip_edit.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004 Scott Ullrich + * Copyright (c) 2005 Bill Marquette <bill.marquette@gmail.com> + * Originally part of pfSense (https://www.pfsense.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-firewall-virtualipaddress-edit +##|*NAME=Firewall: Virtual IP Address: Edit page +##|*DESCR=Allow access to the 'Firewall: Virtual IP Address: Edit' page. +##|*MATCH=firewall_virtual_ip_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("filter.inc"); +require("shaper.inc"); + +if (!is_array($config['virtualip']['vip'])) { + $config['virtualip']['vip'] = array(); +} + +$a_vip = &$config['virtualip']['vip']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +function return_first_two_octets($ip) { + $ip_split = explode(".", $ip); + return $ip_split[0] . "." . $ip_split[1]; +} + +function find_last_used_vhid() { + global $config, $g; + + $vhid = 0; + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['vhid'] > $vhid) { + $vhid = $vip['vhid']; + } + } + + return $vhid; +} + +if (isset($id) && $a_vip[$id]) { + $pconfig['mode'] = $a_vip[$id]['mode']; + $pconfig['vhid'] = $a_vip[$id]['vhid']; + $pconfig['advskew'] = $a_vip[$id]['advskew']; + $pconfig['advbase'] = $a_vip[$id]['advbase']; + $pconfig['password'] = $a_vip[$id]['password']; + $pconfig['range'] = $a_vip[$id]['range']; + $pconfig['subnet'] = $a_vip[$id]['subnet']; + $pconfig['subnet_bits'] = $a_vip[$id]['subnet_bits']; + $pconfig['noexpand'] = $a_vip[$id]['noexpand']; + $pconfig['descr'] = $a_vip[$id]['descr']; + $pconfig['type'] = $a_vip[$id]['type']; + $pconfig['interface'] = $a_vip[$id]['interface']; + $pconfig['uniqid'] = $a_vip[$id]['interface']; +} else { + $lastvhid = find_last_used_vhid(); + $lastvhid++; + $pconfig['vhid'] = $lastvhid; + $pconfig['uniqid'] = uniqid(); +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "mode"); + $reqdfieldsn = array(gettext("Type")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ($_POST['subnet']) { + $_POST['subnet'] = trim($_POST['subnet']); + } + + if ($_POST['subnet']) { + if (!is_ipaddr($_POST['subnet'])) { + $input_errors[] = gettext("A valid IP address must be specified."); + } else { + if (isset($id) && isset($a_vip[$id])) { + $ignore_if = $a_vip[$id]['interface']; + $ignore_mode = $a_vip[$id]['mode']; + if (isset($a_vip[$id]['vhid'])) { + $ignore_vhid = $a_vip[$id]['vhid']; + } + } else { + $ignore_if = $_POST['interface']; + $ignore_mode = $_POST['mode']; + } + + if (!isset($ignore_vhid)) { + $ignore_vhid = $_POST['vhid']; + } + + if ($ignore_mode == 'carp') { + $ignore_if .= "_vip{$ignore_vhid}"; + } else { + $ignore_if .= "_virtualip{$id}"; + } + + if (is_ipaddr_configured($_POST['subnet'], $ignore_if)) { + $input_errors[] = gettext("This IP address is being used by another interface or VIP."); + } + + unset($ignore_if, $ignore_mode); + } + } + + $natiflist = get_configured_interface_with_descr(); + foreach ($natiflist as $natif => $natdescr) { + if ($_POST['interface'] == $natif && (empty($config['interfaces'][$natif]['ipaddr']) && empty($config['interfaces'][$natif]['ipaddrv6']))) { + $input_errors[] = gettext("The interface chosen for the VIP has no IPv4 or IPv6 address configured so it cannot be used as a parent for the VIP."); + } + } + + /* ipalias and carp should not use network or broadcast address */ + if ($_POST['mode'] == "ipalias" || $_POST['mode'] == "carp") { + if (is_ipaddrv4($_POST['subnet']) && $_POST['subnet_bits'] != "32") { + $network_addr = gen_subnet($_POST['subnet'], $_POST['subnet_bits']); + $broadcast_addr = gen_subnet_max($_POST['subnet'], $_POST['subnet_bits']); + } else if (is_ipaddrv6($_POST['subnet']) && $_POST['subnet_bits'] != "128") { + $network_addr = gen_subnetv6($_POST['subnet'], $_POST['subnet_bits']); + $broadcast_addr = gen_subnetv6_max($_POST['subnet'], $_POST['subnet_bits']); + } + + if (isset($network_addr) && $_POST['subnet'] == $network_addr) { + $input_errors[] = gettext("You cannot use the network address for this VIP"); + } else if (isset($broadcast_addr) && $_POST['subnet'] == $broadcast_addr) { + $input_errors[] = gettext("You cannot use the broadcast address for this VIP"); + } + } + + /* make sure new ip is within the subnet of a valid ip + * on one of our interfaces (wan, lan optX) + */ + switch ($_POST['mode']) { + case 'carp': + /* verify against reusage of vhids */ + $idtracker = 0; + foreach($config['virtualip']['vip'] as $vip) { + if($vip['vhid'] == $_POST['vhid'] && $vip['interface'] == $_POST['interface'] && $idtracker != $id) + $input_errors[] = sprintf(gettext("VHID %s is already in use on interface %s. Pick a unique number on this interface."),$_POST['vhid'], convert_friendly_interface_to_friendly_descr($_POST['interface'])); + $idtracker++; + } + + if (empty($_POST['password'])) + $input_errors[] = gettext("You must specify a CARP password that is shared between the two VHID members."); + + if ($_POST['interface'] == 'lo0') + $input_errors[] = gettext("For this type of vip localhost is not allowed."); + else if (strpos($_POST['interface'], '_vip')) + $input_errors[] = gettext("A CARP parent interface can only be used with IP Alias type Virtual IPs."); + + break; + case 'ipalias': + if (strstr($_POST['interface'], "_vip")) { + if (is_ipaddrv4($_POST['subnet'])) { + $parent_ip = get_interface_ip($_POST['interface']); + $parent_sn = get_interface_subnet($_POST['interface']); + $subnet = gen_subnet($parent_ip, $parent_sn); + } else if (is_ipaddrv6($_POST['subnet'])) { + $parent_ip = get_interface_ipv6($_POST['interface']); + $parent_sn = get_interface_subnetv6($_POST['interface']); + $subnet = gen_subnetv6($parent_ip, $parent_sn); + } + + if (isset($parent_ip) && !ip_in_subnet($_POST['subnet'], "{$subnet}/{$parent_sn}") && + !ip_in_interface_alias_subnet(link_carp_interface_to_parent($_POST['interface']), $_POST['subnet'])) { + $cannot_find = $_POST['subnet'] . "/" . $_POST['subnet_bits'] ; + $input_errors[] = sprintf(gettext("Sorry, we could not locate an interface with a matching subnet for %s. Please add an IP alias in this subnet on this interface."),$cannot_find); + } + + unset($parent_ip, $parent_sn, $subnet); + } + + break; + default: + if ($_POST['interface'] == 'lo0') + $input_errors[] = gettext("For this type of vip localhost is not allowed."); + else if (strpos($_POST['interface'], '_vip')) + $input_errors[] = gettext("A CARP parent interface can only be used with IP Alias type Virtual IPs."); + + break; + } + + if (!$input_errors) { + $vipent = array(); + + $vipent['mode'] = $_POST['mode']; + $vipent['interface'] = $_POST['interface']; + + /* ProxyARP specific fields */ + if ($_POST['mode'] === "proxyarp") { + if ($_POST['type'] == "range") { + $vipent['range']['from'] = $_POST['range_from']; + $vipent['range']['to'] = $_POST['range_to']; + + } + + $vipent['noexpand'] = isset($_POST['noexpand']); + } + + /* CARP specific fields */ + if ($_POST['mode'] === "carp") { + $vipent['vhid'] = $_POST['vhid']; + $vipent['vhid'] = $_POST['uinqid']; + $vipent['advskew'] = $_POST['advskew']; + $vipent['advbase'] = $_POST['advbase']; + $vipent['password'] = $_POST['password']; + } + + /* Common fields */ + $vipent['descr'] = $_POST['descr']; + if (isset($_POST['type'])) { + $vipent['type'] = $_POST['type']; + } else { + $vipent['type'] = "single"; + } + + if ($vipent['type'] == "single" || $vipent['type'] == "network") { + if (!isset($_POST['subnet_bits'])) { + $vipent['subnet_bits'] = "32"; + } else { + $vipent['subnet_bits'] = $_POST['subnet_bits']; + } + + $vipent['subnet'] = $_POST['subnet']; + } + + if (!isset($id)) { + $id = count($a_vip); + } + if (file_exists("{$g['tmp_path']}/.firewall_virtual_ip.apply")) { + $toapplylist = unserialize(file_get_contents("{$g['tmp_path']}/.firewall_virtual_ip.apply")); + } else { + $toapplylist = array(); + } + + $toapplylist[$id] = $a_vip[$id]; + + if (!empty($a_vip[$id])) { + /* modify all virtual IP rules with this address */ + for ($i = 0; isset($config['nat']['rule'][$i]); $i++) { + if ($config['nat']['rule'][$i]['destination']['address'] == $a_vip[$id]['subnet']) { + $config['nat']['rule'][$i]['destination']['address'] = $vipent['subnet']; + } + } + } + + $a_vip[$id] = $vipent; + + if (write_config()) { + mark_subsystem_dirty('vip'); + file_put_contents("{$g['tmp_path']}/.firewall_virtual_ip.apply", serialize($toapplylist)); + } + + header("Location: firewall_virtual_ip.php"); + exit; + } +} + +$ipaliashelp = gettext('The mask must be the network\'s subnet mask. It does not specify a CIDR range.'); +$proxyarphelp = gettext('Enter a CIDR block of proxy ARP addresses.'); + +$pgtitle = array(gettext("Firewall"),gettext("Virtual IP Address"),gettext("Edit")); +include("head.inc"); + +function build_if_list() { + $list = array(); + + $interfaces = get_configured_interface_with_descr(false, true); + $carplist = get_configured_carp_interface_list(); + + foreach ($carplist as $cif => $carpip) + $interfaces[$cif] = $carpip . ' (' . get_vip_descr($carpip) . ')'; + + $interfaces['lo0'] = 'Localhost'; + + return($interfaces); +} + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Edit Virtual IP'); + +$group = new Form_Group('Type'); + +$group->add(new Form_Checkbox( + 'mode', + null, + 'IP Alias', + ($pconfig['mode'] == "ipalias"), + 'ipalias' +))->displayAsRadio(); + +$group->add(new Form_Checkbox( + 'mode', + null, + 'CARP', + ($pconfig['mode'] == "carp"), + 'carp' +))->displayAsRadio(); + +$group->add(new Form_Checkbox( + 'mode', + null, + 'Proxy ARP', + ($pconfig['mode'] == "proxyarp"), + 'proxyarp' +))->displayAsRadio(); + +$group->add(new Form_Checkbox( + 'mode', + null, + 'Other', + ($pconfig['mode'] == "other"), + 'other' +))->displayAsRadio(); + +$section->add($group); + +$section->addInput(new Form_Select( + 'interface', + 'Interface', + $pconfig['interface'], + build_if_list() +)); + +$section->addInput(new Form_Select( + 'type', + 'Address type', + ((!$pconfig['range'] && $pconfig['subnet_bits'] == 32) || (!isset($pconfig['subnet']))) ? 'single':'network', + array( + 'single' => 'Single address', + 'network' => 'Network' + ) +))->addClass('typesel'); + +$section->addInput(new Form_IpAddress( + 'subnet', + 'Address(es)', + $pconfig['subnet'] +))->addMask('subnet_bits', $pconfig['subnet_bits'])->setHelp('<span id="address_note"></span>'); + +$section->addInput(new Form_Checkbox( + 'noexpand', + 'Expansion', + 'Disable expansion of this entry into IPs on NAT lists (e.g. 192.168.1.0/24 expands to 256 entries.) ', + isset($pconfig['noexpand']) +)); + +$section->addInput(new Form_Input( + 'password', + 'Virtual IP Password', + 'password', + $pconfig['password'] +))->setHelp('Enter the VHID group password.'); + +$section->addInput(new Form_Select( + 'vhid', + 'VHID Group', + $pconfig['vhid'], + array_combine(range(1, 255, 1), range(1, 255, 1)) +))->setHelp('Enter the VHID group that the machines will share'); + +$group = new Form_Group('Advertising frequency'); +$group->add(new Form_Select( + 'advbase', + 'Base', + $pconfig['advbase'], + array_combine(range(1, 254, 1), range(1, 254, 1)) +))->setHelp('Base'); + +$group->add(new Form_Select( + 'advskew', + 'Skew', + $pconfig['advskew'], + array_combine(range(0, 254, 1), range(0, 254, 1)) +))->setHelp('Skew'); + +$group->setHelp('The frequency that this machine will advertise. 0 means usually master. Otherwise the lowest combination of both values in the cluster determines the master.'); + +$section->add($group); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +if (isset($id) && $a_vip[$id]){ + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$section->addInput(new Form_Input( + 'uniqid', + null, + 'hidden', + $pconfig['uniqid'] +)); + +$form->add($section); + +print($form); + +print_info_box(gettext("Proxy ARP and Other type Virtual IPs cannot be bound to by anything running on the firewall, such as IPsec, OpenVPN, etc. Use a CARP or IP Alias type address for these types.") . '<br />' . + sprintf(gettext("For more information on CARP and the above values, visit the OpenBSD %s"), '<a href="http://www.openbsd.org/faq/pf/carp.html">CARP FAQ"</a>')); +?> + +<script> +//<![CDATA[ +events.push(function(){ + + // Hides the <div> in which the specified checkbox lives so that the checkbox, its label and help text are hidden + function hideCheckbox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // Hide/show input elements depending on the 'mode' radio button setting + function check_mode() { + var mode = $("input[name=mode]:checked").val(); + + disableInput('vhid', true); + disableInput('advbase', true); + disableInput('advskew', true); + disableInput('subnet_bits', true); + disableInput('type', true); + disableInput('password', true); + hideCheckbox('noexpand', true); + + if(mode == 'ipalias') { + $('#address_note').html("<?=$ipaliashelp?>"); + disableInput('subnet_bits', false); + $('#type').val('single'); + + } + else if(mode == 'carp') { + $('#address_note').html("<?=$ipaliashelp?>"); + disableInput('vhid', false); + disableInput('advbase', false); + disableInput('advskew', false); + disableInput('subnet_bits', false); + disableInput('password', false); + $('#type').val('single'); + } + else if(mode == 'proxyarp') { + $('#address_note').html("<?=$proxyarphelp?>"); + disableInput('type', false); + disableInput('subnet_bits', $('#type').val() == 'single'); + } + else { + $('#address_note').html(''); + disableInput('type', false); + } + } + + // When radio buttons are clicked . . + $('input:radio[name=mode]').click(function() { + check_mode(); + }); + + // On clicking the address type selector + $('#type').on('change', function() { + check_mode(); + hideCheckbox('noexpand', (this.value == 'single')); + }); + + // On initial page load + check_mode(); +}); +//]]> +</script> + +<?php + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/foot.inc b/src/usr/local/www/foot.inc new file mode 100755 index 0000000..f5108dd --- /dev/null +++ b/src/usr/local/www/foot.inc @@ -0,0 +1,27 @@ + <!-- + pfSense_MODULE: footer + --> +<?php +/* + * We put jquery in the footer; it is the preferred position for speed + * and helps developers to use events[] instead of executing scripts directly. + */ +?> + </div> + + <footer class="footer"> + <div class="container"> + <p class="text-muted"> + <a target="_blank" href="<?=$g['product_website_footer']?>"><?=$g['product_name']?></a> is © + <?=$g['product_copyright_years']?> by <a href="<?=$g['product_copyright_url']?>" class="tblnk"><?=$g['product_copyright']?></a>. All Rights Reserved. + [<a href="/license.php" class="tblnk">view license</a>] + </p> + </div> + </footer> + + <script src="/jquery/jquery-1.11.2.min.js"></script> + <script src="/jquery/jquery-ui-1.11.2.min.js"></script> + <script src="/bootstrap/js/bootstrap.min.js"></script> + <script src="/jquery/pfSense.js"></script> +</body> +</html>
\ No newline at end of file diff --git a/src/usr/local/www/getserviceproviders.php b/src/usr/local/www/getserviceproviders.php new file mode 100644 index 0000000..c38d356 --- /dev/null +++ b/src/usr/local/www/getserviceproviders.php @@ -0,0 +1,178 @@ +<?php +/* + getserviceproviders.php +*/ + +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2010 Vinicius Coque <vinicius.coque@bluepex.com> + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: ajax +*/ + +##|+PRIV +##|*IDENT=page-getserviceproviders +##|*NAME=AJAX: Get Service Providers +##|*DESCR=Allow access to the 'AJAX: Service Providers' page. +##|*MATCH=getserviceproviders.php* +##|-PRIV +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); + +$serviceproviders_xml = "/usr/local/share/mobile-broadband-provider-info/serviceproviders.xml"; +$serviceproviders_contents = file_get_contents($serviceproviders_xml); +$serviceproviders_attr = xml2array($serviceproviders_contents, 1, "attr"); + +$serviceproviders = &$serviceproviders_attr['serviceproviders']['country']; + +function get_country_providers($country) { + global $serviceproviders; + foreach ($serviceproviders as $sp) { + if ($sp['attr']['code'] == strtolower($country)) { + return is_array($sp['provider'][0]) ? $sp['provider'] : array($sp['provider']); + } + } + return $provider_list; +} + +function country_list() { + global $serviceproviders; + $country_list = get_country_name("ALL"); + foreach ($serviceproviders as $sp) { + foreach ($country_list as $country) { + if (strtoupper($sp['attr']['code']) == $country['code']) { + echo $country['name'] . ":" . $country['code'] . "\n"; + } + } + } +} + +function providers_list($country) { + $serviceproviders = get_country_providers($country); + foreach ($serviceproviders as $sp) { + echo $sp['name']['value'] . "\n"; + } +} + +function provider_plan_data($country, $provider, $connection) { + header("Content-type: application/xml;"); + echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + echo "<connection>\n"; + $serviceproviders = get_country_providers($country); + foreach ($serviceproviders as $sp) { + if (strtolower($sp['name']['value']) == strtolower($provider)) { + if (strtoupper($connection) == "CDMA") { + $conndata = $sp['cdma']; + } else { + if (!is_array($sp['gsm']['apn'][0])) { + $conndata = $sp['gsm']['apn']; + } else { + foreach ($sp['gsm']['apn'] as $apn) { + if ($apn['attr']['value'] == $connection) { + $conndata = $apn; + break; + } + } + } + } + if (is_array($conndata)) { + echo "<apn>" . $connection . "</apn>\n"; + echo "<username>" . $conndata['username']['value'] . "</username>\n"; + echo "<password>" . $conndata['password']['value'] . "</password>\n"; + + $dns_arr = is_array($conndata['dns'][0]) ? $conndata['dns'] : array($conndata['dns']); + foreach ($dns_arr as $dns) { + echo '<dns>' . $dns['value'] . "</dns>\n"; + } + } + break; + } + } + echo "</connection>"; +} + +function provider_plans_list($country, $provider) { + $serviceproviders = get_country_providers($country); + foreach ($serviceproviders as $sp) { + if (strtolower($sp['name']['value']) == strtolower($provider)) { + if (array_key_exists('gsm', $sp)) { + if (array_key_exists('attr', $sp['gsm']['apn'])) { + $name = ($sp['gsm']['apn']['name'] ? $sp['gsm']['apn']['name'] : $sp['name']['value']); + echo $name . ":" . $sp['gsm']['apn']['attr']['value']; + } else { + foreach ($sp['gsm']['apn'] as $apn_info) { + $name = ($apn_info['name']['value'] ? $apn_info['name']['value'] : $apn_info['gsm']['apn']['name']); + echo $name . ":" . $apn_info['attr']['value'] . "\n"; + } + } + } + if (array_key_exists('cdma', $sp)) { + $name = $sp['cdma']['name']['value'] ? $sp['cdma']['name']['value']:$sp['name']['value']; + echo $name . ":" . "CDMA"; + } + } + } +} + +$_GET_OR_POST = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET; + +if (isset($_GET_OR_POST['country']) && !isset($_GET_OR_POST['provider'])) { + providers_list($_GET_OR_POST['country']); +} elseif (isset($_GET_OR_POST['country']) && isset($_GET_OR_POST['provider'])) { + if (isset($_GET_OR_POST['plan'])) { + provider_plan_data($_GET_OR_POST['country'], $_GET_OR_POST['provider'], $_GET_OR_POST['plan']); + } else { + provider_plans_list($_GET_OR_POST['country'], $_GET_OR_POST['provider']); + } +} else { + country_list(); +} +?> diff --git a/src/usr/local/www/getstats.php b/src/usr/local/www/getstats.php new file mode 100644 index 0000000..42f61bf --- /dev/null +++ b/src/usr/local/www/getstats.php @@ -0,0 +1,77 @@ +<?php +/* + getstats.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2009 Bill Marquette + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: ajax +*/ + +##|+PRIV +##|*IDENT=page-getstats +##|*NAME=AJAX: Get Stats +##|*DESCR=Allow access to the 'AJAX: Get Stats' page. +##|*MATCH=getstats.php* +##|-PRIV + +header("Last-Modified: " . gmdate("D, j M Y H:i:s") . " GMT"); +header("Expires: " . gmdate("D, j M Y H:i:s", time()) . " GMT"); +header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP/1.1 +header("Pragma: no-cache"); // HTTP/1.0 + +require_once("guiconfig.inc"); +include_once("includes/functions.inc.php"); + +echo get_stats(); + +?> diff --git a/src/usr/local/www/graph.php b/src/usr/local/www/graph.php new file mode 100755 index 0000000..f9ccb98 --- /dev/null +++ b/src/usr/local/www/graph.php @@ -0,0 +1,433 @@ +<?php +/* + graph.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004-2006 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net> + * and Jonathan Watt <jwatt@jwatt.org> + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: graph +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-interfacetraffic +##|*NAME=Diagnostics: Interface Traffic page +##|*DESCR=Allow access to the 'Diagnostics: Interface Traffic' page. +##|*MATCH=graph.php* +##|-PRIV + +require("globals.inc"); +require("guiconfig.inc"); + +header("Last-Modified: " . gmdate("D, j M Y H:i:s") . " GMT"); +header("Expires: " . gmdate("D, j M Y H:i:s", time()) . " GMT"); +header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP/1.1 +header("Pragma: no-cache"); // HTTP/1.0 +header("Content-type: image/svg+xml"); + +/********** HTTP GET Based Conf ***********/ +$ifnum = @$_GET["ifnum"]; // BSD / SNMP interface name / number +$ifnum = get_real_interface($ifnum); +$ifname = @$_GET["ifname"]?$_GET["ifname"]:"Interface $ifnum"; //Interface name that will be showed on top right of graph + +/********* Other conf *******/ +if (isset($config["widgets"]["trafficgraphs"]["scale_type"])) { + $scale_type = $config["widgets"]["trafficgraphs"]["scale_type"]; +} else { + $scale_type = "up"; +} + +$nb_plot=120; //NB plot in graph +if ($_GET["timeint"]) { + $time_interval = $_GET["timeint"]; //Refresh time Interval +} else { + $time_interval = 3; +} + +if ($_GET["initdelay"]) { + $init_delay = $_GET["initdelay"]; //Initial Delay +} else { + $init_delay = 3; +} + +//SVG attributes +$attribs['axis']='fill="black" stroke="black"'; +$attribs['in']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="7"'; +$attribs['out']='fill="#000000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="7"'; +$attribs['graph_in']='fill="none" stroke="#FF0000" stroke-opacity="0.8"'; +$attribs['graph_out']='fill="none" stroke="#000000" stroke-opacity="0.8"'; +$attribs['legend']='fill="black" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4"'; +$attribs['graphname']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="8"'; +$attribs['grid_txt']='fill="gray" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="6"'; +$attribs['grid']='stroke="gray" stroke-opacity="0.5"'; +$attribs['switch_unit']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4" text-decoration="underline"'; +$attribs['switch_scale']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4" text-decoration="underline"'; +$attribs['error']='fill="blue" font-family="Arial" font-size="4"'; +$attribs['collect_initial']='fill="gray" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4"'; + +//Error text if we cannot fetch data : depends on which method is used +$error_text = "Cannot get data about interface " . htmlspecialchars($ifnum); + +$height=100; //SVG internal height : do not modify +$width=200; //SVG internal width : do not modify + +$fetch_link = "ifstats.php?if=" . htmlspecialchars($ifnum); + +/* check for custom theme colors */ +if (file_exists("/usr/local/www/themes/{$g['theme']}/graph.php")) { + $themetxt = file_get_contents("/usr/local/www/themes/{$g['theme']}/graph.php"); + eval($themetxt); +} + +/********* Graph DATA **************/ +print('<?xml version="1.0" encoding="UTF-8"?>' . "\n");?> +<svg width="100%" height="100%" viewBox="0 0 <?=$width?> <?=$height?>" preserveAspectRatio="none" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="init(evt)"> + <g id="graph"> + <rect id="bg" x1="0" y1="0" width="100%" height="100%" fill="white"/> + <line id="axis_x" x1="0" y1="0" x2="0" y2="100%" <?=$attribs['axis']?>/> + <line id="axis_y" x1="0" y1="100%" x2="100%" y2="100%" <?=$attribs['axis']?>/> + <path id="graph_out" d="M0 <?=$height?> L 0 <?=$height?>" <?=$attribs['graph_out']?>/> + <path id="graph_in" d="M0 <?=$height?> L 0 <?=$height?>" <?=$attribs['graph_in']?>/> + <path id="grid" d="M0 <?=$height/4*1?> L <?=$width?> <?=$height/4*1?> M0 <?=$height/4*2?> L <?=$width?> <?=$height/4*2?> M0 <?=$height/4*3?> L <?=$width?> <?=$height/4*3?>" <?=$attribs['grid']?>/> + <text id="grid_txt1" x="<?=$width?>" y="<?=$height/4*1?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text> + <text id="grid_txt2" x="<?=$width?>" y="<?=$height/4*2?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text> + <text id="grid_txt3" x="<?=$width?>" y="<?=$height/4*3?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text> + <text id="graph_in_lbl" x="5" y="8" <?=$attribs['in']?>><?=gettext("In"); ?></text> + <text id="graph_out_lbl" x="5" y="16" <?=$attribs['out']?>><?=gettext("Out"); ?></text> + <text id="graph_in_txt" x="20" y="8" <?=$attribs['in']?>> </text> + <text id="graph_out_txt" x="20" y="16" <?=$attribs['out']?>> </text> + <text id="ifname" x="<?=$width?>" y="8" <?=$attribs['graphname']?> text-anchor="end"><?=htmlspecialchars($ifname)?></text> + <text id="switch_unit" x="<?=$width*0.55?>" y="5" <?=$attribs['switch_unit']?>><?=gettext("Switch to bytes/s"); ?></text> + <text id="switch_scale" x="<?=$width*0.55?>" y="11" <?=$attribs['switch_scale']?>><?=gettext("AutoScale"); ?> (<?=$scale_type?>)</text> + <text id="date" x="<?=$width*0.33?>" y="5" <?=$attribs['legend']?>> </text> + <text id="time" x="<?=$width*0.33?>" y="11" <?=$attribs['legend']?>> </text> + <text id="graphlast" x="<?=$width*0.55?>" y="17" <?=$attribs['legend']?>><?=gettext("Graph shows last"); ?> <?=$time_interval*$nb_plot?> <?=gettext("seconds"); ?></text> + <polygon id="axis_arrow_x" <?=$attribs['axis']?> points="<?=($width) . "," . ($height)?> <?=($width-2) . "," . ($height-2)?> <?=($width-2) . "," . $height?>"/> + <text id="error" x="<?=$width*0.5?>" y="<?=$height*0.5?>" visibility="hidden" <?=$attribs['error']?> text-anchor="middle"><?=$error_text?></text> + <text id="collect_initial" x="<?=$width*0.5?>" y="<?=$height*0.5?>" visibility="hidden" <?=$attribs['collect_initial']?> text-anchor="middle"><?=gettext("Collecting initial data, please wait"); ?>...</text> + </g> + <script type="text/ecmascript"> + <![CDATA[ + +/** + * getURL is a proprietary Adobe function, but it's simplicity has made it very + * popular. If getURL is undefined we spin our own by wrapping XMLHttpRequest. + */ +if (typeof getURL == 'undefined') { + getURL = function(url, callback) { + if (!url) { + throw '<?=gettext("No URL for getURL"); ?>'; + } + + try { + if (typeof callback.operationComplete == 'function') { + callback = callback.operationComplete; + } + } catch (e) {} + if (typeof callback != 'function') { + throw '<?=gettext("No callback function for getURL"); ?>'; + } + + var http_request = null; + if (typeof XMLHttpRequest != 'undefined') { + http_request = new XMLHttpRequest(); + } else if (typeof ActiveXObject != 'undefined') { + try { + http_request = new ActiveXObject('Msxml2.XMLHTTP'); + } catch (e) { + try { + http_request = new ActiveXObject('Microsoft.XMLHTTP'); + } catch (e) {} + } + } + if (!http_request) { + throw '<?=gettext("Both getURL and XMLHttpRequest are undefined"); ?>'; + } + + http_request.onreadystatechange = function() { + if (http_request.readyState == 4) { + callback( { success : true, + content : http_request.responseText, + contentType : http_request.getResponseHeader("Content-Type") } ); + } + } + http_request.open('GET', url, true); + http_request.send(null); + } +} + +var SVGDoc = null; +var last_ifin = 0; +var last_ifout = 0; +var last_ugmt = 0; +var max = 0; +var plot_in = new Array(); +var plot_out = new Array(); + +var max_num_points = <?=$nb_plot?>; // maximum number of plot data points +var step = <?=$width?> / max_num_points ; +var unit = 'bits'; +var scale_type = '<?=$scale_type?>'; + +function init(evt) { + SVGDoc = evt.target.ownerDocument; + SVGDoc.getElementById("switch_unit").addEventListener("mousedown", switch_unit, false); + SVGDoc.getElementById("switch_scale").addEventListener("mousedown", switch_scale, false); + + fetch_data(); +} + +function switch_unit(event) { + SVGDoc.getElementById('switch_unit').firstChild.data = '<?=gettext("Switch to"); ?> ' + unit + '/s'; + unit = (unit == 'bits') ? 'bytes' : 'bits'; +} + +function switch_scale(event) { + scale_type = (scale_type == 'up') ? '<?=gettext("follow"); ?>' : '<?=gettext("up"); ?>'; + SVGDoc.getElementById('switch_scale').firstChild.data = 'AutoScale (' + scale_type + ')'; +} + +function fetch_data() { + getURL('<?=$fetch_link?>', plot_data); +} + +function plot_data(obj) { + // Show datetimelegend + var now = new Date(); + var time = LZ(now.getHours()) + ":" + LZ(now.getMinutes()) + ":" + LZ(now.getSeconds()); + SVGDoc.getElementById('time').firstChild.data = time; + var date = (now.getMonth()+1) + "/" + now.getDate() + "/" + now.getFullYear(); + SVGDoc.getElementById('date').firstChild.data = date; + + if (!obj.success) { + return handle_error(); // getURL failed to get data + } + + var t = obj.content.split("|"); + var ugmt = parseFloat(t[0]); // ugmt is an unixtimestamp style + var ifin = parseInt(t[1], 10); // number of bytes received by the interface + var ifout = parseInt(t[2], 10); // number of bytes sent by the interface + var scale; + + if (!isNumber(ifin) || !isNumber(ifout)) { + return handle_error(); + } + + var diff_ugmt = ugmt - last_ugmt; + var diff_ifin = ifin - last_ifin; + var diff_ifout = ifout - last_ifout; + + if (diff_ugmt == 0) { + diff_ugmt = 1; /* avoid division by zero */ + } + + last_ugmt = ugmt; + last_ifin = ifin; + last_ifout = ifout; + var graphTimerId = 0; + switch (plot_in.length) { + case 0: + SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'visible'); + plot_in[0] = diff_ifin / diff_ugmt; + plot_out[0] = diff_ifout / diff_ugmt; + setTimeout('fetch_data()', <?=1000*($time_interval + $init_delay)?>); + return; + case 1: + SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'hidden'); + break; + case max_num_points: + // shift plot to left if the maximum number of plot points has been reached + var i = 0; + while (i < max_num_points) { + plot_in[i] = plot_in[i+1]; + plot_out[i] = plot_out[++i]; + } + plot_in.length--; + plot_out.length--; + } + + plot_in[plot_in.length] = diff_ifin / diff_ugmt; + plot_out[plot_out.length]= diff_ifout / diff_ugmt; + var index_plot = plot_in.length - 1; + + SVGDoc.getElementById('graph_in_txt').firstChild.data = formatSpeed(plot_in[index_plot], unit); + SVGDoc.getElementById('graph_out_txt').firstChild.data = formatSpeed(plot_out[index_plot], unit); + + /* determine peak for sensible scaling */ + if (scale_type == 'up') { + if (plot_in[index_plot] > max) { + max = plot_in[index_plot]; + } + if (plot_out[index_plot] > max) { + max = plot_out[index_plot]; + } + } else if (scale_type == 'follow') { + i = 0; + max = 0; + while (i < plot_in.length) { + if (plot_in[i] > max) { + max = plot_in[i]; + } + if (plot_out[i] > max) { + max = plot_out[i]; + } + i++; + } + } + + var rmax; // max, rounded up + + if (unit == 'bits') { + /* round up max, such that + 100 kbps -> 200 kbps -> 400 kbps -> 800 kbps -> 1 Mbps -> 2 Mbps -> ... */ + rmax = 12500; + i = 0; + while (max > rmax) { + i++; + if (i && (i % 4 == 0)) { + rmax *= 1.25; + } else { + rmax *= 2; + } + } + } else { + /* round up max, such that + 10 KB/s -> 20 KB/s -> 40 KB/s -> 80 KB/s -> 100 KB/s -> 200 KB/s -> 400 KB/s -> 800 KB/s -> 1 MB/s ... */ + rmax = 10240; + i = 0; + while (max > rmax) { + i++; + if (i && (i % 4 == 0)) { + rmax *= 1.25; + } else { + rmax *= 2; + } + + if (i == 8) { + rmax *= 1.024; + } + } + } + + scale = <?=$height?> / rmax; + + /* change labels accordingly */ + SVGDoc.getElementById('grid_txt1').firstChild.data = formatSpeed(3*rmax/4, unit); + SVGDoc.getElementById('grid_txt2').firstChild.data = formatSpeed(2*rmax/4, unit); + SVGDoc.getElementById('grid_txt3').firstChild.data = formatSpeed(rmax/4, unit); + + var path_in = "M 0 " + (<?=$height?> - (plot_in[0] * scale)); + var path_out = "M 0 " + (<?=$height?> - (plot_out[0] * scale)); + for (i = 1; i < plot_in.length; i++) { + var x = step * i; + var y_in = <?=$height?> - (plot_in[i] * scale); + var y_out = <?=$height?> - (plot_out[i] * scale); + path_in += " L" + x + " " + y_in; + path_out += " L" + x + " " + y_out; + } + + SVGDoc.getElementById('error').setAttributeNS(null, 'visibility', 'hidden'); + SVGDoc.getElementById('graph_in').setAttributeNS(null, 'd', path_in); + SVGDoc.getElementById('graph_out').setAttributeNS(null, 'd', path_out); + + setTimeout('fetch_data()', <?=1000*$time_interval?>); +} + +function handle_error() { + SVGDoc.getElementById("error").setAttributeNS(null, 'visibility', 'visible'); + setTimeout('fetch_data()', <?=1000*$time_interval?>); +} + +function isNumber(a) { + return typeof a == 'number' && isFinite(a); +} + +function formatSpeed(speed, unit) { + if (unit == 'bits') { + return formatSpeedBits(speed); + } + if (unit == 'bytes') { + return formatSpeedBytes(speed); + } +} + +function formatSpeedBits(speed) { + // format speed in bits/sec, input: bytes/sec + if (speed < 125000) { + return Math.round(speed / 125) + " <?=gettext("Kbps"); ?>"; + } + if (speed < 125000000) { + return Math.round(speed / 1250)/100 + " <?=gettext("Mbps"); ?>"; + } + // else + return Math.round(speed / 1250000)/100 + " <?=gettext("Gbps"); ?>"; /* wow! */ +} + +function formatSpeedBytes(speed) { + // format speed in bytes/sec, input: bytes/sec + if (speed < 1048576) { + return Math.round(speed / 10.24)/100 + " <?=gettext("KB/s"); ?>"; + } + if (speed < 1073741824) { + return Math.round(speed / 10485.76)/100 + " <?=gettext("MB/s"); ?>"; + } + // else + return Math.round(speed / 10737418.24)/100 + " <?=gettext("GB/s"); ?>"; /* wow! */ +} + +function LZ(x) { + return (x < 0 || x > 9 ? "" : "0") + x; +} + + ]]> + </script> +</svg> diff --git a/src/usr/local/www/graph_cpu.php b/src/usr/local/www/graph_cpu.php new file mode 100644 index 0000000..75b8ad7 --- /dev/null +++ b/src/usr/local/www/graph_cpu.php @@ -0,0 +1,239 @@ +<?php +/* + $Id$ +*/ +/* + graph_cpu.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004-2006 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net> + * and Jonathan Watt <jwatt@jwatt.org> + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: graph +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-cpuutilization +##|*NAME=Diagnostics: CPU Utilization page +##|*DESCR=Allow access to the 'Diagnostics: CPU Utilization' page. +##|*MATCH=graph_cpu.php* +##|-PRIV + +require_once("guiconfig.inc"); + +header("Last-Modified: " . gmdate("D, j M Y H:i:s") . " GMT"); +header("Expires: " . gmdate("D, j M Y H:i:s", time()) . " GMT"); +header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP/1.1 +header("Pragma: no-cache"); // HTTP/1.0 +header("Content-type: image/svg+xml"); + +/********* Other conf *******/ + +$nb_plot = 120; // maximum number of data points to plot in the graph +$fetch_link = "stats.php?stats=cpu"; + +//SVG attributes +$attribs['axis']='fill="black" stroke="black"'; +$attribs['cpu']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="7"'; +$attribs['graph_cpu']='fill="none" stroke="#FF0000" stroke-opacity="0.8"'; +$attribs['legend']='fill="black" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4"'; +$attribs['grid_txt']='fill="gray" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="6"'; +$attribs['grid']='stroke="gray" stroke-opacity="0.5"'; +$attribs['error']='fill="blue" font-family="Arial" font-size="4"'; +$attribs['collect_initial']='fill="gray" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4"'; + +$height=100; // SVG internal height : do not modify +$width=200; // SVG internal width : do not modify + +/********* Graph DATA **************/ +print('<?xml version="1.0" encoding="UTF-8"?>' . "\n");?> +<svg width="100%" height="100%" viewBox="0 0 <?=$width?> <?=$height?>" preserveAspectRatio="none" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="init(evt);"> + <g id="graph"> + <rect id="bg" x1="0" y1="0" width="100%" height="100%" fill="white"/> + <line id="axis_x" x1="0" y1="0" x2="0" y2="100%" <?=$attribs['axis']?>/> + <line id="axis_y" x1="0" y1="100%" x2="100%" y2="100%" <?=$attribs['axis']?>/> + <polygon id="axis_arrow_x" <?=$attribs['axis']?> points="<?=($width) . "," . ($height)?> <?=($width-2) . "," . ($height-2)?> <?=($width-2) . "," . $height?>"/> + <path id="graph_cpu" d="" <?=$attribs['graph_cpu']?>/> + <path id="grid" d="M0 <?=$height/4*1?> L <?=$width?> <?=$height/4*1?> M0 <?=$height/4*2?> L <?=$width?> <?=$height/4*2?> M0 <?=$height/4*3?> L <?=$width?> <?=$height/4*3?>" <?=$attribs['grid']?>/> + <text id="grid_txt1" x="100%" y="25%" <?=$attribs['grid_txt']?> text-anchor="end">75%</text> + <text id="grid_txt2" x="100%" y="50%" <?=$attribs['grid_txt']?> text-anchor="end">50%</text> + <text id="grid_txt3" x="100%" y="75%" <?=$attribs['grid_txt']?> text-anchor="end">25%</text> + <text id="graph_cpu_txt" x="4" y="8" <?=$attribs['cpu']?>> </text> + <text id="error" x="50%" y="50%" visibility="hidden" <?=$attribs['error']?> text-anchor="middle"><?=gettext("Cannot get CPU load"); ?></text> + <text id="collect_initial" x="50%" y="50%" visibility="hidden" <?=$attribs['collect_initial']?> text-anchor="middle"><?=gettext("Collecting initial data, please wait"); ?>...</text> + </g> + <script type="text/ecmascript"> + <![CDATA[ + +/** + * getURL is a proprietary Adobe function, but it's simplicity has made it very + * popular. If getURL is undefined we spin our own by wrapping XMLHttpRequest. + */ +if (typeof getURL == 'undefined') { + getURL = function(url, callback) { + if (!url) { + throw '<?=gettext("No URL for getURL"); ?>'; + } + + try { + if (typeof callback.operationComplete == 'function') { + callback = callback.operationComplete; + } + } catch (e) {} + if (typeof callback != 'function') { + throw '<?=gettext("No callback function for getURL"); ?>'; + } + + var http_request = null; + if (typeof XMLHttpRequest != 'undefined') { + http_request = new XMLHttpRequest(); + } else if (typeof ActiveXObject != 'undefined') { + try { + http_request = new ActiveXObject('Msxml2.XMLHTTP'); + } catch (e) { + try { + http_request = new ActiveXObject('Microsoft.XMLHTTP'); + } catch (e) {} + } + } + if (!http_request) { + throw '<?=gettext("Both getURL and XMLHttpRequest are undefined"); ?>'; + } + + http_request.onreadystatechange = function() { + if (http_request.readyState == 4) { + callback( { success : true, + content : http_request.responseText, + contentType : http_request.getResponseHeader("Content-Type") } ); + } + } + http_request.open('GET', url, true); + http_request.send(null); + } +} + +var SVGDoc = null; +var last_cpu_total = 0; +var last_cpu_idle = 0; +var diff_cpu_total = 0; +var diff_cpu_idle = 0; +var cpu_data = new Array(); + +var max_num_points = <?=$nb_plot?>; // maximum number of plot data points +var step = <?=$width?> / max_num_points; // plot X division size +var scale = <?=$height?> / 100; + +function init(evt) { + SVGDoc = evt.target.ownerDocument; + fetch_data(); +} + +function fetch_data() { + getURL('<?=$fetch_link?>', plot_cpu_data); +} + +function plot_cpu_data(obj) { + if (!obj.success) { + return handle_error(); // getURL failed to get current CPU load data + } + + var cpu = parseInt(obj.content); + if (!isNumber(cpu)) { + return handle_error(); + } + + switch (cpu_data.length) { + case 0: + SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'visible'); + cpu_data[0] = cpu; + fetch_data(); + return; + case 1: + SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'hidden'); + break; + case max_num_points: + // shift plot to left if the maximum number of plot points has been reached + var i = 0; + while (i < max_num_points) { + cpu_data[i] = cpu_data[++i]; + } + --cpu_data.length; + } + + cpu_data[cpu_data.length] = cpu; + + var path_data = "M 0 " + (<?=$height?> - (cpu_data[0] * scale)); + for (var i = 1; i < cpu_data.length; ++i) { + var x = step * i; + var y_cpu = <?=$height?> - (cpu_data[i] * scale); + path_data += " L" + x + " " + y_cpu; + } + + SVGDoc.getElementById("error").setAttributeNS(null, 'visibility', 'hidden'); + SVGDoc.getElementById('graph_cpu_txt').firstChild.data = cpu + '%'; + SVGDoc.getElementById('graph_cpu').setAttributeNS(null, "d", path_data); + + fetch_data(); +} + +function handle_error() { + SVGDoc.getElementById("error").setAttributeNS(null, 'visibility', 'visible'); + fetch_data(); +} + +function isNumber(a) { + return typeof a == 'number' && isFinite(a); +} + + ]]> + </script> +</svg> diff --git a/src/usr/local/www/guiconfig.inc b/src/usr/local/www/guiconfig.inc new file mode 100644 index 0000000..b214c0d --- /dev/null +++ b/src/usr/local/www/guiconfig.inc @@ -0,0 +1,1220 @@ +<?php +/* + guiconfig.inc +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: base +*/ + +/* Include authentication routines */ +/* THIS MUST BE ABOVE ALL OTHER CODE */ +if (!$nocsrf) { + function csrf_startup() { + csrf_conf('rewrite-js', '/csrf/csrf-magic.js'); + $timeout_minutes = isset($config['system']['webgui']['session_timeout']) ? $config['system']['webgui']['session_timeout'] : 240; + csrf_conf('expires', $timeout_minutes * 60); + } + require_once("csrf/csrf-magic.php"); +} + +/* make sure nothing is cached */ +if (!$omit_nocacheheaders) { + header("Expires: 0"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + header("Cache-Control: no-cache, no-store, must-revalidate"); + header("Pragma: no-cache"); +} + +header("X-Frame-Options: SAMEORIGIN"); +require_once("authgui.inc"); + +/* parse the configuration and include all configuration functions */ +require_once("functions.inc"); + +/* Pull in all the gui related display classes) */ +foreach (scandir("/usr/local/www/classes/") as $file) { + if (substr($file, -4) == ".inc") { + require_once("classes/{$file}"); + } +} + +$g['theme'] = get_current_theme(); + +/* Set the default interface language */ +if ($config['system']['language'] <> "") { + $g['language'] = $config['system']['language']; +} elseif ($g['language'] == "") { + $g['language'] = 'en_US'; +} + +set_language($g['language']); + +/* used by progress bar */ +$lastseen = "-1"; + +$navlevelsep = ": "; /* navigation level separator string */ +$mandfldhtml = ""; /* display this before mandatory input fields */ +$mandfldhtmlspc = ""; /* same as above, but with spacing */ + +/* Some ajax scripts still need access to GUI */ +if (!$ignorefirmwarelock) { + if (is_subsystem_dirty('firmwarelock')) { + if (!$d_isfwfile) { + header("Location: system_firmware.php"); + exit; + } else { + return; + } + } +} + +/* Reserved table names to avoid collision */ +$reserved_table_names = array( + "bogons", + "bogonsv6", + "negate_networks", + "snort2c", + "sshlockout", + "tonatsubnets", + "virusprot", + "vpn_networks", + "webConfiguratorlockout" +); + +$firewall_rules_dscp_types = array( + "af11", + "af12", + "af13", + "af21", + "af22", + "af23", + "af31", + "af32", + "af33", + "af41", + "af42", + "af43", + "VA", + "EF", + "cs1", + "cs2", + "cs3", + "cs4", + "cs5", + "cs6", + "cs7", + "0x01", + "0x02", + "0x04"); + +$auth_server_types = array( + 'ldap' => "LDAP", + 'radius' => "Radius"); + +$ldap_urltypes = array( + 'TCP - Standard' => 389, + 'SSL - Encrypted' => 636); + +$ldap_scopes = array( + 'one' => "One Level", + 'subtree' => "Entire Subtree"); + +$ldap_protvers = array( + 2, + 3); + +$ldap_templates = array( + + 'open' => array( + 'desc' => "OpenLDAP", + 'attr_user' => "cn", + 'attr_group' => "cn", + 'attr_member' => "member"), + + 'msad' => array( + 'desc' => "Microsoft AD", + 'attr_user' => "samAccountName", + 'attr_group' => "cn", + 'attr_member' => "memberOf"), + + 'edir' => array( + 'desc' => "Novell eDirectory", + 'attr_user' => "cn", + 'attr_group' => "cn", + 'attr_member' => "uniqueMember")); + +$radius_srvcs = array( + 'both' => "Authentication and Accounting", + 'auth' => "Authentication", + 'acct' => "Accounting"); + +$netbios_nodetypes = array( + '0' => "none", + '1' => "b-node", + '2' => "p-node", + '4' => "m-node", + '8' => "h-node"); + +/* some well known ports */ +$wkports = array( + 5999 => "CVSup", + 53 => "DNS", + 21 => "FTP", + 3000 => "HBCI", + 80 => "HTTP", + 443 => "HTTPS", + 5190 => "ICQ", + 113 => "IDENT/AUTH", + 143 => "IMAP", + 993 => "IMAP/S", + 4500 => "IPsec NAT-T", + 500 => "ISAKMP", + 1701 => "L2TP", + 389 => "LDAP", + 1755 => "MMS/TCP", + 7000 => "MMS/UDP", + 445 => "MS DS", + 3389 => "MS RDP", + 1512 => "MS WINS", + 1863 => "MSN", + 119 => "NNTP", + 123 => "NTP", + 138 => "NetBIOS-DGM", + 137 => "NetBIOS-NS", + 139 => "NetBIOS-SSN", + 1194 => "OpenVPN", + 110 => "POP3", + 995 => "POP3/S", + 1723 => "PPTP", + 1812 => "RADIUS", + 1813 => "RADIUS accounting", + 5004 => "RTP", + 5060 => "SIP", + 25 => "SMTP", + 465 => "SMTP/S", + 161 => "SNMP", + 162 => "SNMP-Trap", + 22 => "SSH", + 3478 => "STUN", + 587 => "SUBMISSION", + 3544 => "Teredo", + 23 => "Telnet", + 69 => "TFTP", + 5900 => "VNC"); + +/* TCP flags */ +$tcpflags = array("fin", "syn", "rst", "psh", "ack", "urg", "ece", "cwr"); + +$specialnets = array("(self)" => "This Firewall", "pptp" => "PPTP clients", "pppoe" => "PPPoE clients", "l2tp" => "L2TP clients"); + +$spiflist = get_configured_interface_with_descr(false, true); +foreach ($spiflist as $ifgui => $ifdesc) { + $specialnets[$ifgui] = $ifdesc . " net"; + $specialnets[$ifgui . 'ip'] = $ifdesc . " address"; +} + +$medias = array( + "auto" => "autoselect", + "100full" => "100BASE-TX full-duplex", + "100half" => "100BASE-TX half-duplex", + "10full" => "10BASE-T full-duplex", + "10half" => "10BASE-T half-duplex"); + +$wlan_modes = array( + "bss" => "Infrastructure (BSS)", + "adhoc" => "Ad-hoc (IBSS)", + "hostap" => "Access Point"); + +function do_input_validation($postdata, $reqdfields, $reqdfieldsn, &$input_errors) { + + /* check for bad control characters */ + foreach ($postdata as $pn => $pd) { + if (is_string($pd) && preg_match("/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/", $pd)) { + $input_errors[] = sprintf(gettext("The field %s contains invalid characters."), $pn); + } + } + + for ($i = 0; $i < count($reqdfields); $i++) { + if ($_POST[$reqdfields[$i]] == "" && $_REQUEST[$reqdfields[$i]] == "") { + $input_errors[] = sprintf(gettext("The field %s is required."), $reqdfieldsn[$i]); + } + } +} + +function print_input_errors($input_errors) { + echo '<div class="alert alert-danger input-errors">'; + echo '<p>' . gettext('The following input errors were detected:') . '</p>'; + echo '<ul>'; + + foreach ($input_errors as $ierr) { + echo '<li>' . htmlspecialchars($ierr) . '</li>'; + } + + echo '</ul>'; + echo '</div>'; +} + +function verify_gzip_file($fname) { + $returnvar = mwexec("/usr/bin/gzip -t " . escapeshellarg($fname)); + if ($returnvar != 0) { + return 0; + } else { + return 1; + } +} + +function print_info_box_np($msg, $name="apply",$value="", $showapply=false, $class="alert-warning") { + global $g; + + if(strpos($class, "alert-") !== 0) + $class = 'alert-' . $class; + + if(empty($value)) { + $value = gettext("Apply changes"); + } + + $msg = '<div class="pull-left">' . $msg . '</div>'; + + if (stristr($msg, gettext("apply")) != false || stristr($msg, gettext("save")) != false || stristr($msg, gettext("create")) != false || $showapply) { + $msg .= '<form method="post" class="pull-right"><button type="submit" class="btn btn-default" name="'. $name .'" value="'.$value.'">'.$name.'</button>'; + + if ($_POST['if']) + $msg .= "<input type=\"hidden\" name=\"if\" value=\"" . htmlspecialchars($_POST['if']) . "\" />"; + + $msg .= '</form>'; + } else + $msg = '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>'. $msg; + + echo '<div class="alert ' . $class . ' clearfix" role="alert">'.$msg.'</div>'; +} + +function print_info_box_np_undo($msg, $name = "apply", $value = "Apply changes", $undo) { + global $g; + + if (stristr($msg, "apply") != false || stristr($msg, "save") != false || stristr($msg, "create") != false) { + $savebutton = "<td class=\"infoboxsave nowrap\">"; + $savebutton .= "<input type=\"button\" value=\"". gettext("Undo") . "\" onclick=\"document.location='{$undo}'\" />"; + $savebutton .= "<input name=\"{$name}\" type=\"submit\" class=\"formbtn\" id=\"${name}\" value=\"{$value}\" />"; + $savebutton .= "</td>"; + if ($_POST['if']) { + $savebutton .= "<input type=\"hidden\" name=\"if\" value=\"" . htmlspecialchars($_POST['if']) . "\" />"; + } + } + $nifty_redbox = "#990000"; + $nifty_blackbox = "#000000"; + + $themename = $g['theme']; + + if (file_exists("/usr/local/www/themes/{$themename}/tabcontrols.php")) { + $toeval = file_get_contents("/usr/local/www/themes/{$themename}/tabcontrols.php"); + eval($toeval); + } + + if (file_exists("/usr/local/www/themes/{$themename}/infobox.php")) { + $toeval = file_get_contents("/usr/local/www/themes/{$themename}/infobox.php"); + eval($toeval); + } + + + if (!$savebutton) { + $savebutton = "<td class=\"infoboxsave\"><input value=\"" . gettext("Close") . "\" type=\"button\" onclick=\"jQuery(this).parents('table[id=redboxtable]').hide();\" /></td>"; + } + + echo <<<EOFnp + <table class="infobox" id="redboxtable" summary="red box table"> + <tr> + <td> + <div class="infoboxnp" id="redbox"> + <table class="infoboxnptable2" summary="message"> + <tr> + <td class="infoboxnptd"> + <img class="infoboxnpimg" src="/themes/{$g['theme']}/images/icons/icon_exclam.gif" alt="exclamation" /> + </td> + <td class="infoboxnptd2"> + <b>{$msg}</b> + </td> + {$savebutton} + {$undobutton} + </tr> + </table> + </div> + <div> + <p> </p> + </div> + </td> + </tr> + </table> + <script type="text/javascript"> + //<![CDATA[ + NiftyCheck(); + Rounded("div#redbox","all","#FFF","{$nifty_redbox}","smooth"); + Rounded("td#blackbox","all","#FFF","{$nifty_blackbox}","smooth"); + //]]> + </script> +EOFnp; + +} + +function print_info_box($msg, $class="alert-warning") { + print_info_box_np($msg, null, null, false, $class); + +} + +function get_std_save_message($ok) { + global $d_sysrebootreqd_path; + $filter_related = false; + $filter_pages = array("nat", "filter"); + $to_return = gettext("The changes have been applied successfully."); + foreach ($filter_pages as $fp) { + if (stristr($_SERVER['SCRIPT_FILENAME'], $fp)) { + $filter_related = true; + } + } + if ($filter_related) { + $to_return .= "<br />" . gettext("You can also <a href=\"status_filter_reload.php\">monitor</a> the filter reload progress."); + } + return $to_return; +} + +function pprint_address($adr) { + global $specialnets; + + if (isset($adr['any'])) { + $padr = "*"; + } else if ($adr['network']) { + $padr = $specialnets[$adr['network']]; + } else { + $padr = $adr['address']; + } + + if (isset($adr['not'])) { + $padr = "! " . $padr; + } + + return $padr; +} + +function pprint_port($port) { + global $wkports; + + $pport = ""; + + if (!$port) { + return "*"; + } else { + $srcport = explode("-", $port); + if ((!$srcport[1]) || ($srcport[0] == $srcport[1])) { + $pport = $srcport[0]; + if ($wkports[$srcport[0]]) { + $pport .= " (" . $wkports[$srcport[0]] . ")"; + } + } else { + $pport .= $srcport[0] . " - " . $srcport[1]; + } + } + + return $pport; +} + +function firewall_check_for_advanced_options(&$item) { + $item_set = ""; + if ($item['os']) { + $item_set .= "os {$item['os']} "; + } + if ($item['dscp']) { + $item_set .= "dscp {$item['dscp']} "; + } + if ($item['max']) { + $item_set .= "max {$item['max']} "; + } + if ($item['max-src-nodes']) { + $item_set .= "max-src-nodes {$item['max-src-nodes']} "; + } + if ($item['max-src-conn']) { + $item_set .= "max-src-conn {$item['max-src-conn']} "; + } + if ($item['max-src-states']) { + $item_set .= "max-src-states {$item['max-src-states']} "; + } + if (isset($item['nopfsync'])) { + $item_set .= "nopfsync "; + } + if ($item['statetype'] != "keep state" && $item['statetype'] != "") { + $item_set .= "statetype {$item['statetype']} "; + } + if ($item['statetimeout']) { + $item_set .= "statetimeout {$item['statetimeout']} "; + } + if (isset($item['nosync'])) { + $item_set .= "no XMLRPC Sync "; + } + if ($item['max-src-conn-rate']) { + $item_set .= "max-src-conn-rate {$item['max-src-conn-rate']} "; + } + if ($item['max-src-conn-rates']) { + $item_set .= "max-src-conn-rates {$item['max-src-conn-rates']} "; + } + if ($item['vlanprio']) { + $item_set .= "vlanprio {$item['vlanprio']} "; + } + if ($item['vlanprioset']) { + $item_set .= "vlanprioset {$item['vlanprioset']} "; + } + if ($item['gateway']) { + $item_set .= "gateway {$item['gateway']} "; + } + if ($item['dnpipe']) { + $item_set .= "limiter {$item['dnpipe']} "; + } + if ($item['pdnpipe']) { + $item_set .= "limiter {$item['pdnpipe']} "; + } + if ($item['ackqueue']) { + $item_set .= "ackqueue {$item['ackqueue']} "; + } + if ($item['defaultqueue']) { + $item_set .= "defaultqueue {$item['defaultqueue']} "; + } + if ($item['l7container']) { + $item_set .= "layer7 {$item['l7container']} "; + } + if ($item['tag']) { + $item_set .= "tag {$item['tag']} "; + } + if ($item['tagged']) { + $item_set .= "tagged {$item['tagged']} "; + } + if (isset($item['allowopts'])) { + $item_set .= "allowopts "; + } + if (isset($item['disablereplyto'])) { + $item_set .= "disable reply-to "; + } + if ($item['tcpflags_any'] || $item['tcpflags1'] || $item['tcpflags2']) { + $item_set .= "tcpflags set"; + } + + return $item_set; +} + +function gentitle($title) { + global $navlevelsep; + if (!is_array($title)) { + return $title; + } else { + return join($navlevelsep, $title); + } +} + +function genhtmltitle($title) { + if(!is_array($title)) + return '<h1 class="page-header">' . $title . '</h1>'; + + $heading = '<h1 class="page-header">' . end($title) . '</h1>'; + + $bc = '<ol class="breadcrumb">'; + + foreach ($title as $el) + $bc .= '<li>'.$el.'</li>'; + + $bc .= '</ol>'; + + return $heading . $bc; +} + +/* update the changedesc and changecount(er) variables */ +function update_changedesc($update) { + global $changedesc; + global $changecount; + + $changedesc .= " {$update}"; + $changecount++; +} + +function clear_log_file($logfile = "/var/log/system.log", $restart_syslogd = true) { + global $config, $g; + if ($restart_syslogd) { + exec("/usr/bin/killall syslogd"); + } + if (isset($config['system']['disablesyslogclog'])) { + unlink($logfile); + touch($logfile); + } else { + $log_size = isset($config['syslog']['logfilesize']) ? $config['syslog']['logfilesize'] : "511488"; + if (isset($config['system']['usefifolog'])) { + exec("/usr/sbin/fifolog_create -s {$log_size} " . escapeshellarg($logfile)); + } else { + exec("/usr/local/sbin/clog -i -s {$log_size} " . escapeshellarg($logfile)); + } + } + if ($restart_syslogd) { + system_syslogd_start(); + } +} + +function clear_all_log_files() { + global $g; + exec("/usr/bin/killall syslogd"); + + $log_files = array("system", "filter", "dhcpd", "vpn", "pptps", "poes", "l2tps", "openvpn", "portalauth", "ipsec", "ppp", "relayd", "wireless", "lighttpd", "ntpd", "gateways", "resolver", "routing"); + foreach ($log_files as $lfile) { + clear_log_file("{$g['varlog_path']}/{$lfile}.log", false); + } + + system_syslogd_start(); + killbyname("dhcpd"); + services_dhcpd_configure(); + return; +} + +// This version of dump_clog() does not output <td></td> or any other table elements. It can be renamed +// and the dump_clog() removed once all of the diag_log*.php files have been converted to Bootstrap +function dump_clog_no_table($logfile, $tail, $withorig = true, $grepfor = "", $grepinvert = "") { + global $g, $config; + $sor = isset($config['syslog']['reverse']) ? "-r" : ""; + $logarr = ""; + $grepline = " "; + if(is_array($grepfor)) + $grepline .= " | /usr/bin/egrep " . escapeshellarg(implode("|", $grepfor)); + if(is_array($grepinvert)) + $grepline .= " | /usr/bin/egrep -v " . escapeshellarg(implode("|", $grepinvert)); + if (is_dir($logfile)) { + $logarr = array("File $logfile is a directory."); + } elseif (file_exists($logfile) && filesize($logfile) == 0) { + $logarr = array("Log file started."); + } else { + if($config['system']['disablesyslogclog']) { + exec("cat " . escapeshellarg($logfile) . "{$grepline} | /usr/bin/tail {$sor} -n " . escapeshellarg($tail), $logarr); + } else { + if(isset($config['system']['usefifolog'])) + exec("/usr/sbin/fifolog_reader " . escapeshellarg($logfile) . "{$grepline} | /usr/bin/tail {$sor} -n " . escapeshellarg($tail), $logarr); + else + exec("/usr/local/sbin/clog " . escapeshellarg($logfile) . "{$grepline}| grep -v \"CLOG\" | grep -v \"\033\" | /usr/bin/tail {$sor} -n " . escapeshellarg($tail), $logarr); + } + } + echo "\n"; + + $rows = 0; + foreach ($logarr as $logent) { + $rows++; + $logent = preg_split("/\s+/", $logent, 6); + + if ($withorig) { + if(isset($config['system']['usefifolog'])) { + $entry_date_time = htmlspecialchars(date("F j, Y, g:i a","" . $logent[1] . "")); + $entry_text = htmlspecialchars($logent[5]); + } else { + $entry_date_time = htmlspecialchars(join(" ", array_slice($logent, 0, 3))); + $entry_text = ($logent[3] == $config['system']['hostname']) ? "" : $logent[3] . " "; + $entry_text .= htmlspecialchars($logent[4] . " " . $logent[5]); + } + echo "{$entry_date_time}"; + echo " " . "{$entry_text}" . "\n"; + } else { + echo htmlspecialchars($logent[5]) . "\n"; + } + + } + return($rows); +} + +function dump_clog($logfile, $tail, $withorig = true, $grepfor = "", $grepinvert = "") { + global $g, $config; + $sor = isset($config['syslog']['reverse']) ? "-r" : ""; + $logarr = ""; + $grepline = " "; + if (is_array($grepfor)) { + $grepline .= " | /usr/bin/egrep " . escapeshellarg(implode("|", $grepfor)); + } + if (is_array($grepinvert)) { + $grepline .= " | /usr/bin/egrep -v " . escapeshellarg(implode("|", $grepinvert)); + } + if (is_dir($logfile)) { + $logarr = array("File $logfile is a directory."); + } elseif (file_exists($logfile) && filesize($logfile) == 0) { + $logarr = array("Log file started."); + } else { + if ($config['system']['disablesyslogclog']) { + exec("cat " . escapeshellarg($logfile) . "{$grepline} | /usr/bin/tail {$sor} -n " . escapeshellarg($tail), $logarr); + } else { + if (isset($config['system']['usefifolog'])) { + exec("/usr/sbin/fifolog_reader " . escapeshellarg($logfile) . "{$grepline} | /usr/bin/tail {$sor} -n " . escapeshellarg($tail), $logarr); + } else { + exec("/usr/local/sbin/clog " . escapeshellarg($logfile) . "{$grepline}| grep -v \"CLOG\" | grep -v \"\033\" | /usr/bin/tail {$sor} -n " . escapeshellarg($tail), $logarr); + } + } + } + foreach ($logarr as $logent) { + $logent = preg_split("/\s+/", $logent, 6); + echo "<tr valign=\"top\">\n"; + if ($withorig) { + if (isset($config['system']['usefifolog'])) { + $entry_date_time = htmlspecialchars(date("F j, Y, g:i a", "" . $logent[1] . "")); + $entry_text = htmlspecialchars($logent[5]); + } else { + $entry_date_time = htmlspecialchars(join(" ", array_slice($logent, 0, 3))); + $entry_text = ($logent[3] == $config['system']['hostname']) ? "" : $logent[3] . " "; + $entry_text .= htmlspecialchars($logent[4] . " " . $logent[5]); + } + echo "<td class=\"listlr nowrap\">{$entry_date_time}</td>\n"; + echo "<td class=\"listr\">{$entry_text}</td>\n"; + } else { + echo "<td class=\"listlr\" colspan=\"2\">" . htmlspecialchars($logent[5]) . "</td>\n"; + } + echo "</tr>\n"; + } +} + +function return_clog($logfile, $tail, $withorig = true, $grepfor = "", $grepinvert = "", $grepreverse = false) { + global $g, $config; + $sor = (isset($config['syslog']['reverse']) || $grepreverse) ? "-r" : ""; + $logarr = ""; + $grepline = " "; + if (is_array($grepfor)) { + $grepline .= " | /usr/bin/egrep " . escapeshellarg(implode("|", $grepfor)); + } + if (is_array($grepinvert)) { + $grepline .= " | /usr/bin/egrep -v " . escapeshellarg(implode("|", $grepinvert)); + } + if ($config['system']['disablesyslogclog']) { + exec("cat " . escapeshellarg($logfile) . "{$grepline} | /usr/bin/tail {$sor} -n " . escapeshellarg($tail), $logarr); + } else { + if (isset($config['system']['usefifolog'])) { + exec("/usr/sbin/fifolog_reader " . escapeshellarg($logfile) . "{$grepline} | /usr/bin/tail {$sor} -n " . escapeshellarg($tail), $logarr); + } else { + exec("/usr/local/sbin/clog " . escapeshellarg($logfile) . "{$grepline}| grep -v \"CLOG\" | grep -v \"\033\" | /usr/bin/tail {$sor} -n " . escapeshellarg($tail), $logarr); + } + } + return($logarr); +} + +/* Check if variable has changed, update and log if it has + * returns true if var changed + * varname = variable name in plain text + * orig = original value + * new = new value + */ +function update_if_changed($varname, & $orig, $new) { + if (is_array($orig) && is_array($new)) { + $a_diff = array_diff($orig, $new); + foreach ($a_diff as $diff) { + update_changedesc("removed {$varname}: \"{$diff}\""); + } + $a_diff = array_diff($new, $orig); + foreach ($a_diff as $diff) { + update_changedesc("added {$varname}: \"{$diff}\""); + } + $orig = $new; + return true; + + } else { + if ($orig != $new) { + update_changedesc("{$varname}: \"{$orig}\" -> \"{$new}\""); + $orig = $new; + return true; + } + } + return false; +} + +function address_to_pconfig($adr, &$padr, &$pmask, &$pnot, &$pbeginport, &$pendport) { + if (isset($adr['any'])) { + $padr = "any"; + } else if ($adr['network']) { + $padr = $adr['network']; + } else if ($adr['address']) { + list($padr, $pmask) = explode("/", $adr['address']); + if (!$pmask) { + if (is_ipaddrv6($padr)) { + $pmask = 128; + } else { + $pmask = 32; + } + } + } + + if (isset($adr['not'])) { + $pnot = 1; + } else { + $pnot = 0; + } + + if ($adr['port']) { + list($pbeginport, $pendport) = explode("-", $adr['port']); + if (!$pendport) { + $pendport = $pbeginport; + } + } else if (!is_alias($pbeginport) && !is_alias($pendport)) { + $pbeginport = "any"; + $pendport = "any"; + } +} + +function pconfig_to_address(&$adr, $padr, $pmask, $pnot = false, $pbeginport = 0, $pendport = 0) { + $adr = array(); + + if ($padr == "any") { + $adr['any'] = true; + } else if (is_specialnet($padr)) { + $adr['network'] = $padr; + } else { + $adr['address'] = $padr; + if (is_ipaddrv6($padr)) { + if ($pmask != 128) { + $adr['address'] .= "/" . $pmask; + } + } else { + if ($pmask != 32) { + $adr['address'] .= "/" . $pmask; + } + } + } + + if ($pnot) { + $adr['not'] = true; + } else { + unset($adr['not']); + } + + if (($pbeginport != 0) && ($pbeginport != "any")) { + if ($pbeginport != $pendport) { + $adr['port'] = $pbeginport . "-" . $pendport; + } else { + $adr['port'] = $pbeginport; + } + } + + if (is_alias($pbeginport)) { + $adr['port'] = $pbeginport; + } +} + +function is_specialnet($net) { + global $specialsrcdst; + + if (!$net) { + return false; + } + if (in_array($net, $specialsrcdst)) { + return true; + } else { + return false; + } +} + +//function to create widget tabs when called +function display_widget_tabs(& $tab_array) { + echo "<div id=\"tabs\">"; + $tabscounter = 0; + foreach ($tab_array as $ta) { + $dashpos = strpos($ta[2], '-'); + $tabname = $ta[2] . "-tab"; + $tabclass = substr($ta[2], 0, $dashpos); + $tabclass = $tabclass . "-class"; + if ($ta[1] == true) { + $tabActive = "table-cell"; + $tabNonActive = "none"; + } else { + $tabActive = "none"; + $tabNonActive = "table-cell"; + } + echo "<div id=\"{$ta[2]}-active\" class=\"{$tabclass}-tabactive\" style=\"display:{$tabActive}; background-color:#EEEEEE; color:black;\">"; + echo "<b> {$ta[0]}"; + echo " </b>"; + echo "</div>"; + + echo "<div id=\"{$ta[2]}-deactive\" class=\"{$tabclass}-tabdeactive\" style=\"display:{$tabNonActive}; background-color:#777777; color:white; cursor: pointer;\" onclick=\"return changeTabDIV('{$ta[2]}')\">"; + echo "<b> {$ta[0]}"; + echo " </b>"; + echo "</div>"; + } + + echo "<script type=\"text/javascript\">"; + echo "\n//<![CDATA[\n"; + echo "NiftyCheck();\n"; + echo "Rounded(\"div.{$tabclass}-tabactive\",\"top\",\"#CCCCCC\",\"#EEEEEE\",\"smooth\");\n"; + echo "Rounded(\"div.{$tabclass}-tabdeactive\",\"top\",\"#CCCCCC\",\"#777777\",\"smooth\");\n"; + echo "//]]>\n"; + echo "</script>"; + echo "</div>"; +} + + +// Return inline javascript file or CSS to minimize +// request count going back to server. +function outputJavaScriptFileInline($javascript) { + if (file_exists($javascript)) { + echo "\n<script type=\"text/javascript\">\n"; + include($javascript); + echo "\n</script>\n"; + } else { + echo "\n\n<!-- Could not locate file: {$javascript} -->\n\n"; + } +} + + + +function outputCSSPrintFileInline($css) { + if (file_exists($css)) { + echo "\n<style media=\"print\" type=\"text/css\">\n"; + include($css); + echo "\n</style>\n"; + } else { + echo "\n\n<!-- Could not locate file: {$css} -->\n\n"; + } +} + + +function outputCSSFileInline($css) { + if (file_exists($css)) { + echo "\n<style type=\"text/css\">\n"; + include($css); + echo "\n</style>\n"; + } else { + echo "\n\n<!-- Could not locate file: {$css} -->\n\n"; + } +} + +$rfc2616 = array( + 100 => "100 Continue", + 101 => "101 Switching Protocols", + 200 => "200 OK", + 201 => "201 Created", + 202 => "202 Accepted", + 203 => "203 Non-Authoritative Information", + 204 => "204 No Content", + 205 => "205 Reset Content", + 206 => "206 Partial Content", + 300 => "300 Multiple Choices", + 301 => "301 Moved Permanently", + 302 => "302 Found", + 303 => "303 See Other", + 304 => "304 Not Modified", + 305 => "305 Use Proxy", + 306 => "306 (Unused)", + 307 => "307 Temporary Redirect", + 400 => "400 Bad Request", + 401 => "401 Unauthorized", + 402 => "402 Payment Required", + 403 => "403 Forbidden", + 404 => "404 Not Found", + 405 => "405 Method Not Allowed", + 406 => "406 Not Acceptable", + 407 => "407 Proxy Authentication Required", + 408 => "408 Request Timeout", + 409 => "409 Conflict", + 410 => "410 Gone", + 411 => "411 Length Required", + 412 => "412 Precondition Failed", + 413 => "413 Request Entity Too Large", + 414 => "414 Request-URI Too Long", + 415 => "415 Unsupported Media Type", + 416 => "416 Requested Range Not Satisfiable", + 417 => "417 Expectation Failed", + 500 => "500 Internal Server Error", + 501 => "501 Not Implemented", + 502 => "502 Bad Gateway", + 503 => "503 Service Unavailable", + 504 => "504 Gateway Timeout", + 505 => "505 HTTP Version Not Supported" +); + +function is_rfc2616_code($code) { + global $rfc2616; + if (isset($rfc2616[$code])) { + return true; + } else { + return false; + } +} + +function print_rfc2616_select($tag, $current) { + global $rfc2616; + + /* Default to 200 OK if not set */ + if ($current == "") { + $current = 200; + } + + echo "<select id=\"{$tag}\" name=\"{$tag}\">\n"; + foreach ($rfc2616 as $code => $message) { + if ($code == $current) { + $sel = " selected=\"selected\""; + } else { + $sel = ""; + } + echo "<option value=\"{$code}\"{$sel}>{$message}</option>\n"; + } + echo "</select>\n"; +} + +// Useful debugging function, much cleaner than print_r +function echo_array($array, $return_me = false) { + if (is_array($array) == false) { + $return = "The provided variable is not an array."; + } else { + foreach ($array as $name=>$value) { + if (is_array($value)) { + $return .= ""; + $return .= "['<b>$name</b>'] {<div style=\"margin-left:10px;\">\n"; + $return .= echo_array($value, true); + $return .= "</div>}"; + $return .= "\n\n"; + } else { + if (is_string($value)) { + $value = "\"$value\""; + } + $return .= "['<b>$name</b>'] = $value\n\n"; + } + } + } + if ($return_me == true) { + return $return; + } else { + echo "<pre>".$return."</pre>"; + } +} + +/****f* pfsense-utils/display_top_tabs + * NAME + * display_top_tabs - display tabs with rounded edges + * INPUTS + * $text - array of tabs + * RESULT + * null + ******/ +function display_top_tabs(& $tab_array, $no_drop_down = false, $type = 'pills') { + global $config; + global $g; + global $tab_array_indent; + global $tab_array_space; + global $tab_array_char_limit; + + /* does the user have access to this tab? + * master user has access to everything. + * if the user does not have access, simply + * unset the tab item. + */ + + /* empty string code */ + if ($tab_array_indent == '') { + $tab_array_indent = 0; + } + + if ($tab_array_space == '') { + $tab_array_space = 1; + } + + if ($tab_array_char_limit == '') { + $tab_array_char_limit = 92; + } + + foreach ($tab_array as $tab_id => $ta) { + if (!isAllowedPage($ta[2])) { + unset ($tab_array[$tab_id]); + } + } + + $tab_active_bg = "#EEEEEE"; + $tab_inactive_bg = "#777777"; + $nifty_tabs_corners = "#FFF"; + $font_color = "white"; + + $tabcharcount = 0; + foreach ($tab_array as $ta) + $tabcharcount = $tabcharcount + strlen($ta[0]); + + if($no_drop_down == true) { + $tabcharcount = 0; + unset($tab_array_char_limit); + } + + // If the character count of the tab names is > 670 + // then show a select item dropdown menubox. + if($tabcharcount > $tab_array_char_limit): ?> + echo gettext("Currently viewing: "); + echo "<select name=\"TabSelect\" onchange=\"tabs_will_go(this)\">\n"; + foreach ($tab_array as $ta) { + if($ta[1]=="true") + $selected = " selected=\"selected\""; + else + $selected = ""; + // Onclick in option will not work in some browser + // echo "<option onclick=\"document.location='{$ta[2]}';\"{$selected}>{$ta['0']}</option>\n"; + echo "<option value=\"{$ta[2]}\"{$selected}>{$ta['0']}</option>\n"; + } + echo "</select>\n<p> </p>"; + echo "<script type=\"text/javascript\">"; + echo "\n//<![CDATA[\n"; + echo " function tabs_will_go(obj){ document.location = obj.value; }\n"; + echo "//]]>\n"; + echo "</script>"; +<? else: ?> + <ul class="nav nav-<?= $type ?>"> + <?php foreach ($tab_array as $ta): ?> + <li role="presentation"<?=($ta[1] ? ' class="active"' : '')?>><a href="<?=$ta[2]?>"><?=$ta[0]?></a></li> + <?php endforeach; ?> + </ul> +<?php endif; +} + +function add_package_tabs($pkgname, &$tab_array) { + global $config, $g; + +// $pkg = get_pkg_data($pkgname); + + if (!isset($pkg['configurationfile']) || !file_exists('/usr/local/pkg/' . $pkg['configurationfile'])) { + return; + } + + $pkg_config = parse_xml_config_pkg('/usr/local/pkg/' . $pkg['configurationfile'], "packagegui"); + + if (!isset($pkg_config['tabs']['tab'])) { + return; + } + + foreach ($pkg_config['tabs']['tab'] as $tab) { + $tab_entry = array(); + if ($tab['name']) { + $tab_entry[] = $tab['name']; + $tab_entry[] = false; + $tab_entry[] = $tab['url']; + $tab_array[] = $tab_entry; + } + } +} + +function alias_info_popup($alias_id) { + global $config; + + if (!is_array($config['aliases']['alias'][$alias_id])) + return; + + $maxlength = 60; + $alias = $config['aliases']['alias'][$alias_id]; + $content = ""; + + if ($alias['url']) + { + // TODO: Change it when pf supports tables with ports + if ($alias['type'] == "urltable") { + exec("/sbin/pfctl -t {$alias['name']} -T show | wc -l", $total_entries); + $counter=preg_replace("/\D/","",$total_entries[0]); + exec("/sbin/pfctl -t {$alias['name']} -T show | head -10002", $alias_addresses); + } else { + $urlfn = alias_expand_urltable($alias['name']); + $alias_addresses = explode("\n", file_get_contents($urlfn)); + $counter = count($alias_addresses); + } + + $content .= '<h5>'. $alias['url'] .'</h5><ul><li>'. implode('</li><li>', $alias_ports_address) .'</li></ul>'; + + if ($counter > 10002) + $content .= '<i>'. gettext("listing only first 10k items") .'</i>'; + } + else + { + $alias_addresses = explode (" ", $alias['address']); + $alias_details = explode ("||", $alias['detail']); + + $content .= '<ul><li>'. implode('</li><li>', $alias_addresses) .'</li></ul>'; + } + + if (strlen($alias['descr']) >= $maxlength) + $alias['descr'] = substr($alias['descr'], 0, $maxlength) . '…'; + + return $content; +} + +function rule_columns_with_alias($src, $srcport, $dst, $dstport){ + global $config; + + if ($config['aliases']['alias'] == "" || !is_array($config['aliases']['alias'])) + return; + + $columns = array(); + foreach ($config['aliases']['alias'] as $alias_id => $alias_name){ + if ($alias_name['name'] == $src) + $columns['src'] = $alias_id; + elseif ($alias_name['name'] == $srcport) + $columns['srcport'] = $alias_id; + elseif ($alias_name['name'] == $dst ) + $columns['dst'] = $alias_id; + elseif ($alias_name['name'] == $dstport) + $columns['dstport'] = $alias_id; + + return $columns; + } +} + +function form_output_row($name, $label, $content){ +var_dump($content);die; +?> +<div class="form-group"> + <label for="<?=$name?>" class="col-sm-2 control-label"><?=gettext($label); ?></label> + <div class="col-sm-10"> + <?=$content?> + </div> +</div> +<?php +} + +$timezone = $config['system']['timezone']; +if (!$timezone) { + $timezone = "Etc/UTC"; +} + +date_default_timezone_set($timezone); + +?> diff --git a/src/usr/local/www/halt.php b/src/usr/local/www/halt.php new file mode 100755 index 0000000..efa6dce --- /dev/null +++ b/src/usr/local/www/halt.php @@ -0,0 +1,115 @@ +<?php +/* $Id$ */ +/* + halt.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net> + * Originally part of m0n0wall as reboot.php (http://m0n0.ch/wall) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: header +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-haltsystem +##|*NAME=Diagnostics: Halt system page +##|*DESCR=Allow access to the 'Diagnostics: Halt system' page. +##|*MATCH=halt.php* +##|-PRIV + +// Set DEBUG to true to prevent the system_halt() function from being called +define("DEBUG", false); + +require("guiconfig.inc"); +require("functions.inc"); +require("captiveportal.inc"); + +if ($_POST['save'] == 'No') { + header("Location: index.php"); + exit; +} + +$pgtitle = array(gettext("Diagnostics"), gettext("Halt system")); +include('head.inc'); + +if ($_SERVER['REQUEST_METHOD'] == 'POST') { +?> + <meta http-equiv="refresh" content="70;url=/"> + <div class="alert alert-success" role="alert"> + <?=gettext("The system is halting now. This may take one minute or so.")?> + </div> + +<?php + if(DEBUG) + print("Not actually halting (DEBUG is set true)<br>"); + else + system_halt(); +} else { +?> + +<div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title">Are you sure you want to halt the system?</h2> + </div> + <div class="panel-body"> + <p>Click "Yes" to halt the system immediately, or "No" to go to the system dashboard. (There will be a brief delay before the dashboard appears.)</p> + <form action="halt.php" method="post"> + <input type="submit" class="btn btn-danger pull-center" name="save" value="Yes"> + <a href="/" class="btn btn-default">No</a> + </form> + </div> +</div> + +<?php +} + +include("foot.inc"); diff --git a/src/usr/local/www/head.inc b/src/usr/local/www/head.inc new file mode 100755 index 0000000..5316ee2 --- /dev/null +++ b/src/usr/local/www/head.inc @@ -0,0 +1,495 @@ +<?php +/* + pfSense_MODULE: header +*/ + +require_once("globals.inc"); +require_once("functions.inc"); +require_once("shortcuts.inc"); +require_once("service-utils.inc"); + +/* $Id$ */ + +header('Content-Type: text/html; charset=utf-8'); + +$pagetitle = gentitle( $pgtitle ); + +if (isset($config['system']['webgui']['pagenamefirst'])) { + $tabtitle = $pagetitle . " - " . $config['system']['hostname'] . "." . $config['system']['domain']; +} else { + $tabtitle = $config['system']['hostname'] . "." . $config['system']['domain'] . " - " . $pagetitle; +} +?> +<!DOCTYPE html> +<html lang="en"> +<head> + <link rel="stylesheet" href="/bootstrap/css/pfSense.css" /> + <title><?=$tabtitle?></title> + <script>var events = events || [];</script> +</head> +<body id="<?=basename($_SERVER['SCRIPT_NAME'], '.php')?>"> +<?php + +/* Determine automated help URL. Should output the page name and parameters + separately */ +$uri_split = ""; +preg_match("/\/(.*)\?(.*)/", $_SERVER["REQUEST_URI"], $uri_split); + +/* If there was no match, there were no parameters, just grab the filename + Otherwise, use the matched filename from above. */ +if (empty($uri_split[0])) { + $pagename = ltrim($_SERVER["REQUEST_URI"], '/'); +} else { + $pagename = $uri_split[1]; +} +/* If the page name is still empty, the user must have requested / (index.php) */ +if (empty($pagename)) { + $pagename = "index.php"; +} + +/* If the filename is pkg_edit.php or wizard.php, reparse looking + for the .xml filename */ +if (($pagename == "pkg.php") || ($pagename == "pkg_edit.php") || ($pagename == "wizard.php")) { + $param_split = explode('&', $uri_split[2]); + foreach ($param_split as $param) { + if (substr($param, 0, 4) == "xml=") { + $xmlfile = explode('=', $param); + $pagename = $xmlfile[1]; + } + } +} + +/* Build the full help URL. */ +$helpurl .= "{$g['help_base_url']}?page={$pagename}"; + +function return_ext_menu($section) { + global $config; + $htmltext = ""; + $extarray = array(); + if($config['installedpackages']['menu'] != "") { + foreach($config['installedpackages']['menu'] as $menuitem) { + if($menuitem['section'] != $section) { + continue; + } + if($menuitem['url'] != "") { + $test_url=$menuitem['url']; + $addresswithport = getenv("HTTP_HOST"); + $colonpos = strpos($addresswithport, ":"); + if ($colonpos !== False) { + //my url is actually just the IP address of the pfsense box + $myurl = substr($addresswithport, 0, $colonpos); + } else { + $myurl = $addresswithport; + } + $description = str_replace('$myurl', $myurl, $menuitem['url']); + } else { + $description = '/pkg.php?xml=' . $menuitem['configfile']; + $test_url=$description; + } + if (isAllowedPage($test_url)) { + $extarray[] = array($menuitem['name'], $description); + } + } + } + return $extarray; +} + +function output_menu($arrayitem, $target = null) { + foreach ($arrayitem as $item) { + if (isAllowedPage($item[1]) || $item[1]=="/index.php?logout") { + $attr = sprintf("href=\"%s\"", htmlentities($item[1])); + if ($target) { + $attr .= sprintf(" target=\"%s\"", htmlentities($target)); + } + $class = "navlnk"; + if ($item['class']) { + $class .= " {$item['class']}"; + } + $attr .= sprintf(" class=\"%s\"", htmlentities($class)); + if ($item['style']) { + $attr .= sprintf(" style=\"%s\"", htmlentities($item['style'])); + } + echo "<li>". sprintf("<a %s>%s</a>", $attr, $item[0]). "</li>\n"; + } + } +} + +// System +$system_menu = array(); +$system_menu[] = array(gettext("Logout"), "/index.php?logout"); +$system_menu[] = array(gettext("Advanced"), "/system_advanced_admin.php"); +$system_menu[] = array(gettext("Firmware"), "/system_firmware.php"); +$system_menu[] = array(gettext("General Setup"), "/system.php"); +$system_menu[] = array(gettext("High Avail. Sync"), "/system_hasync.php"); +if ($g['platform'] == "pfSense" or $g['platform'] == "nanobsd") { + $system_menu[] = array(gettext("Packages"), "/pkg_mgr_installed.php"); +} +$system_menu[] = array(gettext("Setup Wizard"), "/wizard.php?xml=setup_wizard.xml"); +$system_menu[] = array(gettext("Routing"), "/system_gateways.php"); +$system_menu[] = array(gettext("Cert Manager"), "/system_camanager.php"); +if (!isAllowedPage("system_usermanager.php*")) { + $system_menu[] = array(gettext("User Manager"), "/system_usermanager_passwordmg.php"); +} else { + $system_menu[] = array(gettext("User Manager"), "/system_usermanager.php"); +} +$system_menu = msort(array_merge($system_menu, return_ext_menu("System")),0); + +// Interfaces +$interfaces_menu = array(); +if (!isset($config['system']['webgui']['noassigninterfaces'])) { + $interfaces_menu[] = array(gettext("(assign)"), "/interfaces_assign.php"); +} +$opts = get_configured_interface_with_descr(false, true); +foreach ($opts as $oif => $odescr) { + if (!isset($config['interfaces'][$oif]['ovpn'])) { + $interfaces_menu[] = array(htmlspecialchars($odescr), "/interfaces.php?if={$oif}"); + } +} +$interfaces_menu = msort(array_merge($interfaces_menu, return_ext_menu("Interfaces")),0); + +// Firewall +$firewall_menu = array(); +$firewall_menu[] = array(gettext("Aliases"), "/firewall_aliases.php"); +$firewall_menu[] = array(gettext("NAT"), "/firewall_nat.php"); +$firewall_menu[] = array(gettext("Rules"), "/firewall_rules.php"); +$firewall_menu[] = array(gettext("Schedules"), "/firewall_schedule.php"); +$firewall_menu[] = array(gettext("Traffic Shaper"), "/firewall_shaper.php"); +$firewall_menu[] = array(gettext("Virtual IPs"), "/firewall_virtual_ip.php"); +$firewall_menu = msort(array_merge($firewall_menu, return_ext_menu("Firewall")),0); + +// Services +$services_menu = array(); +$services_menu[] = array(gettext("Captive Portal"), "/services_captiveportal.php"); +$services_menu[] = array(gettext("DNS Forwarder"), "/services_dnsmasq.php"); +$services_menu[] = array(gettext("DNS Resolver"), "/services_unbound.php"); +$services_menu[] = array(gettext("DHCP Relay"), "/services_dhcp_relay.php"); +$services_menu[] = array(gettext("DHCPv6 Relay"), "/services_dhcpv6_relay.php"); +if($g['services_dhcp_server_enable']) { + $services_menu[] = array(gettext("DHCP Server"), "/services_dhcp.php"); + $services_menu[] = array(gettext("DHCPv6 Server/RA"), "/services_dhcpv6.php"); +} +$services_menu[] = array(gettext("Dynamic DNS"), "/services_dyndns.php"); +$services_menu[] = array(gettext("IGMP proxy"), "/services_igmpproxy.php"); +$services_menu[] = array(gettext("Load Balancer"), "/load_balancer_pool.php"); +$services_menu[] = array(gettext("NTP"), "/services_ntpd.php"); +$services_menu[] = array(gettext("PPPoE Server"), "/vpn_pppoe.php"); +$services_menu[] = array(gettext("SNMP"), "/services_snmp.php"); +if(count($config['interfaces']) > 1) { + /* no use for UPnP in single-interface deployments + remove to reduce user confusion + */ + $services_menu[] = array(gettext("UPnP & NAT-PMP"), "/pkg_edit.php?xml=miniupnpd.xml"); +} +$services_menu[] = array(gettext("Wake on LAN"), "/services_wol.php"); +$services_menu = msort(array_merge($services_menu, return_ext_menu("Services")),0); + +// VPN +$vpn_menu = array(); +$vpn_menu[] = array(gettext("IPsec"), "/vpn_ipsec.php"); +$vpn_menu[] = array(gettext("OpenVPN"), "/vpn_openvpn_server.php"); +//$vpn_menu[] = array(gettext("PPTP"), "/vpn_pptp.php"); +$vpn_menu[] = array(gettext("L2TP"), "/vpn_l2tp.php"); +$vpn_menu = msort(array_merge($vpn_menu, return_ext_menu("VPN")),0); + +// Status +$status_menu = array(); +if (count($config['captiveportal']) > 0) { + $status_menu[] = array(gettext("Captive Portal"), "/status_captiveportal.php"); +} +$status_menu[] = array(gettext("CARP (failover)"), "/carp_status.php"); +$status_menu[] = array(gettext("Dashboard"), "/index.php"); +$status_menu[] = array(gettext("Gateways"), "/status_gateways.php"); +$status_menu[] = array(gettext("DHCP Leases"), "/status_dhcp_leases.php"); +$status_menu[] = array(gettext("DHCPv6 Leases"), "/status_dhcpv6_leases.php"); +$status_menu[] = array(gettext("Filter Reload"), "/status_filter_reload.php"); +$status_menu[] = array(gettext("Interfaces"), "/status_interfaces.php"); +$status_menu[] = array(gettext("IPsec"), "/diag_ipsec.php"); +$status_menu[] = array(gettext("Load Balancer"), "/status_lb_pool.php"); +$status_menu[] = array(gettext("NTP"), "/status_ntpd.php"); +$status_menu[] = array(gettext("OpenVPN"), "/status_openvpn.php"); +if ($g['platform'] == "pfSense") { + $status_menu[] = array(gettext("Package Logs"), "/diag_pkglogs.php"); +} +$status_menu[] = array(gettext("Queues"), "/status_queues.php"); +$status_menu[] = array(gettext("RRD Graphs"), "/status_rrd_graph.php"); +$status_menu[] = array(gettext("Services"), "/status_services.php"); +$status_menu[] = array(gettext("System Logs"), "/diag_logs.php"); +$status_menu[] = array(gettext("Traffic Graph"), "/status_graph.php?if=wan"); +if(count($config['interfaces']) > 1) { + $status_menu[] = array(gettext("UPnP & NAT-PMP"), "/status_upnp.php"); +} +$ifentries = get_configured_interface_with_descr(); +foreach ($ifentries as $ent => $entdesc) { + if (is_array($config['interfaces'][$ent]['wireless']) && + preg_match($g['wireless_regex'], $config['interfaces'][$ent]['if'])) { + $wifdescrs[$ent] = $entdesc; + } +} +if (count($wifdescrs) > 0) { + $status_menu[] = array(gettext("Wireless"), "/status_wireless.php"); +} +$status_menu = msort(array_merge($status_menu, return_ext_menu("Status")),0); + +// Diagnostics +$diagnostics_menu = array(); +$diagnostics_menu[] = array(gettext("ARP Table"), "/diag_arp.php"); +$diagnostics_menu[] = array(gettext("Authentication"), "/diag_authentication.php"); +$diagnostics_menu[] = array(gettext("Backup/Restore"), "/diag_backup.php"); +$diagnostics_menu[] = array(gettext("Command Prompt"), "/exec.php"); +$diagnostics_menu[] = array(gettext("DNS Lookup"), "/diag_dns.php"); +$diagnostics_menu[] = array(gettext("Edit File"), "/edit.php"); +$diagnostics_menu[] = array(gettext("Factory Defaults"), "/diag_defaults.php"); +if(file_exists("/var/run/gmirror_active")) { + $diagnostics_menu[] = array(gettext("GEOM Mirrors"), "/diag_gmirror.php" ); +} +$diagnostics_menu[] = array(gettext("Halt System"), "/halt.php" ); +$diagnostics_menu[] = array(gettext("Limiter Info"), "/diag_limiter_info.php" ); +$diagnostics_menu[] = array(gettext("NDP Table"), "/diag_ndp.php" ); +$diagnostics_menu[] = array(gettext("Tables"), "/diag_tables.php"); +$diagnostics_menu[] = array(gettext("Ping"), "/diag_ping.php"); +$diagnostics_menu[] = array(gettext("Test Port"), "/diag_testport.php"); +$diagnostics_menu[] = array(gettext("pfInfo"), "/diag_pf_info.php"); +$diagnostics_menu[] = array(gettext("pfTop"), "/diag_system_pftop.php"); +$diagnostics_menu[] = array(gettext("Reboot"), "/reboot.php"); +$diagnostics_menu[] = array(gettext("Routes"), "/diag_routes.php"); +$diagnostics_menu[] = array(gettext("SMART Status"), "/diag_smart.php"); +$diagnostics_menu[] = array(gettext("Sockets"), "/diag_sockets.php" ); +$diagnostics_menu[] = array(gettext("States"), "/diag_dump_states.php"); +$diagnostics_menu[] = array(gettext("States Summary"), "/diag_states_summary.php"); +$diagnostics_menu[] = array(gettext("System Activity"), "/diag_system_activity.php"); +$diagnostics_menu[] = array(gettext("Traceroute"), "/diag_traceroute.php"); +$diagnostics_menu[] = array(gettext("Packet Capture"), "/diag_packet_capture.php"); +if($g['platform'] == "nanobsd") { + $diagnostics_menu[] = array(gettext("NanoBSD"), "/diag_nanobsd.php"); +} +if (isset($config['system']['developer'])) { + $diagnostics_menu[] = array(gettext("Restart HTTPD"), "/restart_httpd.php", "style" => "font-weight: bold; color: yellow;"); +} +$diagnostics_menu = msort(array_merge($diagnostics_menu, return_ext_menu("Diagnostics")),0); + +$gold_menu = array(); +$gold_menu[] = array(gettext("pfSense Gold"), "https://www.pfsense.org/gold"); +$gold_menu = msort(array_merge($gold_menu, return_ext_menu("Gold")),0); + +if(! $g['disablehelpmenu']) { + $help_menu = array(); + $help_menu[] = array(gettext("About this Page"), $helpurl); + if($g['product_name'] == "pfSense") { + $help_menu[] = array(gettext("Bug Database"), "https://www.pfsense.org/j.php?jumpto=redmine"); + } + $help_menu[] = array(gettext("User Forum"), "https://www.pfsense.org/j.php?jumpto=forum"); + $help_menu[] = array(gettext("Documentation"), "https://www.pfsense.org/j.php?jumpto=doc"); + $help_menu[] = array(gettext("Developers Wiki"), "https://www.pfsense.org/j.php?jumpto=devwiki"); + $help_menu[] = array(gettext("Paid Support"), "https://www.pfsense.org/j.php?jumpto=portal"); + $help_menu[] = array(gettext("pfSense Book"), "https://www.pfsense.org/j.php?jumpto=book"); + $help_menu[] = array(gettext("Search portal"), "https://www.pfsense.org/j.php?jumpto=searchportal"); + $help_menu[] = array(gettext("FreeBSD Handbook"), "https://www.pfsense.org/j.php?jumpto=fbsdhandbook"); + $help_menu = msort(array_merge($help_menu, return_ext_menu("Help")),0); +} + +?> +<nav class="navbar navbar-static-top navbar-inverse"> + <div class="container"> + <div class="navbar-header"> + <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#pf-navbar"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="navbar-brand" href="/"><img src="/logo.png" alt="pfSense" /></a> + </div> + <div class="collapse navbar-collapse" id="pf-navbar"> + <ul class="nav navbar-nav"> + <?php foreach ([ + ['name' => 'System', 'menu' => $system_menu, 'href' => null], + ['name' => 'Interfaces', 'menu' => $interfaces_menu, 'href' => null], + ['name' => 'Firewall', 'menu' => $firewall_menu, 'href' => null], + ['name' => 'Services', 'menu' => $services_menu, 'href' => null], + ['name' => 'VPN', 'menu' => $vpn_menu, 'href' => null], + ['name' => 'Status', 'menu' => $status_menu, 'href' => null], + ['name' => 'Diagnostics', 'menu' => $diagnostics_menu, 'href' => null], + ['name' => 'Gold', 'menu' => $gold_menu, 'href' => '_blank'], + ['name' => 'Help', 'menu' => $help_menu, 'href' => '_blank'] + ] as $item): + if ($item['name'] == 'Help' && $g['disablehelpmenu']) { + continue; + } ?> + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> + <?=gettext($item['name'])?> + <span class="caret"></span> + </a> + <ul class="dropdown-menu" role="menu"><?=output_menu($item['menu'], $item['href'])?></ul> + </li> + <?php endforeach?> + <?php if(are_notices_pending()):?> + <?php $notices = get_notices()?> + <li class="dropdown"> + <a href="#" data-toggle="modal" data-target="#notices" role="button" aria-expanded="false"><?=gettext("Notices")?> + <span class="badge"><?=count($notices)?></span> + </a> + </li> + <?php + require_once('classes/Modal.class.php'); + + $modal_notices = new Modal('Notices', 'notices', 'large'); + + $modal_notices->addFooter(new Form_Button( + 'close', + 'Close', + '' + ))->setAttribute('data-dismiss', 'modal'); + + $modal_notices->addFooter(new Form_Button( + 'mark_all_as_read', + 'Mark all as read', + '/?closenotice=all' + ))->addClass('btn-primary')->removeClass('btn-default'); + + $noticeCategories = $notice['category']; + foreach($notices as $time => $notice) { + if (!isset($noticeCategories[$notice['category']])) { + $noticeCategories[ $notice['category'] ] = array(); + } + $notice['time'] = $time; + array_push($noticeCategories[$notice['category']], $notice); + } + + foreach($noticeCategories as $category => $catNotices) { + $html .= "<h4>{$category}</h4>"; + $html .= '<ul>'; + foreach($catNotices as $notice) { + $html .= '<li>'; + if (!empty($notice['url'])) { + $html .= "<a href=\"{$notice['url']}\">{$notice['id']}</a> - "; + } + $html .= "{$notice['notice']}<i> @ " . date('Y-m-d H:i:s', $notice['time']) . '</i>'; + $html .= '</li>'; + } + $html .= '</ul>'; + } + $modal_notices->addHtml($html); + print $modal_notices; + + endif; + ?> + </ul> + </div> + </div> + +<?php if(are_notices_pending()):?> + <div class="modal fade" id="notices" role="dialog" aria-labelledby="noticesTitle" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + + <h3 class="modal-title" id="myModalLabel">Notices</h3> + </div> + + <div class="modal-body"> +<?php +$noticeCategories = array(); +foreach($notices as $time => $notice) +{ + if (!isset($noticeCategories[ $notice['category'] ])) + $noticeCategories[ $notice['category'] ] = array(); + + $notice['time'] = $time; + array_push($noticeCategories[ $notice['category'] ], $notice); +} + +foreach($noticeCategories as $category => $catNotices):?> + <h4><?=$category?></h4> + <ul> +<?php + foreach($catNotices as $notice): +?> + <li> + <b> +<?php if (!empty($notice['url'])):?> + <a href="<?=$notice['url']?>"><?=$notice['id']?></a> - +<?php endif;?> + </b> + <?=$notice['notice']?> + <i>@ <?=date('Y-m-d H:i:s', $notice['time'])?></i> + </li> +<?php endforeach;?> +</ul> +<?php endforeach;?> + </div> + + <div class="modal-footer"> + <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> + <a type="button" class="btn btn-primary" href="/?closenotice=all">Mark all as read</a> + </div> + </div> + </div> + </div> +<?php endif; ?> + +</nav> +<div class="container"> + <header class="header"> + <?=genhtmltitle($pgtitle)?> + <ul class="context-links"> +<?php +if (!$hide_service_status && !empty($shortcuts[$shortcut_section]['service'])) { + $ssvc = array(); + switch ($shortcut_section) { + case "openvpn": + $ssvc = find_service_by_openvpn_vpnid($vpnid); + break; + case "captiveportal": + $ssvc = find_service_by_cp_zone($cpzone); + break; + default: + $ssvc = find_service_by_name($shortcuts[$shortcut_section]['service']); + } + if (!empty($ssvc)) { + // echo '<li>'. get_service_status_icon($ssvc, false). '</li>'; TODO: Add missing function + echo '<li>'. get_service_control_links($ssvc, false). '</li>'; + } +} + +echo '<li>'. get_shortcut_main_link($shortcut_section, false). '</li>'; +echo '<li>'. get_shortcut_status_link($shortcut_section, false). '</li>'; +echo '<li>'. get_shortcut_log_link($shortcut_section, false). '</li>'; + +?> + <?php if(! $g['disablehelpicon']): ?> + <li> + <a href="<?=$helpurl?>" title="<?=gettext("Help for items on this page")?>" class="help-icon"> + <i class="icon-large icon-question-sign"></i> + </a> + </li> + <?php endif?> + </ul> + </header> +<?php +/* if upgrade in progress, alert user */ +if (is_subsystem_dirty('packagelock') || file_exists('/conf/needs_package_sync' && platform_booting())) { + if (file_exists('/conf/needs_package_sync') && platform_booting()) { + $info_text = sprintf(gettext("%s is booting then packages will be reinstalled in the background.<p>Do not make changes in the GUI until this is complete."), $g['product_name']); + } else { + $pgtitle = array(gettext("System"),gettext("Package Manager")); + $info_text = gettext("Packages are currently being reinstalled in the background.<p>Do not make changes in the GUI until this is complete."); + } + print_info_box("{$info_text}<p><img src='/themes/{$g['theme']}/images/icons/icon_fw-update.gif' alt='firmware update' />"); +} + +$pgtitle_output = true; + +/* If this page is being remotely managed then do not allow the loading of the contents. */ +if ($config['remote_managed_pages']['item']) { + foreach($config['remote_managed_pages']['item'] as $rmp) { + if ($rmp == $_SERVER['SCRIPT_NAME']) { + print_info_box_np("This page is currently being managed by a remote machine."); + include("foot.inc"); + exit; + } + } +} diff --git a/src/usr/local/www/help.php b/src/usr/local/www/help.php new file mode 100644 index 0000000..a59c521 --- /dev/null +++ b/src/usr/local/www/help.php @@ -0,0 +1,425 @@ +<?php +/* + Redirector for Contextual Help System + */ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * c) 2009 Jim Pingle <jimp@pfsense.org> + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +require_once("guiconfig.inc"); + +/* Define hash of jumpto url maps */ + +/* Links to categories could probably be more specific. */ +$helppages = array( + /* These pages are confirmed to work and have usable content */ + 'index.php' => 'https://doc.pfsense.org/index.php/Dashboard', + 'license.php' => 'https://www.pfsense.org/about-pfsense/#legal', + 'miniupnpd.xml' => 'https://doc.pfsense.org/index.php/What_are_UPnP_and_NAT-PMP', + 'status_upnp.php' => 'https://doc.pfsense.org/index.php/What_are_UPnP_and_NAT-PMP', + 'firewall_virtual_ip.php' => 'https://doc.pfsense.org/index.php/What_are_Virtual_IP_Addresses', + 'firewall_virtual_ip_edit.php' => 'https://doc.pfsense.org/index.php/What_are_Virtual_IP_Addresses', + 'firewall_aliases.php' => 'https://doc.pfsense.org/index.php/Aliases', + 'firewall_aliases_edit.php' => 'https://doc.pfsense.org/index.php/Aliases', + 'firewall_aliases_import.php' => 'https://doc.pfsense.org/index.php/Aliases', + 'firewall_nat_out.php' => 'https://doc.pfsense.org/index.php/Outbound_NAT', + 'firewall_nat_out_edit.php' => 'https://doc.pfsense.org/index.php/Outbound_NAT', + 'firewall_rules.php' => 'https://doc.pfsense.org/index.php/Firewall_Rule_Basics', + 'firewall_rules_edit.php' => 'https://doc.pfsense.org/index.php/Firewall_Rule_Basics', + 'firewall_schedule.php' => 'https://doc.pfsense.org/index.php/Firewall_Rule_Schedules', + 'firewall_schedule_edit.php' => 'https://doc.pfsense.org/index.php/Firewall_Rule_Schedules', + 'interfaces_vlan.php' => 'https://doc.pfsense.org/index.php/VLAN_Trunking', + 'interfaces_vlan_edit.php' => 'https://doc.pfsense.org/index.php/VLAN_Trunking', + 'diag_routes.php' => 'https://doc.pfsense.org/index.php/Viewing_Routes', + 'diag_packet_capture.php' => 'https://doc.pfsense.org/index.php/Sniffers,_Packet_Capture', + 'diag_system_pftop.php' => 'https://doc.pfsense.org/index.php/How_can_I_monitor_bandwidth_usage#pftop', + 'status_rrd_graph.php' => 'https://doc.pfsense.org/index.php/RRD_Graphs', + 'status_rrd_graph_img.php' => 'https://doc.pfsense.org/index.php/RRD_Graphs', + 'status_rrd_graph_settings.php' => 'https://doc.pfsense.org/index.php/RRD_Graphs', + 'firewall_nat.php' => 'https://doc.pfsense.org/index.php/How_can_I_forward_ports_with_pfSense', + 'firewall_nat_edit.php' => 'https://doc.pfsense.org/index.php/How_can_I_forward_ports_with_pfSense', + 'diag_arp.php' => 'https://doc.pfsense.org/index.php/ARP_Table', + 'diag_backup.php' => 'https://doc.pfsense.org/index.php/Configuration_Backup_and_Restore', + 'diag_confbak.php' => 'https://doc.pfsense.org/index.php/Configuration_History', + 'diag_defaults.php' => 'https://doc.pfsense.org/index.php/Factory_Defaults', + 'firewall_shaper.php' => 'https://doc.pfsense.org/index.php/Traffic_Shaping_Guide', + 'firewall_shaper_layer7.php' => 'https://doc.pfsense.org/index.php/Layer_7', + 'firewall_shaper_queues.php' => 'https://doc.pfsense.org/index.php/Traffic_Shaping_Guide', + 'firewall_shaper_vinterface.php' => 'https://doc.pfsense.org/index.php/Limiters', + 'firewall_shaper_wizards.php' => 'https://doc.pfsense.org/index.php/Traffic_Shaping_Guide', + 'status_queues.php' => 'https://doc.pfsense.org/index.php/Traffic_Shaping_Guide', + 'status_dhcp_leases.php' => 'https://doc.pfsense.org/index.php/DHCP_Leases', + 'diag_dns.php' => 'https://doc.pfsense.org/index.php/DNS_Lookup', + 'diag_dump_states.php' => 'https://doc.pfsense.org/index.php/Show_States', + 'diag_resetstate.php' => 'https://doc.pfsense.org/index.php/Reset_States', + 'diag_logs.php' => 'https://doc.pfsense.org/index.php/System_Logs', + 'diag_logs_auth.php' => 'https://doc.pfsense.org/index.php/Captive_Portal_Authentication_Logs', + 'diag_logs_dhcp.php' => 'https://doc.pfsense.org/index.php/DHCP_Logs', + 'diag_logs_filter.php' => 'https://doc.pfsense.org/index.php/Firewall_Logs', + 'diag_logs_filter_dynamic.php' => 'https://doc.pfsense.org/index.php/Firewall_Logs', + 'diag_logs_filter_summary.php' => 'https://doc.pfsense.org/index.php/Firewall_Logs', + 'diag_logs_ntpd.php' => 'https://doc.pfsense.org/index.php/NTP_Logs', + 'diag_logs_ppp.php' => 'https://doc.pfsense.org/index.php/PPP_Logs', + 'diag_logs_relayd.php' => 'https://doc.pfsense.org/index.php/Load_Balancer_Logs', + 'diag_logs_settings.php' => 'https://doc.pfsense.org/index.php/Log_Settings', + 'diag_logs_vpn.php' => 'https://doc.pfsense.org/index.php/PPTP_VPN_Logs', + 'diag_logs_ipsec.php' => 'https://doc.pfsense.org/index.php/IPsec_Logs', + 'diag_logs_openvpn.php' => 'https://doc.pfsense.org/index.php/OpenVPN_Logs', + 'diag_nanobsd.php' => 'https://doc.pfsense.org/index.php/NanoBSD_Diagnostics', + 'diag_patterns.php' => 'https://doc.pfsense.org/index.php/Layer7_Pattern_Diagnostics', + 'diag_ping.php' => 'https://doc.pfsense.org/index.php/Ping_Host', + 'diag_pkglogs.php' => 'https://doc.pfsense.org/index.php/Package_Logs', + 'diag_tables.php' => 'https://doc.pfsense.org/index.php/Tables', + 'diag_system_activity.php' => 'https://doc.pfsense.org/index.php/System_Activity', + 'diag_traceroute.php' => 'https://doc.pfsense.org/index.php/Traceroute', + 'easyrule.php' => 'https://doc.pfsense.org/index.php/Easy_Rule', + 'edit.php' => 'https://doc.pfsense.org/index.php/Edit_File', + 'exec.php' => 'https://doc.pfsense.org/index.php/Execute_Command', + 'firewall_nat_1to1.php' => 'https://doc.pfsense.org/index.php/1:1_NAT', + 'firewall_nat_1to1_edit.php' => 'https://doc.pfsense.org/index.php/1:1_NAT', + 'halt.php' => 'https://doc.pfsense.org/index.php/Halt_System', + 'reboot.php' => 'https://doc.pfsense.org/index.php/Reboot_System', + 'status_filter_reload.php' => 'https://doc.pfsense.org/index.php/Filter_Reload_Status', + 'status_gateway_groups.php' => 'https://doc.pfsense.org/index.php/Gateway_Status', + 'status_gateways.php' => 'https://doc.pfsense.org/index.php/Gateway_Status', + 'status_graph.php' => 'https://doc.pfsense.org/index.php/Traffic_Graph', + 'status_graph_cpu.php' => 'https://doc.pfsense.org/index.php/CPU_Load', + 'status_interfaces.php' => 'https://doc.pfsense.org/index.php/Interface_Status', + 'status_services.php' => 'https://doc.pfsense.org/index.php/Services_Status', + 'status_wireless.php' => 'https://doc.pfsense.org/index.php/Wireless_Status', + 'pkg_mgr.php' => 'https://doc.pfsense.org/index.php/Package_Manager', + 'pkg_mgr_install.php' => 'https://doc.pfsense.org/index.php/Package_Manager', + 'pkg_mgr_installed.php' => 'https://doc.pfsense.org/index.php/Package_Manager', + 'pkg_mgr_settings.php' => 'https://doc.pfsense.org/index.php/Package_Manager_Settings', + 'interfaces.php' => 'https://doc.pfsense.org/index.php/Interface_Settings', + 'interfaces_assign.php' => 'https://doc.pfsense.org/index.php/Assign_Interfaces', + 'interfaces_bridge.php' => 'https://doc.pfsense.org/index.php/Interface_Bridges', + 'interfaces_bridge_edit.php' => 'https://doc.pfsense.org/index.php/Interface_Bridges', + 'interfaces_gif.php' => 'https://doc.pfsense.org/index.php/GIF_Interfaces', + 'interfaces_gif_edit.php' => 'https://doc.pfsense.org/index.php/GIF_Interfaces', + 'interfaces_gre.php' => 'https://doc.pfsense.org/index.php/GRE_Interfaces', + 'interfaces_gre_edit.php' => 'https://doc.pfsense.org/index.php/GRE_Interfaces', + 'interfaces_groups.php' => 'https://doc.pfsense.org/index.php/Interface_Groups', + 'interfaces_groups_edit.php' => 'https://doc.pfsense.org/index.php/Interface_Groups', + 'interfaces_lagg.php' => 'https://doc.pfsense.org/index.php/LAGG_Interfaces', + 'interfaces_lagg_edit.php' => 'https://doc.pfsense.org/index.php/LAGG_Interfaces', + 'interfaces_ppps.php' => 'https://doc.pfsense.org/index.php/PPP_Interfaces', + 'interfaces_ppps_edit.php' => 'https://doc.pfsense.org/index.php/PPP_Interfaces', + 'interfaces_qinq.php' => 'https://doc.pfsense.org/index.php/QinQ_Interfaces', + 'interfaces_qinq_edit.php' => 'https://doc.pfsense.org/index.php/QinQ_Interfaces', + 'services_dyndns.php' => 'https://doc.pfsense.org/index.php/Dynamic_DNS', + 'services_dyndns_edit.php' => 'https://doc.pfsense.org/index.php/Dynamic_DNS', + 'services_rfc2136.php' => 'https://doc.pfsense.org/index.php/Dynamic_DNS', + 'services_rfc2136_edit.php' => 'https://doc.pfsense.org/index.php/Dynamic_DNS', + 'services_dhcp.php' => 'https://doc.pfsense.org/index.php/DHCP_Server', + 'services_dhcp_edit.php' => 'https://doc.pfsense.org/index.php/DHCP_Server', + 'services_dhcp_relay.php' => 'https://doc.pfsense.org/index.php/DHCP_Relay', + 'services_dnsmasq.php' => 'https://doc.pfsense.org/index.php/DNS_Forwarder', + 'services_dnsmasq_domainoverride_edit.php' => 'https://doc.pfsense.org/index.php/DNS_Forwarder', + 'services_dnsmasq_edit.php' => 'https://doc.pfsense.org/index.php/DNS_Forwarder', + 'services_igmpproxy.php' => 'https://doc.pfsense.org/index.php/IGMP_Proxy', + 'services_igmpproxy_edit.php' => 'https://doc.pfsense.org/index.php/IGMP_Proxy', + 'services_snmp.php' => 'https://doc.pfsense.org/index.php/SNMP_Daemon', + 'services_wol.php' => 'https://doc.pfsense.org/index.php/Wake_on_LAN', + 'services_wol_edit.php' => 'https://doc.pfsense.org/index.php/Wake_on_LAN', + 'system.php' => 'https://doc.pfsense.org/index.php/General_Setup', + 'system_advanced_admin.php' => 'https://doc.pfsense.org/index.php/Advanced_Setup', + 'system_advanced_firewall.php' => 'https://doc.pfsense.org/index.php/Advanced_Setup#Firewall.2FNAT', + 'system_advanced_misc.php' => 'https://doc.pfsense.org/index.php/Advanced_Setup#Miscellaneous', + 'system_advanced_network.php' => 'https://doc.pfsense.org/index.php/Advanced_Setup#Firewall.2FNAT', + 'system_advanced_notifications.php' => 'https://doc.pfsense.org/index.php/Advanced_Setup#Notifications', + 'system_advanced_sysctl.php' => 'https://doc.pfsense.org/index.php/Advanced_Setup#System_Tunables', + 'system_firmware.php' => 'https://doc.pfsense.org/index.php/Firmware_Updates', + 'system_firmware_auto.php' => 'https://doc.pfsense.org/index.php/Firmware_Updates', + 'system_firmware_check.php' => 'https://doc.pfsense.org/index.php/Firmware_Updates', + 'system_firmware_settings.php' => 'https://doc.pfsense.org/index.php/Firmware_Updates', + 'system_gateway_groups.php' => 'https://doc.pfsense.org/index.php/Gateway_Settings', + 'system_gateway_groups_edit.php' => 'https://doc.pfsense.org/index.php/Gateway_Settings', + 'system_gateways.php' => 'https://doc.pfsense.org/index.php/Gateway_Settings', + 'system_gateways_edit.php' => 'https://doc.pfsense.org/index.php/Gateway_Settings', + 'system_routes.php' => 'https://doc.pfsense.org/index.php/Static_Routes', + 'system_routes_edit.php' => 'https://doc.pfsense.org/index.php/Static_Routes', + 'system_authservers.php' => 'https://doc.pfsense.org/index.php/User_Authentication_Servers', + 'system_groupmanager.php' => 'https://doc.pfsense.org/index.php/Group_Manager', + 'system_groupmanager_addprivs.php' => 'https://doc.pfsense.org/index.php/Group_Manager', + 'system_usermanager.php' => 'https://doc.pfsense.org/index.php/User_Manager', + 'system_usermanager_addprivs.php' => 'https://doc.pfsense.org/index.php/User_Manager', + 'system_usermanager_settings.php' => 'https://doc.pfsense.org/index.php/User_Manager', + 'system_usermanager_settings_ldapacpicker.php' => 'https://doc.pfsense.org/index.php/User_Manager', + 'system_usermanager_settings_test.php' => 'https://doc.pfsense.org/index.php/User_Manager', + 'system_usermanager_passwordmg.php' => 'https://doc.pfsense.org/index.php/User_Manager', + 'system_camanager.php' => 'https://doc.pfsense.org/index.php/Certificate_Management', + 'system_certmanager.php' => 'https://doc.pfsense.org/index.php/Certificate_Management', + 'vpn_l2tp.php' => 'https://doc.pfsense.org/index.php/L2TP_VPN_Settings', + 'vpn_l2tp_users.php' => 'https://doc.pfsense.org/index.php/L2TP_VPN_Settings', + 'vpn_l2tp_users_edit.php' => 'https://doc.pfsense.org/index.php/L2TP_VPN_Settings', + 'vpn_pppoe.php' => 'https://doc.pfsense.org/index.php/PPPoE_Server_Settings', + 'vpn_pppoe_edit.php' => 'https://doc.pfsense.org/index.php/PPPoE_Server_Settings', + 'vpn_pptp.php' => 'https://doc.pfsense.org/index.php/PPTP_VPN_Settings', + 'vpn_pptp_users.php' => 'https://doc.pfsense.org/index.php/PPTP_VPN_Settings', + 'vpn_pptp_users_edit.php' => 'https://doc.pfsense.org/index.php/PPTP_VPN_Settings', + 'diag_ipsec.php' => 'https://doc.pfsense.org/index.php/IPsec_Status', + 'diag_ipsec_sad.php' => 'https://doc.pfsense.org/index.php/IPsec_Status', + 'diag_ipsec_spd.php' => 'https://doc.pfsense.org/index.php/IPsec_Status', + 'vpn_ipsec.php' => 'https://doc.pfsense.org/index.php/IPsec_Tunnels', + 'vpn_ipsec_mobile.php' => 'https://doc.pfsense.org/index.php/IPsec_Mobile_Clients', + 'diag_ipsec_leases.php' => 'https://doc.pfsense.org/index.php/IPsec_Mobile_Clients', + 'vpn_ipsec_phase1.php' => 'https://doc.pfsense.org/index.php/IPsec_Tunnels', + 'vpn_ipsec_phase2.php' => 'https://doc.pfsense.org/index.php/IPsec_Tunnels', + 'vpn_ipsec_keys.php' => 'https://doc.pfsense.org/index.php/IPsec_Tunnels', + 'vpn_ipsec_keys_edit.php' => 'https://doc.pfsense.org/index.php/IPsec_Tunnels', + 'vpn_ipsec_settings.php' => 'https://doc.pfsense.org/index.php/Advanced_IPsec_Settings', + 'services_captiveportal.php' => 'https://doc.pfsense.org/index.php/Captive_Portal', + 'services_captiveportal_filemanager.php' => 'https://doc.pfsense.org/index.php/Captive_Portal', + 'services_captiveportal_ip.php' => 'https://doc.pfsense.org/index.php/Captive_Portal', + 'services_captiveportal_ip_edit.php' => 'https://doc.pfsense.org/index.php/Captive_Portal', + 'services_captiveportal_mac.php' => 'https://doc.pfsense.org/index.php/Captive_Portal', + 'services_captiveportal_mac_edit.php' => 'https://doc.pfsense.org/index.php/Captive_Portal', + 'services_captiveportal_hostname.php' => 'https://doc.pfsense.org/index.php/Captive_Portal', + 'services_captiveportal_hostname_edit.php' => 'https://doc.pfsense.org/index.php/Captive_Portal', + 'status_captiveportal.php' => 'https://doc.pfsense.org/index.php/Captive_Portal_Status', + 'status_captiveportal_test.php' => 'https://doc.pfsense.org/index.php/Captive_Portal_Status', + 'services_captiveportal_vouchers.php' => 'https://doc.pfsense.org/index.php/Captive_Portal_Vouchers', + 'services_captiveportal_vouchers_edit.php' => 'https://doc.pfsense.org/index.php/Captive_Portal_Vouchers', + 'status_captiveportal_voucher_rolls.php' => 'https://doc.pfsense.org/index.php/Captive_Portal_Vouchers', + 'status_captiveportal_vouchers.php' => 'https://doc.pfsense.org/index.php/Captive_Portal_Vouchers', + 'status_openvpn.php' => 'https://doc.pfsense.org/index.php/OpenVPN_Status', + 'vpn_openvpn_client.php' => 'https://doc.pfsense.org/index.php/OpenVPN_Settings', + 'vpn_openvpn_csc.php' => 'https://doc.pfsense.org/index.php/OpenVPN_Settings', + 'vpn_openvpn_server.php' => 'https://doc.pfsense.org/index.php/OpenVPN_Settings', + 'openvpn-client-export.xml' => 'https://doc.pfsense.org/index.php/OpenVPN_Client_Exporter', /* Package */ + 'vpn_openvpn_export.php' => 'https://doc.pfsense.org/index.php/OpenVPN_Client_Exporter', /* Package */ + 'diag_authentication.php' => 'https://doc.pfsense.org/index.php/User_Authentication_Servers', + 'diag_limiter_info.php' => 'https://doc.pfsense.org/index.php/Limiters', + 'diag_pf_info.php' => 'https://doc.pfsense.org/index.php/Packet_Filter_Information', + 'diag_smart.php' => 'https://doc.pfsense.org/index.php/SMART_Status', + 'diag_states_summary.php' => 'https://doc.pfsense.org/index.php/States_Summary', + 'interfaces_wireless.php' => 'https://doc.pfsense.org/index.php/Wireless_Interfaces', + 'interfaces_wireless_edit.php' => 'https://doc.pfsense.org/index.php/Wireless_Interfaces', + 'system_crlmanager.php' => 'https://doc.pfsense.org/index.php/Certificate_Management', + 'crash_reporter.php' => 'https://doc.pfsense.org/index.php/Unexpected_Reboot_Troubleshooting', + 'diag_dump_states_sources.php' => 'https://doc.pfsense.org/index.php/Show_Source_Tracking', + 'diag_logs_gateways.php' => 'https://doc.pfsense.org/index.php/Gateway_Logs', + 'diag_logs_resolver.php' => 'https://doc.pfsense.org/index.php/Resolver_Logs', + 'diag_logs_routing.php' => 'https://doc.pfsense.org/index.php/Routing_Logs', + 'diag_logs_wireless.php' => 'https://doc.pfsense.org/index.php/Wireless_Logs', + 'diag_ndp.php' => 'https://doc.pfsense.org/index.php/NDP_Table', + 'diag_sockets.php' => 'https://doc.pfsense.org/index.php/Diag_Sockets', + 'diag_testport.php' => 'https://doc.pfsense.org/index.php/Test_Port', + 'firewall_nat_npt.php' => 'https://doc.pfsense.org/index.php/NPt', + 'firewall_nat_npt_edit.php' => 'https://doc.pfsense.org/index.php/NPt', + 'services_captiveportal_zones.php' => 'https://doc.pfsense.org/index.php/Captive_Portal', + 'services_captiveportal_zones_edit.php' => 'https://doc.pfsense.org/index.php/Captive_Portal', + 'status_captiveportal_expire.php' => 'https://doc.pfsense.org/index.php/Captive_Portal', + 'services_ntpd.php' => 'https://doc.pfsense.org/index.php/NTP_Server', + 'status_ntpd.php' => 'https://doc.pfsense.org/index.php/NTP_Server', + 'services_ntpd_gps.php' => 'https://doc.pfsense.org/index.php/NTP_Server', + 'services_ntpd_pps.php' => 'https://doc.pfsense.org/index.php/NTP_Server', + 'system_firmware_restorefullbackup.php' => 'https://doc.pfsense.org/index.php/Full_Backup', + 'load_balancer_monitor.php' => 'https://doc.pfsense.org/index.php/Inbound_Load_Balancing', + 'load_balancer_monitor_edit.php' => 'https://doc.pfsense.org/index.php/Inbound_Load_Balancing', + 'load_balancer_pool.php' => 'https://doc.pfsense.org/index.php/Inbound_Load_Balancing#Set_up_Load_Balancing_Pool', + 'load_balancer_pool_edit.php' => 'https://doc.pfsense.org/index.php/Inbound_Load_Balancing#Set_up_Load_Balancing_Pool', + 'load_balancer_virtual_server.php' => 'https://doc.pfsense.org/index.php/Inbound_Load_Balancing#Set_up_Virtual_Server', + 'load_balancer_virtual_server_edit.php' => 'https://doc.pfsense.org/index.php/Inbound_Load_Balancing#Set_up_Virtual_Server', + 'load_balancer_setting.php' => 'https://doc.pfsense.org/index.php/Inbound_Load_Balancing#Advanced_Settings', + 'status_lb_pool.php' => 'https://doc.pfsense.org/index.php/Inbound_Load_Balancing_Status', + 'status_lb_vs.php' => 'https://doc.pfsense.org/index.php/Inbound_Load_Balancing_Status', + 'services_dhcpv6_relay.php' => 'https://doc.pfsense.org/index.php/DHCP_Relay', + 'status_dhcpv6_leases.php' => 'https://doc.pfsense.org/index.php/DHCPv6_Leases', + 'services_dhcpv6.php' => 'https://doc.pfsense.org/index.php/DHCPv6_Server', + 'services_dhcpv6_edit.php' => 'https://doc.pfsense.org/index.php/DHCPv6_Server', + 'services_router_advertisements.php' => 'https://doc.pfsense.org/index.php/Router_Advertisements', + 'carp_status.php' => 'https://doc.pfsense.org/index.php/CARP_Status', + 'system_hasync.php' => 'https://doc.pfsense.org/index.php/High_Availability', + 'services_unbound.php' => 'https://doc.pfsense.org/index.php/Unbound_DNS_Resolver', + 'services_unbound_advanced.php' => 'https://doc.pfsense.org/index.php/Unbound_DNS_Resolver#Advanced_Settings_Tab', + 'services_unbound_acls.php' => 'https://doc.pfsense.org/index.php/Unbound_DNS_Resolver#Access_Lists_Tab', + 'services_unbound_domainoverride_edit.php' => 'https://doc.pfsense.org/index.php/Unbound_DNS_Resolver', + 'services_unbound_host_edit.php' => 'https://doc.pfsense.org/index.php/Unbound_DNS_Resolver', + 'diag_gmirror.php' => 'https://doc.pfsense.org/index.php/Create_a_Software_RAID1_%28gmirror%29', + + /* From here down are packages. Not checking these as strictly, + any information is better than nothing. */ + 'olsrd.xml' => 'https://doc.pfsense.org/index.php/OLSR_Daemon', + 'routed.xml' => 'https://doc.pfsense.org/index.php/Routing_Information_Protocol_(RIP)', # RIP + 'autoconfigbackup.xml' => 'https://doc.pfsense.org/index.php/AutoConfigBackup', + 'phpservice.xml' => 'https://doc.pfsense.org/index.php/PHPService', + 'anyterm.xml' => 'https://doc.pfsense.org/index.php/AnyTerm_package', + 'avahi.xml' => 'https://doc.pfsense.org/index.php/Avahi_package', + 'squid.xml' => 'https://doc.pfsense.org/index.php/Category:Squid', + 'squid_auth.xml' => 'https://doc.pfsense.org/index.php/Category:Squid', + 'squid_cache.xml' => 'https://doc.pfsense.org/index.php/Category:Squid', + 'squid_extauth.xml' => 'https://doc.pfsense.org/index.php/Category:Squid', + 'squid_nac.xml' => 'https://doc.pfsense.org/index.php/Category:Squid', + 'squid_ng.xml' => 'https://doc.pfsense.org/index.php/Category:Squid', + 'squid_traffic.xml' => 'https://doc.pfsense.org/index.php/Category:Squid', + 'squid_upstream.xml' => 'https://doc.pfsense.org/index.php/Category:Squid', + 'squid_users.xml' => 'https://doc.pfsense.org/index.php/Category:Squid', + 'squidGuard.xml' => 'https://doc.pfsense.org/index.php/SquidGuard_package', + 'squidguard.xml' => 'https://doc.pfsense.org/index.php/SquidGuard_package', + 'squidguard_acl.xml' => 'https://doc.pfsense.org/index.php/SquidGuard_package', + 'squidguard_default.xml' => 'https://doc.pfsense.org/index.php/SquidGuard_package', + 'squidguard_dest.xml' => 'https://doc.pfsense.org/index.php/SquidGuard_package', + 'squidguard_log.xml' => 'https://doc.pfsense.org/index.php/SquidGuard_package', + 'squidguard_rewr.xml' => 'https://doc.pfsense.org/index.php/SquidGuard_package', + 'squidguard_time.xml' => 'https://doc.pfsense.org/index.php/SquidGuard_package', + 'bandwidthd.xml' => 'https://doc.pfsense.org/index.php/How_can_I_monitor_bandwidth_usage', + 'pfflowd.xml' => 'https://doc.pfsense.org/index.php/How_can_I_monitor_bandwidth_usage', + 'darkstat.xml' => 'https://doc.pfsense.org/index.php/How_can_I_monitor_bandwidth_usage', + 'rate.xml' => 'https://doc.pfsense.org/index.php/How_can_I_monitor_bandwidth_usage', + 'ntop.xml' => 'https://doc.pfsense.org/index.php/How_can_I_monitor_bandwidth_usage', + 'ntopng.xml' => 'https://doc.pfsense.org/index.php/How_can_I_monitor_bandwidth_usage', + 'vnstat.xml' => 'https://doc.pfsense.org/index.php/How_can_I_monitor_bandwidth_usage', + 'widentd.xml' => 'https://doc.pfsense.org/index.php/Widentd_package', + 'tinydns.xml' => 'https://doc.pfsense.org/index.php/Tinydns_package', + 'tinydns_domains.xml' => 'https://doc.pfsense.org/index.php/Tinydns_package', + 'tinydns_sync.xml' => 'https://doc.pfsense.org/index.php/Tinydns_package', + 'blinkled.xml' => 'https://doc.pfsense.org/index.php/BlinkLED_Package', + 'havp.xml' => 'https://doc.pfsense.org/index.php/HAVP_Package_for_HTTP_Anti-Virus_Scanning', + 'havp_avset.xml' => 'https://doc.pfsense.org/index.php/HAVP_Package_for_HTTP_Anti-Virus_Scanning', + 'havp_blacklist.xml' => 'https://doc.pfsense.org/index.php/HAVP_Package_for_HTTP_Anti-Virus_Scanning', + 'havp_fscan.xml' => 'https://doc.pfsense.org/index.php/HAVP_Package_for_HTTP_Anti-Virus_Scanning', + 'havp_trans_exclude.xml' => 'https://doc.pfsense.org/index.php/HAVP_Package_for_HTTP_Anti-Virus_Scanning', + 'havp_whitelist.xml' => 'https://doc.pfsense.org/index.php/HAVP_Package_for_HTTP_Anti-Virus_Scanning', + 'snort.xml' => 'https://doc.pfsense.org/index.php/Setup_Snort_Package', + 'snort/snort_interfaces.php' => 'https://doc.pfsense.org/index.php/Snort_interfaces', + 'snort/snort_interfaces_global.php' => 'https://doc.pfsense.org/index.php/Snort_interfaces_global', + 'snort/snort_download_updates.php' => 'https://doc.pfsense.org/index.php/Snort_updates', + 'snort/snort_alerts.php' => 'https://doc.pfsense.org/index.php/Snort_alerts', + 'snort/snort_blocked.php' => 'https://doc.pfsense.org/index.php/Snort_blocked_hosts', + 'snort/snort_passlist.php' => 'https://doc.pfsense.org/index.php/Snort_passlist', + 'snort/snort_passlist_edit.php' => 'https://doc.pfsense.org/index.php/Snort_passlist', + 'snort/snort_interfaces_suppress.php' => 'https://doc.pfsense.org/index.php/Snort_suppress_list', + 'snort/snort_interfaces_suppress_edit.php' => 'https://doc.pfsense.org/index.php/Snort_suppress_list', + 'snort/snort_interfaces_edit.php' => 'https://doc.pfsense.org/index.php/Snort_interfaces_edit', + 'snort/snort_rulesets.php' => 'https://doc.pfsense.org/index.php/Snort_rulesets', + 'snort/snort_rules.php' => 'https://doc.pfsense.org/index.php/Snort_rules', + 'snort/snort_define_servers.php' => 'https://doc.pfsense.org/index.php/Snort_define_servers', + 'snort/snort_preprocessors.php' => 'https://doc.pfsense.org/index.php/Snort_preprocessors', + 'snort/snort_barnyard.php' => 'https://doc.pfsense.org/index.php/Snort_barnyard2', + 'snort/snort_ip_reputation.php' => 'https://doc.pfsense.org/index.php/Snort_ip_reputation_preprocessor', + 'snort/snort_ip_list_mgmt.php' => 'https://doc.pfsense.org/index.php/Snort_ip_list_mgmt', + 'snort/snort_sync.xml' => 'https://doc.pfsense.org/index.php/Snort_sync', + 'stunnel.xml' => 'https://doc.pfsense.org/index.php/Stunnel_package', + 'stunnel_certs.xml' => 'https://doc.pfsense.org/index.php/Stunnel_package', + 'openbgpd.xml' => 'https://doc.pfsense.org/index.php/OpenBGPD_package', + 'openbgpd_groups.xml' => 'https://doc.pfsense.org/index.php/OpenBGPD_package', + 'openbgpd_neighbors.xml' => 'https://doc.pfsense.org/index.php/OpenBGPD_package', + 'iperf.xml' => 'https://doc.pfsense.org/index.php/Iperf_package', + 'iperfserver.xml' => 'https://doc.pfsense.org/index.php/Iperf_package', + 'jail_template.xml' => 'https://doc.pfsense.org/index.php/PfJailctl_package', + 'jailctl.xml' => 'https://doc.pfsense.org/index.php/PfJailctl_package', + 'jailctl_defaults.xml' => 'https://doc.pfsense.org/index.php/PfJailctl_package', + 'jailctl_settings.xml' => 'https://doc.pfsense.org/index.php/PfJailctl_package', + 'siproxd.xml' => 'https://doc.pfsense.org/index.php/Siproxd_package', + 'siproxdusers.xml' => 'https://doc.pfsense.org/index.php/Siproxd_package', + 'open-vm-tools.xml' => 'https://doc.pfsense.org/index.php/Open_VM_Tools_package', + 'arping.xml' => 'https://doc.pfsense.org/index.php/Arping_package', + 'unbound.xml' => 'https://doc.pfsense.org/index.php/Unbound_package', + 'nut.xml' => 'https://doc.pfsense.org/index.php/Nut_package', + +); + +$pagename = ""; +/* Check for parameter "page". */ +if ($_GET && isset($_GET['page'])) { + $pagename = $_GET['page']; +} + +/* If "page" is not found, check referring URL */ +if (empty($pagename)) { + /* Attempt to parse out filename */ + $uri_split = ""; + preg_match("/\/(.*)\?(.*)/", $_SERVER["HTTP_REFERER"], $uri_split); + + /* If there was no match, there were no parameters, just grab the filename + Otherwise, use the matched filename from above. */ + if (empty($uri_split[0])) { + $pagename = ltrim(parse_url($_SERVER["HTTP_REFERER"], PHP_URL_PATH), '/'); + } else { + $pagename = $uri_split[1]; + } + + /* If the page name is still empty, the user must have requested / (index.php) */ + if (empty($pagename)) { + $pagename = "index.php"; + } + + /* If the filename is pkg_edit.php or wizard.php, reparse looking + for the .xml filename */ + if (($pagename == "pkg.php") || ($pagename == "pkg_edit.php") || ($pagename == "wizard.php")) { + $param_split = explode('&', $uri_split[2]); + foreach ($param_split as $param) { + if (substr($param, 0, 4) == "xml=") { + $xmlfile = explode('=', $param); + $pagename = $xmlfile[1]; + } + } + } +} + +/* Using the derived page name, attempt to find in the URL mapping hash */ +if (array_key_exists($pagename, $helppages)) { + $helppage = $helppages[$pagename]; +} + +/* If we haven't determined a proper page, use a generic help page + stating that a given page does not have help yet. */ + +if (empty($helppage)) { + $helppage = 'https://doc.pfsense.org/index.php/No_Help_Found'; +} + +/* Redirect to help page. */ +header("Location: {$helppage}"); + +?> diff --git a/src/usr/local/www/ifstats.php b/src/usr/local/www/ifstats.php new file mode 100644 index 0000000..fe6ce70 --- /dev/null +++ b/src/usr/local/www/ifstats.php @@ -0,0 +1,91 @@ +<?php +/* $Id$ */ +/* + ifstats.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /usr/bin/netstat + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-xmlrpcinterfacestats +##|*NAME=XMLRPC Interface Stats page +##|*DESCR=Allow access to the 'XMLRPC Interface Stats' page. +##|*MATCH=ifstats.php* +##|-PRIV + + require_once('guiconfig.inc'); + require_once("interfaces.inc"); + + $if = $_GET['if']; + + $realif = get_real_interface($if); + if (!$realif) { + $realif = $if; // Need for IPsec case interface. + } + + $ifinfo = pfSense_get_interface_stats($realif); + + $temp = gettimeofday(); + $timing = (double)$temp["sec"] + (double)$temp["usec"] / 1000000.0; + + header("Last-Modified: " . gmdate("D, j M Y H:i:s") . " GMT"); + header("Expires: " . gmdate("D, j M Y H:i:s", time()) . " GMT"); + header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP/1.1 + header("Pragma: no-cache"); // HTTP/1.0 + + echo "$timing|" . $ifinfo['inbytes'] . "|" . $ifinfo['outbytes'] . "\n"; + +?> diff --git a/src/usr/local/www/includes/functions.inc.php b/src/usr/local/www/includes/functions.inc.php new file mode 100644 index 0000000..2c48543 --- /dev/null +++ b/src/usr/local/www/includes/functions.inc.php @@ -0,0 +1,407 @@ +<? +/* + functions.inc.php + pfSense_MODULE: ajax + Copyright (C) 2013-2015 Electric Sheep Fencing, LP +*/ + +if (Connection_Aborted()) { + exit; +} + +require_once("config.inc"); +require_once("pfsense-utils.inc"); + +function get_stats() { + $stats['cpu'] = cpu_usage(); + $stats['mem'] = mem_usage(); + $stats['uptime'] = get_uptime(); + $stats['states'] = get_pfstate(); + $stats['temp'] = get_temp(); + $stats['datetime'] = update_date_time(); + $stats['interfacestatistics'] = get_interfacestats(); + $stats['interfacestatus'] = get_interfacestatus(); + $stats['gateways'] = get_gatewaystats(); + $stats['cpufreq'] = get_cpufreq(); + $stats['load_average'] = get_load_average(); + $stats['mbuf'] = get_mbuf(); + $stats['mbufpercent'] = get_mbuf(true); + $stats['statepercent'] = get_pfstate(true); + $stats = join("|", $stats); + return $stats; +} + +function get_gatewaystats() { + global $config; + if (isset($config["widgets"]["gateways_widget"]["display_type"])) { + $display_type = $config["widgets"]["gateways_widget"]["display_type"]; + } else { + $display_type = "gw_ip"; + } + + $a_gateways = return_gateways_array(); + $gateways_status = array(); + $gateways_status = return_gateways_status(true); + $data = ""; + $isfirst = true; + foreach ($a_gateways as $gname => $gw) { + if (!$isfirst) { + $data .= ","; + } + $isfirst = false; + $data .= $gw['name'] . ","; + + $monitor_address = ""; + $monitor_address_disp = ""; + if ($display_type == "monitor_ip" || $display_type == "both_ip") { + $monitor_address = $gw['monitor']; + if ($monitor_address != "" && $display_type == "both_ip") { + $monitor_address_disp = " (" . $monitor_address . ")"; + } else { + $monitor_address_disp = $monitor_address; + } + } + + if ($gateways_status[$gname]) { + if ($display_type == "gw_ip" || $display_type == "both_ip" || ($display_type == "monitor_ip" && $monitor_address == "")) { + $if_gw = lookup_gateway_ip_by_name($gname); + } else { + $if_gw = ""; + } + if ($monitor_address == $if_gw) { + $monitor_address_disp = ""; + } + + $data .= "<b>" . $if_gw . $monitor_address_disp . "</b>,"; + $gws = $gateways_status[$gname]; + switch (strtolower($gws['status'])) { + case "none": + $online = "Online"; + $bgcolor = "#90EE90"; // lightgreen + break; + case "down": + $online = "Offline"; + $bgcolor = "#F08080"; // lightcoral + break; + case "delay": + $online = "Latency"; + $bgcolor = "#F0E68C"; // khaki + break; + case "loss": + $online = "Packetloss"; + $bgcolor = "#F0E68C"; // khaki + break; + default: + $online = "Pending"; + break; + } + } else { + if ($display_type == "gw_ip" || $display_type == "both_ip" || ($display_type == "monitor_ip" && $monitor_address == "")) { + $if_gw = "~"; + } else { + $if_gw = ""; + } + $data .= $if_gw . $monitor_address_disp . ","; + $gws['delay'] = "~"; + $gws['loss'] = "~"; + $online = "Unknown"; + $bgcolor = "#ADD8E6"; // lightblue + } + $data .= ($online == "Pending") ? "{$online},{$online}," : "{$gws['delay']},{$gws['loss']},"; + $data .= "{$online}^{$bgcolor}"; + } + return $data; +} + +function get_uptime() { + $uptime = get_uptime_sec(); + + if (intval($uptime) == 0) { + return; + } + + $updays = (int)($uptime / 86400); + $uptime %= 86400; + $uphours = (int)($uptime / 3600); + $uptime %= 3600; + $upmins = (int)($uptime / 60); + $uptime %= 60; + $upsecs = (int)($uptime); + + $uptimestr = ""; + if ($updays > 1) { + $uptimestr .= "$updays Days "; + } else if ($updays > 0) { + $uptimestr .= "1 Day "; + } + + if ($uphours > 1) { + $hours = "s"; + } + + if ($upmins > 1) { + $minutes = "s"; + } + + if ($upmins > 1) { + $seconds = "s"; + } + + $uptimestr .= sprintf("%02d Hour$hours %02d Minute$minutes %02d Second$seconds", $uphours, $upmins, $upsecs); + return $uptimestr; +} + +/* Calculates non-idle CPU time and returns as a percentage */ +function cpu_usage() { + $duration = 1; + $diff = array('user', 'nice', 'sys', 'intr', 'idle'); + $cpuTicks = array_combine($diff, explode(" ", get_single_sysctl('kern.cp_time'))); + sleep($duration); + $cpuTicks2 = array_combine($diff, explode(" ", get_single_sysctl('kern.cp_time'))); + + $totalStart = array_sum($cpuTicks); + $totalEnd = array_sum($cpuTicks2); + + // Something wrapped ?!?! + if ($totalEnd <= $totalStart) { + return 0; + } + + // Calculate total cycles used + $totalUsed = ($totalEnd - $totalStart) - ($cpuTicks2['idle'] - $cpuTicks['idle']); + + // Calculate the percentage used + $cpuUsage = floor(100 * ($totalUsed / ($totalEnd - $totalStart))); + + return $cpuUsage; +} + +function get_pfstate($percent=false) { + global $config; + $matches = ""; + if (isset($config['system']['maximumstates']) and $config['system']['maximumstates'] > 0) { + $maxstates="{$config['system']['maximumstates']}"; + } else { + $maxstates=pfsense_default_state_size(); + } + $curentries = `/sbin/pfctl -si |grep current`; + if (preg_match("/([0-9]+)/", $curentries, $matches)) { + $curentries = $matches[1]; + } + if (!is_numeric($curentries)) { + $curentries = 0; + } + if ($percent) { + if (intval($maxstates) > 0) { + return round(($curentries / $maxstates) * 100, 0); + } else { + return "NA"; + } + } else { + return $curentries . "/" . $maxstates; + } +} + +function has_temp() { + /* no known temp monitors available at present */ + + /* should only reach here if there is no hardware monitor */ + return false; +} + +function get_hwtype() { + return; +} + +function get_mbuf($percent=false) { + $mbufs_output=trim(`/usr/bin/netstat -mb | /usr/bin/grep "mbuf clusters in use" | /usr/bin/awk '{ print $1 }'`); + list($mbufs_current, $mbufs_cache, $mbufs_total, $mbufs_max) = explode("/", $mbufs_output); + if ($percent) { + if ($mbufs_max > 0) { + return round(($mbufs_total / $mbufs_max) * 100, 0); + } else { + return "NA"; + } + } else { + return "{$mbufs_total}/{$mbufs_max}"; + } +} + +function get_temp() { + $temp_out = get_single_sysctl("dev.cpu.0.temperature"); + if ($temp_out == "") { + $temp_out = get_single_sysctl("hw.acpi.thermal.tz0.temperature"); + } + + // Remove 'C' from the end + return rtrim($temp_out, 'C'); +} + +/* Get mounted filesystems and usage. Do not display entries for virtual filesystems (e.g. devfs, nullfs, unionfs) */ +function get_mounted_filesystems() { + $mout = ""; + $filesystems = array(); + exec("/bin/df -Tht ufs,zfs,cd9660 | /usr/bin/awk '{print $1, $2, $3, $6, $7;}'", $mout); + + /* Get rid of the header */ + array_shift($mout); + foreach ($mout as $fs) { + $f = array(); + list($f['device'], $f['type'], $f['total_size'], $f['percent_used'], $f['mountpoint']) = explode(' ', $fs); + + /* We dont' want the trailing % sign. */ + $f['percent_used'] = trim($f['percent_used'], '%'); + + $filesystems[] = $f; + } + return $filesystems; +} + +function disk_usage($slice = '/') { + $dfout = ""; + exec("/bin/df -h {$slice} | /usr/bin/tail -n 1 | /usr/bin/awk '{ print $5 }' | /usr/bin/cut -d '%' -f 1", $dfout); + $diskusage = trim($dfout[0]); + + return $diskusage; +} + +function swap_usage() { + exec("/usr/sbin/swapinfo", $swap_info); + $swap_used = ""; + foreach ($swap_info as $line) { + if (preg_match('/(\d+)%$/', $line, $matches)) { + $swap_used = $matches[1]; + break; + } + } + + return $swap_used; +} + +function mem_usage() { + $totalMem = get_single_sysctl("vm.stats.vm.v_page_count"); + if ($totalMem > 0) { + $inactiveMem = get_single_sysctl("vm.stats.vm.v_inactive_count"); + $cachedMem = get_single_sysctl("vm.stats.vm.v_cache_count"); + $freeMem = get_single_sysctl("vm.stats.vm.v_free_count"); + $usedMem = $totalMem - ($inactiveMem + $cachedMem + $freeMem); + $memUsage = round(($usedMem * 100) / $totalMem, 0); + } else { + $memUsage = "NA"; + } + + return $memUsage; +} + +function update_date_time() { + $datetime = date("D M j G:i:s T Y"); + return $datetime; +} + +function get_cpufreq() { + $cpufreqs = ""; + $out = ""; + $cpufreqs = explode(" ", get_single_sysctl('dev.cpu.0.freq_levels')); + $maxfreq = explode("/", $cpufreqs[0]); + $maxfreq = $maxfreq[0]; + $curfreq = ""; + $curfreq = get_single_sysctl('dev.cpu.0.freq'); + if (($curfreq > 0) && ($curfreq != $maxfreq)) { + $out = "Current: {$curfreq} MHz, Max: {$maxfreq} MHz"; + } + return $out; +} + +function get_cpu_count($show_detail = false) { + $cpucount = get_single_sysctl('kern.smp.cpus'); + + if ($show_detail) { + $cpudetail = ""; + exec("/usr/bin/grep 'SMP.*package.*core' /var/log/dmesg.boot | /usr/bin/cut -f2- -d' '", $cpudetail); + $cpucount = $cpudetail[0]; + } + return $cpucount; +} + +function get_load_average() { + $load_average = ""; + exec("/usr/bin/uptime | /usr/bin/sed 's/^.*: //'", $load_average); + return $load_average[0]; +} + +function get_interfacestats() { + global $config; + //build interface list for widget use + $ifdescrs = get_configured_interface_list(); + + $array_in_packets = array(); + $array_out_packets = array(); + $array_in_bytes = array(); + $array_out_bytes = array(); + $array_in_errors = array(); + $array_out_errors = array(); + $array_collisions = array(); + $array_interrupt = array(); + $new_data = ""; + + //build data arrays + foreach ($ifdescrs as $ifdescr => $ifname) { + $ifinfo = get_interface_info($ifdescr); + $new_data .= "{$ifinfo['inpkts']},"; + $new_data .= "{$ifinfo['outpkts']},"; + $new_data .= format_bytes($ifinfo['inbytes']) . ","; + $new_data .= format_bytes($ifinfo['outbytes']) . ","; + if (isset($ifinfo['inerrs'])) { + $new_data .= "{$ifinfo['inerrs']},"; + $new_data .= "{$ifinfo['outerrs']},"; + } else { + $new_data .= "0,"; + $new_data .= "0,"; + } + if (isset($ifinfo['collisions'])) { + $new_data .= htmlspecialchars($ifinfo['collisions']) . ","; + } else { + $new_data .= "0,"; + } + }//end for + + return $new_data; +} + +function get_interfacestatus() { + $data = ""; + global $config; + + //build interface list for widget use + $ifdescrs = get_configured_interface_with_descr(); + + foreach ($ifdescrs as $ifdescr => $ifname) { + $ifinfo = get_interface_info($ifdescr); + $data .= $ifname . "^"; + if ($ifinfo['status'] == "up" || $ifinfo['status'] == "associated") { + $data .= "up"; + } else if ($ifinfo['status'] == "no carrier") { + $data .= "down"; + } else if ($ifinfo['status'] == "down") { + $data .= "block"; + } + $data .= "^"; + if ($ifinfo['ipaddr']) { + $data .= "<strong>" . htmlspecialchars($ifinfo['ipaddr']) . "</strong>"; + } + $data .= "^"; + if ($ifinfo['ipaddrv6']) { + $data .= "<strong>" . htmlspecialchars($ifinfo['ipaddrv6']) . "</strong>"; + } + $data .= "^"; + if ($ifinfo['status'] != "down") { + $data .= htmlspecialchars($ifinfo['media']); + } + + $data .= "~"; + + } + return $data; +} + +?> diff --git a/src/usr/local/www/index.php b/src/usr/local/www/index.php new file mode 100644 index 0000000..9f3b625 --- /dev/null +++ b/src/usr/local/www/index.php @@ -0,0 +1,435 @@ +<?php +/* $Id$ */ +/* + index.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004-2012 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-system-login/logout +##|*NAME=System: Login / Logout page / Dashboard +##|*DESCR=Allow access to the 'System: Login / Logout' page and Dashboard. +##|*MATCH=index.php* +##|-PRIV + +// Turn on buffering to speed up rendering +ini_set('output_buffering', 'true'); + +// Start buffering with a cache size of 100000 +ob_start(null, "1000"); + + +## Load Essential Includes +require_once('guiconfig.inc'); +require_once('functions.inc'); +require_once('notices.inc'); +require_once("pkg-utils.inc"); + +if (isset($_REQUEST['closenotice'])) { + close_notice($_REQUEST['closenotice']); + echo get_menu_messages(); + exit; +} + +if ($g['disablecrashreporter'] != true) { + // Check to see if we have a crash report + $x = 0; + if (file_exists("/tmp/PHP_errors.log")) { + $total = `/usr/bin/grep -vi warning /tmp/PHP_errors.log | /usr/bin/wc -l | /usr/bin/awk '{ print $1 }'`; + if ($total > 0) { + $x++; + } + } + $crash = glob("/var/crash/*"); + $skip_files = array(".", "..", "minfree", ""); + if (is_array($crash)) { + foreach ($crash as $c) { + if (!in_array(basename($c), $skip_files)) { + $x++; + } + } + if ($x > 0) { + $savemsg = "{$g['product_name']} has detected a crash report or programming bug. Click <a href='crash_reporter.php'>here</a> for more information."; + } + } +} + +##build list of widgets +foreach (glob("/usr/local/www/widgets/widgets/*.widget.php") as $file) +{ + $name = basename($file, '.widget.php'); + $widgets[ $name ] = array('name' => ucwords(str_replace('_', ' ', $name)), 'display' => 'none'); +} + +##insert the system information widget as first, so as to be displayed first +unset($widgets['system_information']); +$widgets = array_merge(array('system_information' => array('name' => 'System Information')), $widgets); + +##if no config entry found, initialize config entry +if (!is_array($config['widgets'])) { + $config['widgets'] = array(); +} + +if ($_POST && $_POST['sequence']) { + $config['widgets']['sequence'] = rtrim($_POST['sequence'], ','); + + foreach($widgets as $widgetname => $widgetconfig){ + if ($_POST[$widgetname . '-config']){ + $config['widgets'][$widgetname . '-config'] = $_POST[$name . '-config']; + } + } + + write_config(gettext("Widget configuration has been changed.")); + header("Location: /"); + exit; +} + +## Load Functions Files +require_once('includes/functions.inc.php'); + +## Check to see if we have a swap space, +## if true, display, if false, hide it ... +if(file_exists("/usr/sbin/swapinfo")) { + $swapinfo = `/usr/sbin/swapinfo`; + if(stristr($swapinfo,'%') == true) $showswap=true; +} + +## User recently restored his config. +## If packages are installed lets resync +if(file_exists('/conf/needs_package_sync')) { + if($config['installedpackages'] <> '' && is_array($config['installedpackages']['package'])) { + if($g['platform'] == "pfSense" || $g['platform'] == "nanobsd") { + ## If the user has logged into webGUI quickly while the system is booting then do not redirect them to + ## the package reinstall page. That is about to be done by the boot script anyway. + ## The code in head.inc will put up a notice to the user. + if (!platform_booting()) { + header('Location: pkg_mgr_install.php?mode=reinstallall'); + exit; + } + } + } else { + conf_mount_rw(); + @unlink('/conf/needs_package_sync'); + conf_mount_ro(); + } +} + +## If it is the first time webConfigurator has been +## accessed since initial install show this stuff. +if(file_exists('/conf/trigger_initial_wizard')) { +?> +<!DOCTYPE html> +<html lang="en"> +<head> + <link rel="stylesheet" href="/bootstrap/css/pfSense.css" /> + <title>{$g['product_name']}.localdomain - {$g['product_name']} first time setup</title> + <meta http-equiv="refresh" content="1;url=wizard.php?xml=setup_wizard.xml" /> +</head> +<body id="loading-wizard" class="no-menu"> + <div id="jumbotron"> + <div class="container"> + <div class="col-sm-offset-3 col-sm-6 col-xs-12"> + <p><?=sprintf(gettext("Welcome to %s!\n"),$g['product_name'])?></p> + <p><?=gettext("One moment while we start the initial setup wizard.")?></p> + <p><?=gettext("Embedded platform users: Please be patient, the wizard takes a little longer to run than the normal GUI.")?></p> + <p><?=sprintf(gettext("To bypass the wizard, click on the %s logo on the initial page."),$g['product_name'])?></p> + </div> + </div> + </div> +</body> +</html> +<?php + exit; +} + +## Find out whether there's hardware encryption or not +unset($hwcrypto); +$fd = @fopen("{$g['varlog_path']}/dmesg.boot", "r"); +if ($fd) { + while (!feof($fd)) { + $dmesgl = fgets($fd); + if (preg_match("/^hifn.: (.*?),/", $dmesgl, $matches) + or preg_match("/.*(VIA Padlock)/", $dmesgl, $matches) + or preg_match("/^safe.: (\w.*)/", $dmesgl, $matches) + or preg_match("/^ubsec.: (.*?),/", $dmesgl, $matches) + or preg_match("/^padlock.: <(.*?)>,/", $dmesgl, $matches) + or preg_match("/^glxsb.: (.*?),/", $dmesgl, $matches) + or preg_match("/^aesni.: (.*?),/", $dmesgl, $matches)) { + $hwcrypto = $matches[1]; + break; + } + } + fclose($fd); +} + +##build widget saved list information +if ($config['widgets'] && $config['widgets']['sequence'] != "") { + $pconfig['sequence'] = $config['widgets']['sequence']; + $widgetsfromconfig = array(); + + foreach (explode(',', $pconfig['sequence']) as $line) + { + list($file, $col, $display) = explode(':', $line); + + // be backwards compatible + $offset = strpos($file, '-container'); + if (false !== $offset) + $file = substr($file, 0, $offset); + + $widgetsfromconfig[ $file ] = array( + 'name' => ucwords(str_replace('_', ' ', $file)), + 'col' => $col, + 'display' => $display, + ); + } + + ##add widgets that may not be in the saved configuration, in case they are to be displayed later + $widgets = $widgetsfromconfig + $widgets; + + ##find custom configurations of a particular widget and load its info to $pconfig + foreach($widgets as $widgetname => $widgetconfig){ + if ($config['widgets'][$name . '-config']){ + $pconfig[$name . '-config'] = $config['widgets'][$name . '-config']; + } + } +} + +##build list of php include files +$phpincludefiles = array(); +$directory = "/usr/local/www/widgets/include/"; +$dirhandle = opendir($directory); +$filename = ""; +while (false !== ($filename = readdir($dirhandle))) { + $phpincludefiles[] = $filename; +} +foreach ($phpincludefiles as $includename) { + if (!stristr($includename, ".inc")) { + continue; + } + include($directory . $includename); +} + +## Set Page Title and Include Header +$pgtitle = array(gettext("Status: Dashboard")); +include("head.inc"); + +/* Print package server mismatch warning. See https://redmine.pfsense.org/issues/484 */ +if (!verify_all_package_servers()) { + print_info_box(package_server_mismatch_message()); +} + +if ($savemsg) { + print_info_box($savemsg); +} + +pfSense_handle_custom_code("/usr/local/pkg/dashboard/pre_dashboard"); + +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Available Widgets"); ?></h2></div> + <div class="panel-body"> +<?php foreach($widgets as $widgetname => $widgetconfig): ?> + <?php if ($widgetconfig['display'] == 'none'): ?> + <div class="col-sm-3"><a href="#"><i class="icon icon-plus"></i> <?=$widgetconfig['name']?></a></div> + <?php endif; ?> +<?php endforeach; ?> + </div> +</div> + +<div class="modal fade"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> + <h4 class="modal-title"><?=gettext("Welcome to the Dashboard page"); ?>!</h4> + </div> + <div class="modal-body"> + <p> + <?=gettext("This page allows you to customize the information you want to be displayed!");?> + <?=gettext("To get started click the");?> FIXME <?=gettext("icon to add widgets.");?><br /> + <br /> + <?=gettext("You can move any widget around by clicking and dragging the title.");?> + </p> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-default btn-primary" data-dismiss="modal">Close</button> + </div> + </div> + </div> +</div> + +<div class="hidden" id="widgetSequence"> + <form action="/" method="post" id="widgetSequence"> + <input type="hidden" name="sequence" value="" /> + + <button type="submit" class="btn btn-primary">Store widget configuration</button> + </form> +</div> + +<?php +$widgetColumns = array(); +foreach ($widgets as $widgetname => $widgetconfig) +{ + if ($widgetconfig['display'] == 'none') + continue; + + if (!isset($widgetColumns[ $widgetconfig['col'] ])) + $widgetColumns[ $widgetconfig['col'] ] = array(); + + $widgetColumns[ $widgetconfig['col'] ][ $widgetname ] = $widgetconfig; +} +?> + +<div class="row"> +<?php foreach ($widgetColumns as $column => $columnWidgets):?> + <div class="col-md-6" id="widgets-<?=$column?>"> +<?php foreach ($columnWidgets as $widgetname => $widgetconfig):?> + <div class="panel panel-default" id="widget-<?=$widgetname?>"> + <div class="panel-heading"> + <?=$widgetconfig['name']?> + <span class="icons"> + <a data-toggle="collapse" href="#widget-<?=$widgetname?> .panel-footer" class="config hidden"> + <i class="icon icon-wrench"></i> + </a> + <a data-toggle="collapse" href="#widget-<?=$widgetname?> .panel-body"> + <!-- actual icon is determined in css based on state of body --> + <i class="icon icon-plus-sign"></i> + </a> + <a data-toggle="close" href="#widget-<?=$widgetname?>"> + <i class="icon icon-remove-sign"></i> + </a> + </span> + </div> + <div class="panel-body collapse<?=($widgetconfig['display']=='close' ? '' : ' in')?>"> + <?php include('/usr/local/www/widgets/widgets/'. $widgetname.'.widget.php'); ?> + </div> + </div> +<?php endforeach; ?> + </div> +<?php endforeach; ?> +</div> + +<script> +function updateWidgets() +{ + var sequence = ''; + + $('.container .col-md-6').each(function(idx, col){ + $('.panel', col).each(function(idx, widget){ + var isOpen = $('.panel-body', widget).hasClass('in'); + + sequence += widget.id.split('-')[1] +':'+ col.id.split('-')[1] +':'+ (isOpen ? 'open' : 'close') +','; + }); + }); + + $('#widgetSequence').removeClass('hidden'); + $('input[name=sequence]', $('#widgetSequence')).val(sequence); +} + +events.push(function() { + // Hide configuration button for panels without configuration + $('.container .panel-heading a.config').each(function (idx, el){ + var config = $(el).parents('.panel').children('.panel-footer'); + if (config.length == 1) + $(el).removeClass('hidden'); + }); + + // Initial state & toggle icons of collapsed panel + $('.container .panel-heading a[data-toggle="collapse"]').each(function (idx, el){ + var body = $(el).parents('.panel').children('.panel-body'), isOpen = body.hasClass('in'); + $(el).toggleClass('icon-plus-sign', !isOpen); + $(el).toggleClass('icon-minus-sign', isOpen); + + body.on('shown.bs.collapse', function(){ + $(el).toggleClass('icon-minus-sign', true); + $(el).toggleClass('icon-plus-sign', false); + + updateWidgets(); + }); + + body.on('hidden.bs.collapse', function(){ + $(el).toggleClass('icon-minus-sign', false); + $(el).toggleClass('icon-plus-sign', true); + + updateWidgets(); + }); + }); + + // Make panels destroyable + $('.container .panel-heading a[data-toggle="close"]').each(function (idx, el){ + $(el).on('click', function(e){ + $(el).parents('.panel').remove(); + updateWidgets(); + }) + }); + + // Make panels sortable + $('.container .col-md-6').sortable({ + handle: '.panel-heading', + cursor: 'grabbing', + connectWith: '.container .col-md-6', + update: updateWidgets + }); +}); +</script> +<?php +//build list of javascript include files +foreach (glob('widgets/javascript/*.js') as $file) + echo '<script src="'.$file.'"></script>'; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/interfaces.php b/src/usr/local/www/interfaces.php new file mode 100644 index 0000000..9533b8b --- /dev/null +++ b/src/usr/local/www/interfaces.php @@ -0,0 +1,3826 @@ +<?php +/* $Id$ */ +/* + interfaces.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004-2008 Scott Ullrich + * Copyright (c) 2006 Daniel S. Haischt + * Copyright (c) 2008-2010 Ermal Luçi + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_BUILDER_BINARIES: /usr/sbin/arp + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces +##|*NAME=Interfaces: WAN page +##|*DESCR=Allow access to the 'Interfaces' page. +##|*MATCH=interfaces.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("ipsec.inc"); +require_once("functions.inc"); +require_once("captiveportal.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("rrd.inc"); +require_once("vpn.inc"); +require_once("xmlparse_attr.inc"); + + +if (isset($_POST['referer'])) { + $referer = $_POST['referer']; +} else { + $referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/interfaces.php'); +} + +// Get configured interface list +$ifdescrs = get_configured_interface_with_descr(false, true); + +$if = "wan"; +if ($_REQUEST['if']) { + $if = $_REQUEST['if']; +} + +if (empty($ifdescrs[$if])) { + header("Location: interfaces.php"); + exit; +} + +define("CRON_MONTHLY_PATTERN", "0 0 1 * *"); +define("CRON_WEEKLY_PATTERN", "0 0 * * 0"); +define("CRON_DAILY_PATTERN", "0 0 * * *"); +define("CRON_HOURLY_PATTERN", "0 * * * *"); + +if (!is_array($pconfig)) { + $pconfig = array(); +} + +if (!is_array($config['ppps'])) { + $config['ppps'] = array(); +} +if (!is_array($config['ppps']['ppp'])) { + $config['ppps']['ppp'] = array(); +} +$a_ppps = &$config['ppps']['ppp']; + +function remove_bad_chars($string) { + return preg_replace('/[^a-z_0-9]/i', '', $string); +} + +if (!is_array($config['gateways']['gateway_item'])) { + $config['gateways']['gateway_item'] = array(); +} +$a_gateways = &$config['gateways']['gateway_item']; + +$wancfg = &$config['interfaces'][$if]; +$old_wancfg = $wancfg; +$old_wancfg['realif'] = get_real_interface($if); +$old_ppps = $a_ppps; +// Populate page descr if it does not exist. +if ($if == "wan" && !$wancfg['descr']) { + $wancfg['descr'] = "WAN"; +} else if ($if == "lan" && !$wancfg['descr']) { + $wancfg['descr'] = "LAN"; +} + +/* NOTE: The code here is used to set the $pppid for the curious */ +foreach ($a_ppps as $pppid => $ppp) { + if ($wancfg['if'] == $ppp['if']) { + break; + } +} + +$type_disabled = (substr($wancfg['if'], 0, 3) == 'gre') ? 'disabled="disabled"' : ''; + +if ($wancfg['if'] == $a_ppps[$pppid]['if']) { + $pconfig['pppid'] = $pppid; + $pconfig['ptpid'] = $a_ppps[$pppid]['ptpid']; + $pconfig['port'] = $a_ppps[$pppid]['ports']; + if ($a_ppps[$pppid]['type'] == "ppp") { + $pconfig['ppp_username'] = $a_ppps[$pppid]['username']; + $pconfig['ppp_password'] = base64_decode($a_ppps[$pppid]['password']); + + $pconfig['phone'] = $a_ppps[$pppid]['phone']; + $pconfig['apn'] = $a_ppps[$pppid]['apn']; + } else if ($a_ppps[$pppid]['type'] == "pppoe") { + $pconfig['pppoe_username'] = $a_ppps[$pppid]['username']; + $pconfig['pppoe_password'] = base64_decode($a_ppps[$pppid]['password']); + $pconfig['provider'] = $a_ppps[$pppid]['provider']; + $pconfig['pppoe_dialondemand'] = isset($a_ppps[$pppid]['ondemand']); + $pconfig['pppoe_idletimeout'] = $a_ppps[$pppid]['idletimeout']; + + /* ================================================ */ + /* = force a connection reset at a specific time? = */ + /* ================================================ */ + + if (isset($a_ppps[$pppid]['pppoe-reset-type'])) { + $pconfig['pppoe-reset-type'] = $a_ppps[$pppid]['pppoe-reset-type']; + $itemhash = getMPDCRONSettings($a_ppps[$pppid]['if']); + if ($itemhash) { + $cronitem = $itemhash['ITEM']; + } + if (isset($cronitem)) { + $resetTime = "{$cronitem['minute']} {$cronitem['hour']} {$cronitem['mday']} {$cronitem['month']} {$cronitem['wday']}"; + } else { + $resetTime = NULL; + } + //log_error("ResetTime:".$resetTime); + if ($a_ppps[$pppid]['pppoe-reset-type'] == "custom") { + if ($cronitem) { + $pconfig['pppoe_pr_custom'] = true; + $pconfig['pppoe_resetminute'] = $cronitem['minute']; + $pconfig['pppoe_resethour'] = $cronitem['hour']; + if ($cronitem['mday'] != "*" && $cronitem['month'] != "*") { + $pconfig['pppoe_resetdate'] = "{$cronitem['month']}/{$cronitem['mday']}/" . date("Y"); + } + } + } else if ($a_ppps[$pppid]['pppoe-reset-type'] == "preset") { + $pconfig['pppoe_pr_preset'] = true; + switch ($resetTime) { + case CRON_MONTHLY_PATTERN: + $pconfig['pppoe_monthly'] = true; + break; + case CRON_WEEKLY_PATTERN: + $pconfig['pppoe_weekly'] = true; + break; + case CRON_DAILY_PATTERN: + $pconfig['pppoe_daily'] = true; + break; + case CRON_HOURLY_PATTERN: + $pconfig['pppoe_hourly'] = true; + break; + } + } + } // End force pppoe reset at specific time + // End if type == pppoe + } else if ($a_ppps[$pppid]['type'] == "pptp" || $a_ppps[$pppid]['type'] == "l2tp") { + $pconfig['pptp_username'] = $a_ppps[$pppid]['username']; + $pconfig['pptp_password'] = base64_decode($a_ppps[$pppid]['password']); + $pconfig['pptp_local'] = explode(",", $a_ppps[$pppid]['localip']); + $pconfig['pptp_subnet'] = explode(",", $a_ppps[$pppid]['subnet']); + $pconfig['pptp_remote'] = explode(",", $a_ppps[$pppid]['gateway']); + $pconfig['pptp_dialondemand'] = isset($a_ppps[$pppid]['ondemand']); + $pconfig['pptp_idletimeout'] = $a_ppps[$pppid]['timeout']; + } +} else { + $pconfig['ptpid'] = interfaces_ptpid_next(); + $pppid = count($a_ppps); +} +$pconfig['dhcphostname'] = $wancfg['dhcphostname']; +$pconfig['alias-address'] = $wancfg['alias-address']; +$pconfig['alias-subnet'] = $wancfg['alias-subnet']; +$pconfig['dhcprejectfrom'] = $wancfg['dhcprejectfrom']; + +$pconfig['adv_dhcp_pt_timeout'] = $wancfg['adv_dhcp_pt_timeout']; +$pconfig['adv_dhcp_pt_retry'] = $wancfg['adv_dhcp_pt_retry']; +$pconfig['adv_dhcp_pt_select_timeout'] = $wancfg['adv_dhcp_pt_select_timeout']; +$pconfig['adv_dhcp_pt_reboot'] = $wancfg['adv_dhcp_pt_reboot']; +$pconfig['adv_dhcp_pt_backoff_cutoff'] = $wancfg['adv_dhcp_pt_backoff_cutoff']; +$pconfig['adv_dhcp_pt_initial_interval'] = $wancfg['adv_dhcp_pt_initial_interval']; + +$pconfig['adv_dhcp_pt_values'] = $wancfg['adv_dhcp_pt_values']; + +$pconfig['adv_dhcp_send_options'] = $wancfg['adv_dhcp_send_options']; +$pconfig['adv_dhcp_request_options'] = $wancfg['adv_dhcp_request_options']; +$pconfig['adv_dhcp_required_options'] = $wancfg['adv_dhcp_required_options']; +$pconfig['adv_dhcp_option_modifiers'] = $wancfg['adv_dhcp_option_modifiers']; + +$pconfig['adv_dhcp_config_advanced'] = $wancfg['adv_dhcp_config_advanced']; +$pconfig['adv_dhcp_config_file_override'] = $wancfg['adv_dhcp_config_file_override']; +$pconfig['adv_dhcp_config_file_override_path'] = $wancfg['adv_dhcp_config_file_override_path']; + +$pconfig['adv_dhcp6_interface_statement_send_options'] = $wancfg['adv_dhcp6_interface_statement_send_options']; +$pconfig['adv_dhcp6_interface_statement_request_options'] = $wancfg['adv_dhcp6_interface_statement_request_options']; +$pconfig['adv_dhcp6_interface_statement_information_only_enable'] = $wancfg['adv_dhcp6_interface_statement_information_only_enable']; +$pconfig['adv_dhcp6_interface_statement_script'] = $wancfg['adv_dhcp6_interface_statement_script']; + +$pconfig['adv_dhcp6_id_assoc_statement_address_enable'] = $wancfg['adv_dhcp6_id_assoc_statement_address_enable']; +$pconfig['adv_dhcp6_id_assoc_statement_address'] = $wancfg['adv_dhcp6_id_assoc_statement_address']; +$pconfig['adv_dhcp6_id_assoc_statement_address_id'] = $wancfg['adv_dhcp6_id_assoc_statement_address_id']; +$pconfig['adv_dhcp6_id_assoc_statement_address_pltime'] = $wancfg['adv_dhcp6_id_assoc_statement_address_pltime']; +$pconfig['adv_dhcp6_id_assoc_statement_address_vltime'] = $wancfg['adv_dhcp6_id_assoc_statement_address_vltime']; + +$pconfig['adv_dhcp6_id_assoc_statement_prefix_enable'] = $wancfg['adv_dhcp6_id_assoc_statement_prefix_enable']; +$pconfig['adv_dhcp6_id_assoc_statement_prefix'] = $wancfg['adv_dhcp6_id_assoc_statement_prefix']; +$pconfig['adv_dhcp6_id_assoc_statement_prefix_id'] = $wancfg['adv_dhcp6_id_assoc_statement_prefix_id']; +$pconfig['adv_dhcp6_id_assoc_statement_prefix_pltime'] = $wancfg['adv_dhcp6_id_assoc_statement_prefix_pltime']; +$pconfig['adv_dhcp6_id_assoc_statement_prefix_vltime'] = $wancfg['adv_dhcp6_id_assoc_statement_prefix_vltime']; + +$pconfig['adv_dhcp6_prefix_interface_statement_sla_id'] = $wancfg['adv_dhcp6_prefix_interface_statement_sla_id']; +$pconfig['adv_dhcp6_prefix_interface_statement_sla_len'] = $wancfg['adv_dhcp6_prefix_interface_statement_sla_len']; + +$pconfig['adv_dhcp6_authentication_statement_authname'] = $wancfg['adv_dhcp6_authentication_statement_authname']; +$pconfig['adv_dhcp6_authentication_statement_protocol'] = $wancfg['adv_dhcp6_authentication_statement_protocol']; +$pconfig['adv_dhcp6_authentication_statement_algorithm'] = $wancfg['adv_dhcp6_authentication_statement_algorithm']; +$pconfig['adv_dhcp6_authentication_statement_rdm'] = $wancfg['adv_dhcp6_authentication_statement_rdm']; + +$pconfig['adv_dhcp6_key_info_statement_keyname'] = $wancfg['adv_dhcp6_key_info_statement_keyname']; +$pconfig['adv_dhcp6_key_info_statement_realm'] = $wancfg['adv_dhcp6_key_info_statement_realm']; +$pconfig['adv_dhcp6_key_info_statement_keyid'] = $wancfg['adv_dhcp6_key_info_statement_keyid']; +$pconfig['adv_dhcp6_key_info_statement_secret'] = $wancfg['adv_dhcp6_key_info_statement_secret']; +$pconfig['adv_dhcp6_key_info_statement_expire'] = $wancfg['adv_dhcp6_key_info_statement_expire']; + +$pconfig['adv_dhcp6_config_advanced'] = $wancfg['adv_dhcp6_config_advanced']; +$pconfig['adv_dhcp6_config_file_override'] = $wancfg['adv_dhcp6_config_file_override']; +$pconfig['adv_dhcp6_config_file_override_path'] = $wancfg['adv_dhcp6_config_file_override_path']; + +$pconfig['dhcp_plus'] = isset($wancfg['dhcp_plus']); +$pconfig['descr'] = remove_bad_chars($wancfg['descr']); +$pconfig['enable'] = isset($wancfg['enable']); + +if (is_array($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $alias) { + if ($alias['name'] == $wancfg['descr']) { + $input_errors[] = sprintf(gettext("Sorry, an alias with the name %s already exists."), $wancfg['descr']); + } + } +} + +switch ($wancfg['ipaddr']) { + case "dhcp": + $pconfig['type'] = "dhcp"; + break; + case "pppoe": + case "pptp": + case "l2tp": + case "ppp": + $pconfig['type'] = $wancfg['ipaddr']; + break; + default: + if (is_ipaddrv4($wancfg['ipaddr'])) { + $pconfig['type'] = "staticv4"; + $pconfig['ipaddr'] = $wancfg['ipaddr']; + $pconfig['subnet'] = $wancfg['subnet']; + $pconfig['gateway'] = $wancfg['gateway']; + } else { + $pconfig['type'] = "none"; + } + break; +} + +switch ($wancfg['ipaddrv6']) { + case "slaac": + $pconfig['type6'] = "slaac"; + break; + case "dhcp6": + $pconfig['dhcp6-duid'] = $wancfg['dhcp6-duid']; + if (!isset($wancfg['dhcp6-ia-pd-len'])) { + $wancfg['dhcp6-ia-pd-len'] = "none"; + } + $pconfig['dhcp6-ia-pd-len'] = $wancfg['dhcp6-ia-pd-len']; + $pconfig['dhcp6-ia-pd-send-hint'] = isset($wancfg['dhcp6-ia-pd-send-hint']); + $pconfig['type6'] = "dhcp6"; + $pconfig['dhcp6prefixonly'] = isset($wancfg['dhcp6prefixonly']); + $pconfig['dhcp6usev4iface'] = isset($wancfg['dhcp6usev4iface']); + break; + case "6to4": + $pconfig['type6'] = "6to4"; + break; + case "track6": + $pconfig['type6'] = "track6"; + $pconfig['track6-interface'] = $wancfg['track6-interface']; + if ($wancfg['track6-prefix-id'] == "") { + $pconfig['track6-prefix-id'] = 0; + } else { + $pconfig['track6-prefix-id'] = $wancfg['track6-prefix-id']; + } + $pconfig['track6-prefix-id--hex'] = sprintf("%x", $pconfig['track6-prefix-id']); + break; + case "6rd": + $pconfig['prefix-6rd'] = $wancfg['prefix-6rd']; + if ($wancfg['prefix-6rd-v4plen'] == "") { + $wancfg['prefix-6rd-v4plen'] = "0"; + } + $pconfig['prefix-6rd-v4plen'] = $wancfg['prefix-6rd-v4plen']; + $pconfig['type6'] = "6rd"; + $pconfig['gateway-6rd'] = $wancfg['gateway-6rd']; + break; + default: + if (is_ipaddrv6($wancfg['ipaddrv6'])) { + $pconfig['type6'] = "staticv6"; + $pconfig['ipaddrv6'] = $wancfg['ipaddrv6']; + $pconfig['subnetv6'] = $wancfg['subnetv6']; + $pconfig['gatewayv6'] = $wancfg['gatewayv6']; + } else { + $pconfig['type6'] = "none"; + } + break; +} + +// print_r($pconfig); + +$pconfig['blockpriv'] = isset($wancfg['blockpriv']); +$pconfig['blockbogons'] = isset($wancfg['blockbogons']); +$pconfig['spoofmac'] = $wancfg['spoofmac']; +$pconfig['mtu'] = $wancfg['mtu']; +$pconfig['mss'] = $wancfg['mss']; + +/* Wireless interface? */ +if (isset($wancfg['wireless'])) { + /* Sync first to be sure it displays the actual settings that will be used */ + interface_sync_wireless_clones($wancfg, false); + /* Get wireless modes */ + $wlanif = get_real_interface($if); + if (!does_interface_exist($wlanif)) { + interface_wireless_clone($wlanif, $wancfg); + } + $wlanbaseif = interface_get_wireless_base($wancfg['if']); + preg_match("/^(.*?)([0-9]*)$/", $wlanbaseif, $wlanbaseif_split); + $wl_modes = get_wireless_modes($if); + $wl_chaninfo = get_wireless_channel_info($if); + $wl_sysctl_prefix = 'dev.' . $wlanbaseif_split[1] . '.' . $wlanbaseif_split[2]; + $wl_sysctl = get_sysctl( + array( + "{$wl_sysctl_prefix}.diversity", + "{$wl_sysctl_prefix}.txantenna", + "{$wl_sysctl_prefix}.rxantenna", + "{$wl_sysctl_prefix}.slottime", + "{$wl_sysctl_prefix}.acktimeout", + "{$wl_sysctl_prefix}.ctstimeout")); + $wl_regdomain_xml_attr = array(); + $wl_regdomain_xml = parse_xml_regdomain($wl_regdomain_xml_attr); + $wl_regdomains = &$wl_regdomain_xml['regulatory-domains']['rd']; + $wl_regdomains_attr = &$wl_regdomain_xml_attr['regulatory-domains']['rd']; + $wl_countries = &$wl_regdomain_xml['country-codes']['country']; + $wl_countries_attr = &$wl_regdomain_xml_attr['country-codes']['country']; + $pconfig['persistcommonwireless'] = isset($config['wireless']['interfaces'][$wlanbaseif]); + $pconfig['standard'] = $wancfg['wireless']['standard']; + $pconfig['mode'] = $wancfg['wireless']['mode']; + $pconfig['protmode'] = $wancfg['wireless']['protmode']; + $pconfig['ssid'] = $wancfg['wireless']['ssid']; + $pconfig['channel'] = $wancfg['wireless']['channel']; + $pconfig['txpower'] = $wancfg['wireless']['txpower']; + $pconfig['diversity'] = $wancfg['wireless']['diversity']; + $pconfig['txantenna'] = $wancfg['wireless']['txantenna']; + $pconfig['rxantenna'] = $wancfg['wireless']['rxantenna']; + $pconfig['distance'] = $wancfg['wireless']['distance']; + $pconfig['regdomain'] = $wancfg['wireless']['regdomain']; + $pconfig['regcountry'] = $wancfg['wireless']['regcountry']; + $pconfig['reglocation'] = $wancfg['wireless']['reglocation']; + $pconfig['wme_enable'] = isset($wancfg['wireless']['wme']['enable']); + if (isset($wancfg['wireless']['puren']['enable'])) { + $pconfig['puremode'] = '11n'; + } else if (isset($wancfg['wireless']['pureg']['enable'])) { + $pconfig['puremode'] = '11g'; + } else { + $pconfig['puremode'] = 'any'; + } + $pconfig['apbridge_enable'] = isset($wancfg['wireless']['apbridge']['enable']); + $pconfig['authmode'] = $wancfg['wireless']['authmode']; + $pconfig['hidessid_enable'] = isset($wancfg['wireless']['hidessid']['enable']); + $pconfig['auth_server_addr'] = $wancfg['wireless']['auth_server_addr']; + $pconfig['auth_server_port'] = $wancfg['wireless']['auth_server_port']; + $pconfig['auth_server_shared_secret'] = $wancfg['wireless']['auth_server_shared_secret']; + $pconfig['auth_server_addr2'] = $wancfg['wireless']['auth_server_addr2']; + $pconfig['auth_server_port2'] = $wancfg['wireless']['auth_server_port2']; + $pconfig['auth_server_shared_secret2'] = $wancfg['wireless']['auth_server_shared_secret2']; + if (is_array($wancfg['wireless']['wpa'])) { + $pconfig['debug_mode'] = $wancfg['wireless']['wpa']['debug_mode']; + $pconfig['macaddr_acl'] = $wancfg['wireless']['wpa']['macaddr_acl']; + $pconfig['mac_acl_enable'] = isset($wancfg['wireless']['wpa']['mac_acl_enable']); + $pconfig['auth_algs'] = $wancfg['wireless']['wpa']['auth_algs']; + $pconfig['wpa_mode'] = $wancfg['wireless']['wpa']['wpa_mode']; + $pconfig['wpa_key_mgmt'] = $wancfg['wireless']['wpa']['wpa_key_mgmt']; + $pconfig['wpa_pairwise'] = $wancfg['wireless']['wpa']['wpa_pairwise']; + $pconfig['wpa_group_rekey'] = $wancfg['wireless']['wpa']['wpa_group_rekey']; + $pconfig['wpa_gmk_rekey'] = $wancfg['wireless']['wpa']['wpa_gmk_rekey']; + $pconfig['wpa_strict_rekey'] = isset($wancfg['wireless']['wpa']['wpa_strict_rekey']); + $pconfig['passphrase'] = $wancfg['wireless']['wpa']['passphrase']; + $pconfig['ieee8021x'] = isset($wancfg['wireless']['wpa']['ieee8021x']['enable']); + $pconfig['rsn_preauth'] = isset($wancfg['wireless']['wpa']['rsn_preauth']); + $pconfig['ext_wpa_sw'] = $wancfg['wireless']['wpa']['ext_wpa_sw']; + $pconfig['wpa_enable'] = isset($wancfg['wireless']['wpa']['enable']); + } + $pconfig['wep_enable'] = isset($wancfg['wireless']['wep']['enable']); + $pconfig['mac_acl'] = $wancfg['wireless']['mac_acl']; + if (is_array($wancfg['wireless']['wep']) && is_array($wancfg['wireless']['wep']['key'])) { + $i = 1; + foreach ($wancfg['wireless']['wep']['key'] as $wepkey) { + $pconfig['key' . $i] = $wepkey['value']; + if (isset($wepkey['txkey'])) { + $pconfig['txkey'] = $i; + } + $i++; + } + if (!isset($wepkey['txkey'])) { + $pconfig['txkey'] = 1; + } + } +} + +if ($_POST['apply']) { + unset($input_errors); + if (!is_subsystem_dirty('interfaces')) { + $input_errors[] = gettext("You have already applied your settings!"); + } else { + unlink_if_exists("{$g['tmp_path']}/config.cache"); + clear_subsystem_dirty('interfaces'); + + if (file_exists("{$g['tmp_path']}/.interfaces.apply")) { + $toapplylist = unserialize(file_get_contents("{$g['tmp_path']}/.interfaces.apply")); + foreach ($toapplylist as $ifapply => $ifcfgo) { + if (isset($config['interfaces'][$ifapply]['enable'])) { + interface_bring_down($ifapply, false, $ifcfgo); + interface_configure($ifapply, true); + } else { + interface_bring_down($ifapply, true, $ifcfgo); + if (isset($config['dhcpd'][$ifapply]['enable']) || + isset($config['dhcpdv6'][$ifapply]['enable'])) { + services_dhcpd_configure(); + } + } + } + } + /* restart snmp so that it binds to correct address */ + services_snmpd_configure(); + + /* sync filter configuration */ + setup_gateways_monitor(); + + clear_subsystem_dirty('interfaces'); + + filter_configure(); + + enable_rrd_graphing(); + + if (is_subsystem_dirty('staticroutes') && (system_routing_configure() == 0)) { + clear_subsystem_dirty('staticroutes'); + } + } + @unlink("{$g['tmp_path']}/.interfaces.apply"); + header("Location: interfaces.php?if={$if}"); + exit; +} else if ($_POST && $_POST['enable'] != "yes") { + unset($wancfg['enable']); + if (isset($wancfg['wireless'])) { + interface_sync_wireless_clones($wancfg, false); + } + write_config("Interface {$_POST['descr']}({$if}) is now disabled."); + mark_subsystem_dirty('interfaces'); + if (file_exists("{$g['tmp_path']}/.interfaces.apply")) { + $toapplylist = unserialize(file_get_contents("{$g['tmp_path']}/.interfaces.apply")); + } else { + $toapplylist = array(); + } + $toapplylist[$if]['ifcfg'] = $wancfg; + $toapplylist[$if]['ppps'] = $a_ppps; + /* we need to be able remove IP aliases for IPv6 */ + file_put_contents("{$g['tmp_path']}/.interfaces.apply", serialize($toapplylist)); + header("Location: interfaces.php?if={$if}"); + exit; +} else if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + if (is_numeric("0x" . $_POST['track6-prefix-id--hex'])) { + $pconfig['track6-prefix-id'] = intval($_POST['track6-prefix-id--hex'], 16); + } else { + $pconfig['track6-prefix-id'] = 0; + } + conf_mount_rw(); + + /* filter out spaces from descriptions */ + $_POST['descr'] = remove_bad_chars($_POST['descr']); + + /* okay first of all, cause we are just hiding the PPPoE HTML + * fields related to PPPoE resets, we are going to unset $_POST + * vars, if the reset feature should not be used. Otherwise the + * data validation procedure below, may trigger a false error + * message. + */ + if (empty($_POST['pppoe-reset-type'])) { + unset($_POST['pppoe_pr_type']); + unset($_POST['pppoe_resethour']); + unset($_POST['pppoe_resetminute']); + unset($_POST['pppoe_resetdate']); + unset($_POST['pppoe_pr_preset_val']); + } + /* description unique? */ + foreach ($ifdescrs as $ifent => $ifdescr) { + if ($if != $ifent && $ifdescr == $_POST['descr']) { + $input_errors[] = gettext("An interface with the specified description already exists."); + break; + } + } + if (is_numeric($_POST['descr'])) { + $input_errors[] = gettext("The interface description cannot contain only numbers."); + } + /* input validation */ + if (isset($config['dhcpd']) && isset($config['dhcpd'][$if]['enable']) && (!preg_match("/^staticv4/", $_POST['type']))) { + $input_errors[] = gettext("The DHCP Server is active on this interface and it can be used only with a static IP configuration. Please disable the DHCP Server service on this interface first, then change the interface configuration."); + } + if (isset($config['dhcpdv6']) && isset($config['dhcpdv6'][$if]['enable']) && (!preg_match("/^staticv6/", $_POST['type6']))) { + $input_errors[] = gettext("The DHCP6 Server is active on this interface and it can be used only with a static IPv6 configuration. Please disable the DHCPv6 Server service on this interface first, then change the interface configuration."); + } + + switch (strtolower($_POST['type'])) { + case "staticv4": + $reqdfields = explode(" ", "ipaddr subnet gateway"); + $reqdfieldsn = array(gettext("IPv4 address"), gettext("Subnet bit count"), gettext("Gateway")); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + break; + case "none": + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $vip) { + if (is_ipaddrv4($vip['subnet']) && $vip['interface'] == $if) { + $input_errors[] = gettext("This interface is referenced by IPv4 VIPs. Please delete those before setting the interface to 'none' configuration."); + } + } + } + break; + case "ppp": + $reqdfields = explode(" ", "port phone"); + $reqdfieldsn = array(gettext("Modem Port"), gettext("Phone Number")); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + break; + case "pppoe": + if ($_POST['pppoe_dialondemand']) { + $reqdfields = explode(" ", "pppoe_username pppoe_password pppoe_dialondemand pppoe_idletimeout"); + $reqdfieldsn = array(gettext("PPPoE username"), gettext("PPPoE password"), gettext("Dial on demand"), gettext("Idle timeout value")); + } else { + $reqdfields = explode(" ", "pppoe_username pppoe_password"); + $reqdfieldsn = array(gettext("PPPoE username"), gettext("PPPoE password")); + } + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + break; + case "pptp": + if ($_POST['pptp_dialondemand']) { + $reqdfields = explode(" ", "pptp_username pptp_password pptp_local pptp_subnet pptp_remote pptp_dialondemand pptp_idletimeout"); + $reqdfieldsn = array(gettext("PPTP username"), gettext("PPTP password"), gettext("PPTP local IP address"), gettext("PPTP subnet"), gettext("PPTP remote IP address"), gettext("Dial on demand"), gettext("Idle timeout value")); + } else { + $reqdfields = explode(" ", "pptp_username pptp_password pptp_local pptp_subnet pptp_remote"); + $reqdfieldsn = array(gettext("PPTP username"), gettext("PPTP password"), gettext("PPTP local IP address"), gettext("PPTP subnet"), gettext("PPTP remote IP address")); + } + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + break; + case "l2tp": + if ($_POST['pptp_dialondemand']) { + $reqdfields = explode(" ", "pptp_username pptp_password pptp_remote pptp_dialondemand pptp_idletimeout"); + $reqdfieldsn = array(gettext("L2TP username"), gettext("L2TP password"), gettext("L2TP remote IP address"), gettext("Dial on demand"), gettext("Idle timeout value")); + } else { + $reqdfields = explode(" ", "pptp_username pptp_password pptp_remote"); + $reqdfieldsn = array(gettext("L2TP username"), gettext("L2TP password"), gettext("L2TP remote IP address")); + } + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + break; + } + switch (strtolower($_POST['type6'])) { + case "staticv6": + $reqdfields = explode(" ", "ipaddrv6 subnetv6 gatewayv6"); + $reqdfieldsn = array(gettext("IPv6 address"), gettext("Subnet bit count"), gettext("Gateway")); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + break; + case "none": + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $vip) { + if (is_ipaddrv6($vip['subnet']) && $vip['interface'] == $if) { + $input_errors[] = gettext("This interface is referenced by IPv6 VIPs. Please delete those before setting the interface to 'none' configuration."); + } + } + } + break; + case "dhcp6": + if (in_array($wancfg['ipaddrv6'], array())) { + $input_errors[] = sprintf(gettext("You have to reassign the interface to be able to configure as %s."), $_POST['type6']); + } + if ($_POST['dhcp6-ia-pd-send-hint'] && strtolower($_POST['dhcp6-ia-pd-len']) == 'none') { + $input_errors[] = gettext('DHCPv6 Prefix Delegation size must be provided when Send IPv6 prefix hint flag is checked'); + } + break; + case "6rd": + foreach ($ifdescrs as $ifent => $ifdescr) { + if ($if != $ifent && ($config[interfaces][$ifent]['ipaddrv6'] == $_POST['type6'])) { + if ($config[interfaces][$ifent]['prefix-6rd'] == $_POST['prefix-6rd']) { + $input_errors[] = gettext("You can only have one interface configured in 6rd with same prefix."); + break; + } + } + } + if (in_array($wancfg['ipaddrv6'], array())) { + $input_errors[] = sprintf(gettext("You have to reassign the interface to be able to configure as %s."), $_POST['type6']); + } + break; + case "6to4": + foreach ($ifdescrs as $ifent => $ifdescr) { + if ($if != $ifent && ($config[interfaces][$ifent]['ipaddrv6'] == $_POST['type6'])) { + $input_errors[] = sprintf(gettext("You can only have one interface configured as 6to4."), $_POST['type6']); + break; + } + } + if (in_array($wancfg['ipaddrv6'], array())) { + $input_errors[] = sprintf(gettext("You have to reassign the interface to be able to configure as %s."), $_POST['type6']); + } + break; + case "track6": + /* needs to check if $track6-prefix-id is used on another interface */ + if (in_array($wancfg['ipaddrv6'], array())) { + $input_errors[] = sprintf(gettext("You have to reassign the interface to be able to configure as %s."), $_POST['type6']); + } + + if ($_POST['track6-prefix-id--hex'] != "" && !is_numeric("0x" . $_POST['track6-prefix-id--hex'])) { + $input_errors[] = gettext("You must enter a valid hexadecimal number for the IPv6 prefix ID."); + } else { + $track6_prefix_id = intval($_POST['track6-prefix-id--hex'], 16); + if ($track6_prefix_id < 0 || $track6_prefix_id > $_POST['ipv6-num-prefix-ids-' . $_POST['track6-interface']]) { + $input_errors[] = gettext("You specified an IPv6 prefix ID that is out of range.") . + " ({$_POST['track6-interface']}) - (0) - (" . sprintf('%x', $_POST['ipv6-num-prefix-ids-' . $_POST['track6-interface']]) . ")"; + } else { + foreach ($ifdescrs as $ifent => $ifdescr) { + if ($if == $ifent) { + continue; + } + if ($config['interfaces'][$ifent]['ipaddrv6'] == 'track6' && + $config['interfaces'][$ifent]['track6-interface'] == $_POST['track6-interface'] && + $config['interfaces'][$ifent]['track6-prefix-id'] == $track6_prefix_id) { + $input_errors[] = sprintf(gettext("This track6 prefix ID is already being used in %s."), $ifdescr); + } + } + } + } + break; + } + + /* normalize MAC addresses - lowercase and convert Windows-ized hyphenated MACs to colon delimited */ + $staticroutes = get_staticroutes(true); + $_POST['spoofmac'] = strtolower(str_replace("-", ":", $_POST['spoofmac'])); + if ($_POST['ipaddr']) { + if (!is_ipaddrv4($_POST['ipaddr'])) { + $input_errors[] = gettext("A valid IPv4 address must be specified."); + } else { + $where_ipaddr_configured = where_is_ipaddr_configured($_POST['ipaddr'], $if, true, true, $_POST['subnet']); + if (count($where_ipaddr_configured)) { + $subnet_conflict_text = sprintf(gettext("IPv4 address %s is being used by or overlaps with:"), $_POST['ipaddr'] . "/" . $_POST['subnet']); + foreach ($where_ipaddr_configured as $subnet_conflict) { + $subnet_conflict_text .= " " . convert_friendly_interface_to_friendly_descr($subnet_conflict['if']) . " (" . $subnet_conflict['ip_or_subnet'] . ")"; + } + $input_errors[] = $subnet_conflict_text; + } + + /* Do not accept network or broadcast address, except if subnet is 31 or 32 */ + if ($_POST['subnet'] < 31) { + if ($_POST['ipaddr'] == gen_subnet($_POST['ipaddr'], $_POST['subnet'])) { + $input_errors[] = gettext("This IPv4 address is the network address and cannot be used"); + } else if ($_POST['ipaddr'] == gen_subnet_max($_POST['ipaddr'], $_POST['subnet'])) { + $input_errors[] = gettext("This IPv4 address is the broadcast address and cannot be used"); + } + } + + foreach ($staticroutes as $route_subnet) { + list($network, $subnet) = explode("/", $route_subnet); + if ($_POST['subnet'] == $subnet && $network == gen_subnet($_POST['ipaddr'], $_POST['subnet'])) { + $input_errors[] = gettext("This IPv4 address conflicts with a Static Route."); + break; + } + unset($network, $subnet); + } + } + } + if ($_POST['ipaddrv6']) { + if (!is_ipaddrv6($_POST['ipaddrv6'])) { + $input_errors[] = gettext("A valid IPv6 address must be specified."); + } else { + $where_ipaddr_configured = where_is_ipaddr_configured($_POST['ipaddrv6'], $if, true, true, $_POST['subnetv6']); + if (count($where_ipaddr_configured)) { + $subnet_conflict_text = sprintf(gettext("IPv6 address %s is being used by or overlaps with:"), $_POST['ipaddrv6'] . "/" . $_POST['subnetv6']); + foreach ($where_ipaddr_configured as $subnet_conflict) { + $subnet_conflict_text .= " " . convert_friendly_interface_to_friendly_descr($subnet_conflict['if']) . " (" . $subnet_conflict['ip_or_subnet'] . ")"; + } + $input_errors[] = $subnet_conflict_text; + } + + foreach ($staticroutes as $route_subnet) { + list($network, $subnet) = explode("/", $route_subnet); + if ($_POST['subnetv6'] == $subnet && $network == gen_subnetv6($_POST['ipaddrv6'], $_POST['subnetv6'])) { + $input_errors[] = gettext("This IPv6 address conflicts with a Static Route."); + break; + } + unset($network, $subnet); + } + } + } + if (($_POST['subnet'] && !is_numeric($_POST['subnet']))) { + $input_errors[] = gettext("A valid subnet bit count must be specified."); + } + if (($_POST['subnetv6'] && !is_numeric($_POST['subnetv6']))) { + $input_errors[] = gettext("A valid subnet bit count must be specified."); + } + if (($_POST['alias-address'] && !is_ipaddrv4($_POST['alias-address']))) { + $input_errors[] = gettext("A valid alias IP address must be specified."); + } + if (($_POST['alias-subnet'] && !is_numeric($_POST['alias-subnet']))) { + $input_errors[] = gettext("A valid alias subnet bit count must be specified."); + } + if ($_POST['dhcprejectfrom'] && !is_ipaddrv4($_POST['dhcprejectfrom'])) { + $input_errors[] = gettext("A valid alias IP address must be specified to reject DHCP Leases from."); + } + if (($_POST['gateway'] != "none") || ($_POST['gatewayv6'] != "none")) { + $match = false; + foreach ($a_gateways as $gateway) { + if (in_array($_POST['gateway'], $gateway)) { + $match = true; + } + } + foreach ($a_gateways as $gateway) { + if (in_array($_POST['gatewayv6'], $gateway)) { + $match = true; + } + } + if (!$match) { + $input_errors[] = gettext("A valid gateway must be specified."); + } + } + if (($_POST['provider'] && !is_domain($_POST['provider']))) { + $input_errors[] = gettext("The service name contains invalid characters."); + } + if (($_POST['pppoe_idletimeout'] != "") && !is_numericint($_POST['pppoe_idletimeout'])) { + $input_errors[] = gettext("The idle timeout value must be an integer."); + } + if ($_POST['pppoe_resethour'] != "" && !is_numericint($_POST['pppoe_resethour']) && + $_POST['pppoe_resethour'] >= 0 && $_POST['pppoe_resethour'] <=23) { + $input_errors[] = gettext("A valid PPPoE reset hour must be specified (0-23)."); + } + if ($_POST['pppoe_resetminute'] != "" && !is_numericint($_POST['pppoe_resetminute']) && + $_POST['pppoe_resetminute'] >= 0 && $_POST['pppoe_resetminute'] <=59) { + $input_errors[] = gettext("A valid PPPoE reset minute must be specified (0-59)."); + } + if ($_POST['pppoe_resetdate'] != "" && !is_numeric(str_replace("/", "", $_POST['pppoe_resetdate']))) { + $input_errors[] = gettext("A valid PPPoE reset date must be specified (mm/dd/yyyy)."); + } + if (($_POST['pptp_local'] && !is_ipaddrv4($_POST['pptp_local']))) { + $input_errors[] = gettext("A valid PPTP local IP address must be specified."); + } + if (($_POST['pptp_subnet'] && !is_numeric($_POST['pptp_subnet']))) { + $input_errors[] = gettext("A valid PPTP subnet bit count must be specified."); + } + if (($_POST['pptp_remote'] && !is_ipaddrv4($_POST['pptp_remote']) && !is_hostname($_POST['gateway'][$iface]))) { + $input_errors[] = gettext("A valid PPTP remote IP address must be specified."); + } + if (($_POST['pptp_idletimeout'] != "") && !is_numericint($_POST['pptp_idletimeout'])) { + $input_errors[] = gettext("The idle timeout value must be an integer."); + } + if (($_POST['spoofmac'] && !is_macaddr($_POST['spoofmac']))) { + $input_errors[] = gettext("A valid MAC address must be specified."); + } + if ($_POST['mtu']) { + if (!is_numericint($_POST['mtu'])) { + $input_errors[] = "MTU must be an integer."; + } + if (substr($wancfg['if'], 0, 3) == 'gif') { + $min_mtu = 1280; + $max_mtu = 8192; + } else { + $min_mtu = 576; + $max_mtu = 9000; + } + + if ($_POST['mtu'] < $min_mtu || $_POST['mtu'] > $max_mtu) { + $input_errors[] = sprintf(gettext("The MTU must be between %d and %d bytes."), $min_mtu, $max_mtu); + } + + unset($min_mtu, $max_mtu); + + if (stristr($wancfg['if'], "_vlan")) { + $realhwif_array = get_parent_interface($wancfg['if']); + // Need code to handle MLPPP if we ever use $realhwif for MLPPP handling + $parent_realhwif = $realhwif_array[0]; + $parent_if = convert_real_interface_to_friendly_interface_name($parent_realhwif); + if (!empty($parent_if) && !empty($config['interfaces'][$parent_if]['mtu'])) { + if ($_POST['mtu'] > intval($config['interfaces'][$parent_if]['mtu'])) { + $input_errors[] = gettext("The MTU of a VLAN cannot be greater than that of its parent interface."); + } + } + } else { + foreach ($config['interfaces'] as $idx => $ifdata) { + if (($idx == $if) || !preg_match('/_vlan[0-9]/', $ifdata['if'])) { + continue; + } + + $realhwif_array = get_parent_interface($ifdata['if']); + // Need code to handle MLPPP if we ever use $realhwif for MLPPP handling + $parent_realhwif = $realhwif_array[0]; + + if ($parent_realhwif != $wancfg['if']) { + continue; + } + + if (isset($ifdata['mtu']) && $ifdata['mtu'] > $_POST['mtu']) { + $input_errors[] = sprintf(gettext("Interface %s (VLAN) has MTU set to a larger value"), $ifdata['descr']); + } + } + } + } + if ($_POST['mss'] != '') { + if (!is_numericint($_POST['mss']) || ($_POST['mss'] < 576 || $_POST['mss'] > 65535)) { + $input_errors[] = gettext("The MSS must be an integer between 576 and 65535 bytes."); + } + } + /* Wireless interface? */ + if (isset($wancfg['wireless'])) { + $reqdfields = array("mode"); + $reqdfieldsn = array(gettext("Mode")); + if ($_POST['mode'] == 'hostap') { + $reqdfields[] = "ssid"; + $reqdfieldsn[] = gettext("SSID"); + if (isset($_POST['channel']) && $_POST['channel'] == "0") { + // auto channel with hostap is broken, prevent this for now. + $input_errors[] = gettext("A specific channel, not auto, must be selected for Access Point mode."); + } + } + if (stristr($_POST['standard'], '11n')) { + if (!($_POST['wme_enable'])) { + $input_errors[] = gettext("802.11n standards require enabling WME."); + } + } + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + check_wireless_mode(); + if (isset($_POST['wpa_group_rekey']) && (!is_numericint($_POST['wpa_group_rekey']) || $_POST['wpa_group_rekey'] < 1 || $_POST['wpa_group_rekey'] > 9999)) { + $input_errors[] = gettext("Key Rotation must be an integer between 1 and 9999."); + } + if (isset($_POST['wpa_gmk_rekey']) && (!is_numericint($_POST['wpa_gmk_rekey']) || $_POST['wpa_gmk_rekey'] < 1 || $_POST['wpa_gmk_rekey'] > 9999)) { + $input_errors[] = gettext("Master Key Regeneration must be an integer between 1 and 9999."); + } + if (isset($_POST['wpa_group_rekey']) && isset($_POST['wpa_gmk_rekey'])) { + if ($_POST['wpa_group_rekey'] > $_POST['wpa_gmk_rekey']) { + $input_errors[] = gettext("Master Key Regeneration must be greater than Key Rotation."); + } + } + if (!empty($_POST['auth_server_addr'])) { + if (!is_domain($_POST['auth_server_addr']) && !is_ipaddr($_POST['auth_server_addr'])) { + $input_errors[] = gettext("802.1X Authentication Server must be an IP or hostname."); + } + } + if (!empty($_POST['auth_server_addr2'])) { + if (!is_domain($_POST['auth_server_addr2']) && !is_ipaddr($_POST['auth_server_addr2'])) { + $input_errors[] = gettext("Secondary 802.1X Authentication Server must be an IP or hostname."); + } + } + if (!empty($_POST['auth_server_port'])) { + if (!is_port($_POST['auth_server_port'])) { + $input_errors[] = gettext("802.1X Authentication Server Port must be a valid port number (1-65535)."); + } + } + if (!empty($_POST['auth_server_port2'])) { + if (!is_port($_POST['auth_server_port2'])) { + $input_errors[] = gettext("Secondary 802.1X Authentication Server Port must be a valid port number (1-65535)."); + } + } + if (isset($_POST['channel']) && !is_numericint($_POST['channel'])) { + if (!is_numericint($_POST['channel'])) { + $input_errors[] = gettext("Invalid channel specified."); + } else { + if ($_POST['channel'] > 255 || $_POST['channel'] < 0) { + $input_errors[] = gettext("Channel must be between 0-255."); + } + } + } + if (!empty($_POST['distance']) && !is_numericint($_POST['distance'])) { + $input_errors[] = gettext("Distance must be an integer."); + } + if (isset($_POST['standard']) && (stristr($_POST['standard'], '11na') || stristr($_POST['standard'], '11a'))) { + if ($_POST['channel'] != 0 && $_POST['channel'] < 15) { + $input_errors[] = gettext("Channel selected is not valid for 802.11a or 802.11na."); + } + } + if (isset($_POST['standard']) && ($_POST['standard'] == "11b" || $_POST['standard'] == "11g")) { + if ($_POST['channel'] > 14) { + $input_errors[] = gettext("Channel selected is not valid for 802.11b or 802.11g."); + } + } + if (!empty($_POST['protmode']) && !in_array($_POST['protmode'], array("off", "cts", "rtscts"))) { + $input_errors[] = gettext("Invalid option chosen for OFDM Protection Mode"); + } + /* loop through keys and enforce size */ + for ($i = 1; $i <= 4; $i++) { + if ($_POST['key' . $i]) { + /* 64 bit */ + if (strlen($_POST['key' . $i]) == 5) { + continue; + } + if (strlen($_POST['key' . $i]) == 10) { + /* hex key */ + if (stristr($_POST['key' . $i], "0x") == false) { + $_POST['key' . $i] = "0x" . $_POST['key' . $i]; + } + continue; + } + if (strlen($_POST['key' . $i]) == 12) { + /* hex key */ + if (stristr($_POST['key' . $i], "0x") == false) { + $_POST['key' . $i] = "0x" . $_POST['key' . $i]; + } + continue; + } + /* 128 bit */ + if (strlen($_POST['key' . $i]) == 13) { + continue; + } + if (strlen($_POST['key' . $i]) == 26) { + /* hex key */ + if (stristr($_POST['key' . $i], "0x") == false) { + $_POST['key' . $i] = "0x" . $_POST['key' . $i]; + } + continue; + } + if (strlen($_POST['key' . $i]) == 28) { + continue; + } + $input_errors[] = gettext("Invalid WEP key. Enter a valid 40, 64, 104 or 128 bit WEP key."); + break; + } + } + + if ($_POST['passphrase']) { + $passlen = strlen($_POST['passphrase']); + if ($passlen < 8 || $passlen > 63) { + $input_errors[] = gettext("The WPA passphrase must be between 8 and 63 characters long."); + } + } + if ($_POST['wpa_enable'] == "yes") { + if (empty($_POST['passphrase']) && stristr($_POST['wpa_key_mgmt'], "WPA-PSK")) { + $input_errors[] = gettext("A WPA Passphrase must be specified when WPA PSK is enabled."); + } + } + } + if (!$input_errors) { + if ($wancfg['ipaddr'] != $_POST['type']) { + if (in_array($wancfg['ipaddr'], array("ppp", "pppoe", "pptp", "l2tp"))) { + $wancfg['if'] = $a_ppps[$pppid]['ports']; + unset($a_ppps[$pppid]); + } else if ($wancfg['ipaddr'] == "dhcp") { + kill_dhclient_process($wancfg['if']); + } + if ($wancfg['ipaddrv6'] == "dhcp6") { + $pid = find_dhcp6c_process($wancfg['if']); + if ($pid) { + posix_kill($pid, SIGTERM); + } + } + } + $ppp = array(); + if ($wancfg['ipaddr'] != "ppp") { + unset($wancfg['ipaddr']); + } + if ($wancfg['ipaddrv6'] != "ppp") { + unset($wancfg['ipaddrv6']); + } + unset($wancfg['subnet']); + unset($wancfg['gateway']); + unset($wancfg['subnetv6']); + unset($wancfg['gatewayv6']); + unset($wancfg['dhcphostname']); + unset($wancfg['dhcprejectfrom']); + unset($wancfg['dhcp6-duid']); + unset($wancfg['dhcp6-ia-pd-len']); + unset($wancfg['dhcp6-ia-pd-send-hint']); + unset($wancfg['dhcp6prefixonly']); + unset($wancfg['dhcp6usev4iface']); + unset($wancfg['track6-interface']); + unset($wancfg['track6-prefix-id']); + unset($wancfg['prefix-6rd']); + unset($wancfg['prefix-6rd-v4plen']); + unset($wancfg['gateway-6rd']); + + unset($wancfg['adv_dhcp_pt_timeout']); + unset($wancfg['adv_dhcp_pt_retry']); + unset($wancfg['adv_dhcp_pt_select_timeout']); + unset($wancfg['adv_dhcp_pt_reboot']); + unset($wancfg['adv_dhcp_pt_backoff_cutoff']); + unset($wancfg['adv_dhcp_pt_initial_interval']); + + unset($wancfg['adv_dhcp_pt_values']); + + unset($wancfg['adv_dhcp_send_options']); + unset($wancfg['adv_dhcp_request_options']); + unset($wancfg['adv_dhcp_required_options']); + unset($wancfg['adv_dhcp_option_modifiers']); + + unset($wancfg['adv_dhcp_config_advanced']); + unset($wancfg['adv_dhcp_config_file_override']); + unset($wancfg['adv_dhcp_config_file_override_path']); + + unset($wancfg['adv_dhcp6_interface_statement_send_options']); + unset($wancfg['adv_dhcp6_interface_statement_request_options']); + unset($wancfg['adv_dhcp6_interface_statement_information_only_enable']); + unset($wancfg['adv_dhcp6_interface_statement_script']); + + unset($wancfg['adv_dhcp6_id_assoc_statement_address_enable']); + unset($wancfg['adv_dhcp6_id_assoc_statement_address']); + unset($wancfg['adv_dhcp6_id_assoc_statement_address_id']); + unset($wancfg['adv_dhcp6_id_assoc_statement_address_pltime']); + unset($wancfg['adv_dhcp6_id_assoc_statement_address_vltime']); + + unset($wancfg['adv_dhcp6_id_assoc_statement_prefix_enable']); + unset($wancfg['adv_dhcp6_id_assoc_statement_prefix']); + unset($wancfg['adv_dhcp6_id_assoc_statement_prefix_id']); + unset($wancfg['adv_dhcp6_id_assoc_statement_prefix_pltime']); + unset($wancfg['adv_dhcp6_id_assoc_statement_prefix_vltime']); + + unset($wancfg['adv_dhcp6_prefix_interface_statement_sla_id']); + unset($wancfg['adv_dhcp6_prefix_interface_statement_sla_len']); + + unset($wancfg['adv_dhcp6_authentication_statement_authname']); + unset($wancfg['adv_dhcp6_authentication_statement_protocol']); + unset($wancfg['adv_dhcp6_authentication_statement_algorithm']); + unset($wancfg['adv_dhcp6_authentication_statement_rdm']); + + unset($wancfg['adv_dhcp6_key_info_statement_keyname']); + unset($wancfg['adv_dhcp6_key_info_statement_realm']); + unset($wancfg['adv_dhcp6_key_info_statement_keyid']); + unset($wancfg['adv_dhcp6_key_info_statement_secret']); + unset($wancfg['adv_dhcp6_key_info_statement_expire']); + + unset($wancfg['adv_dhcp6_config_advanced']); + unset($wancfg['adv_dhcp6_config_file_override']); + unset($wancfg['adv_dhcp6_config_file_override_path']); + + unset($wancfg['pppoe_password']); + unset($wancfg['pptp_username']); + unset($wancfg['pptp_password']); + unset($wancfg['provider']); + unset($wancfg['ondemand']); + unset($wancfg['timeout']); + if (empty($wancfg['pppoe']['pppoe-reset-type'])) { + unset($wancfg['pppoe']['pppoe-reset-type']); + } + unset($wancfg['local']); + + unset($wancfg['remote']); + if (is_array($a_ppps[$pppid]) && in_array($wancfg['ipaddr'], array("ppp", "pppoe", "pptp", "l2tp"))) { + if ($wancfg['ipaddr'] != 'ppp') { + unset($a_ppps[$pppid]['apn']); + unset($a_ppps[$pppid]['phone']); + unset($a_ppps[$pppid]['provider']); + unset($a_ppps[$pppid]['ondemand']); + } + if (in_array($wancfg['ipaddr'], array("pppoe", "pptp", "l2tp"))) { + unset($a_ppps[$pppid]['localip']); + unset($a_ppps[$pppid]['subnet']); + unset($a_ppps[$pppid]['gateway']); + } + if ($wancfg['ipaddr'] != 'pppoe') { + unset($a_ppps[$pppid]['pppoe-reset-type']); + } + if ($wancfg['type'] != $_POST['type']) { + unset($a_ppps[$pppid]['idletimeout']); + } + } + + $wancfg['descr'] = remove_bad_chars($_POST['descr']); + $wancfg['enable'] = $_POST['enable'] == "yes" ? true : false; + + /* let return_gateways_array() do the magic on dynamic interfaces for us */ + switch ($_POST['type']) { + case "staticv4": + $wancfg['ipaddr'] = $_POST['ipaddr']; + $wancfg['subnet'] = $_POST['subnet']; + if ($_POST['gateway'] != "none") { + $wancfg['gateway'] = $_POST['gateway']; + } + break; + case "dhcp": + $wancfg['ipaddr'] = "dhcp"; + $wancfg['dhcphostname'] = $_POST['dhcphostname']; + $wancfg['alias-address'] = $_POST['alias-address']; + $wancfg['alias-subnet'] = $_POST['alias-subnet']; + $wancfg['dhcprejectfrom'] = $_POST['dhcprejectfrom']; + + $wancfg['adv_dhcp_pt_timeout'] = $_POST['adv_dhcp_pt_timeout']; + $wancfg['adv_dhcp_pt_retry'] = $_POST['adv_dhcp_pt_retry']; + $wancfg['adv_dhcp_pt_select_timeout'] = $_POST['adv_dhcp_pt_select_timeout']; + $wancfg['adv_dhcp_pt_reboot'] = $_POST['adv_dhcp_pt_reboot']; + $wancfg['adv_dhcp_pt_backoff_cutoff'] = $_POST['adv_dhcp_pt_backoff_cutoff']; + $wancfg['adv_dhcp_pt_initial_interval'] = $_POST['adv_dhcp_pt_initial_interval']; + + $wancfg['adv_dhcp_pt_values'] = $_POST['adv_dhcp_pt_values']; + + $wancfg['adv_dhcp_send_options'] = $_POST['adv_dhcp_send_options']; + $wancfg['adv_dhcp_request_options'] = $_POST['adv_dhcp_request_options']; + $wancfg['adv_dhcp_required_options'] = $_POST['adv_dhcp_required_options']; + $wancfg['adv_dhcp_option_modifiers'] = $_POST['adv_dhcp_option_modifiers']; + + $wancfg['adv_dhcp_config_advanced'] = $_POST['adv_dhcp_config_advanced']; + $wancfg['adv_dhcp_config_file_override'] = $_POST['adv_dhcp_config_file_override']; + $wancfg['adv_dhcp_config_file_override_path'] = $_POST['adv_dhcp_config_file_override_path']; + + $wancfg['dhcp_plus'] = $_POST['dhcp_plus'] == "yes" ? true : false; + if ($gateway_item) { + $a_gateways[] = $gateway_item; + } + break; + case "ppp": + $a_ppps[$pppid]['ptpid'] = $_POST['ptpid']; + $a_ppps[$pppid]['type'] = $_POST['type']; + $a_ppps[$pppid]['if'] = $_POST['type'].$_POST['ptpid']; + $a_ppps[$pppid]['ports'] = $_POST['port']; + $a_ppps[$pppid]['username'] = $_POST['ppp_username']; + $a_ppps[$pppid]['password'] = base64_encode($_POST['ppp_password']); + $a_ppps[$pppid]['phone'] = $_POST['phone']; + $a_ppps[$pppid]['apn'] = $_POST['apn']; + $wancfg['if'] = $_POST['type'] . $_POST['ptpid']; + $wancfg['ipaddr'] = $_POST['type']; + break; + + case "pppoe": + $a_ppps[$pppid]['ptpid'] = $_POST['ptpid']; + $a_ppps[$pppid]['type'] = $_POST['type']; + $a_ppps[$pppid]['if'] = $_POST['type'].$_POST['ptpid']; + if (isset($_POST['ppp_port'])) { + $a_ppps[$pppid]['ports'] = $_POST['ppp_port']; + } else { + $a_ppps[$pppid]['ports'] = $wancfg['if']; + } + $a_ppps[$pppid]['username'] = $_POST['pppoe_username']; + $a_ppps[$pppid]['password'] = base64_encode($_POST['pppoe_password']); + if (!empty($_POST['provider'])) { + $a_ppps[$pppid]['provider'] = $_POST['provider']; + } else { + $a_ppps[$pppid]['provider'] = true; + } + $a_ppps[$pppid]['ondemand'] = $_POST['pppoe_dialondemand'] ? true : false; + if (!empty($_POST['pppoe_idletimeout'])) { + $a_ppps[$pppid]['idletimeout'] = $_POST['pppoe_idletimeout']; + } else { + unset($a_ppps[$pppid]['idletimeout']); + } + + if (!empty($_POST['pppoe-reset-type'])) { + $a_ppps[$pppid]['pppoe-reset-type'] = $_POST['pppoe-reset-type']; + } else { + unset($a_ppps[$pppid]['pppoe-reset-type']); + } + $wancfg['if'] = $_POST['type'].$_POST['ptpid']; + $wancfg['ipaddr'] = $_POST['type']; + if ($gateway_item) { + $a_gateways[] = $gateway_item; + } + + break; + case "pptp": + case "l2tp": + $a_ppps[$pppid]['ptpid'] = $_POST['ptpid']; + $a_ppps[$pppid]['type'] = $_POST['type']; + $a_ppps[$pppid]['if'] = $_POST['type'].$_POST['ptpid']; + if (isset($_POST['ppp_port'])) { + $a_ppps[$pppid]['ports'] = $_POST['ppp_port']; + } else { + $a_ppps[$pppid]['ports'] = $wancfg['if']; + } + $a_ppps[$pppid]['username'] = $_POST['pptp_username']; + $a_ppps[$pppid]['password'] = base64_encode($_POST['pptp_password']); + $a_ppps[$pppid]['localip'] = $_POST['pptp_local']; + $a_ppps[$pppid]['subnet'] = $_POST['pptp_subnet']; + $a_ppps[$pppid]['gateway'] = $_POST['pptp_remote']; + $a_ppps[$pppid]['ondemand'] = $_POST['pptp_dialondemand'] ? true : false; + if (!empty($_POST['pptp_idletimeout'])) { + $a_ppps[$pppid]['idletimeout'] = $_POST['pptp_idletimeout']; + } else { + unset($a_ppps[$pppid]['idletimeout']); + } + $wancfg['if'] = $_POST['type'].$_POST['ptpid']; + $wancfg['ipaddr'] = $_POST['type']; + if ($gateway_item) { + $a_gateways[] = $gateway_item; + } + break; + case "none": + break; + } + switch ($_POST['type6']) { + case "staticv6": + $wancfg['ipaddrv6'] = $_POST['ipaddrv6']; + $wancfg['subnetv6'] = $_POST['subnetv6']; + if ($_POST['gatewayv6'] != "none") { + $wancfg['gatewayv6'] = $_POST['gatewayv6']; + } + break; + case "slaac": + $wancfg['ipaddrv6'] = "slaac"; + break; + case "dhcp6": + $wancfg['ipaddrv6'] = "dhcp6"; + $wancfg['dhcp6-duid'] = $_POST['dhcp6-duid']; + $wancfg['dhcp6-ia-pd-len'] = $_POST['dhcp6-ia-pd-len']; + if ($_POST['dhcp6-ia-pd-send-hint'] == "yes") { + $wancfg['dhcp6-ia-pd-send-hint'] = true; + } + if ($_POST['dhcp6prefixonly'] == "yes") { + $wancfg['dhcp6prefixonly'] = true; + } + if ($_POST['dhcp6usev4iface'] == "yes") { + $wancfg['dhcp6usev4iface'] = true; + } + + if (!empty($_POST['adv_dhcp6_interface_statement_send_options'])) { + $wancfg['adv_dhcp6_interface_statement_send_options'] = $_POST['adv_dhcp6_interface_statement_send_options']; + } + if (!empty($_POST['adv_dhcp6_interface_statement_request_options'])) { + $wancfg['adv_dhcp6_interface_statement_request_options'] = $_POST['adv_dhcp6_interface_statement_request_options']; + } + if (isset($_POST['adv_dhcp6_interface_statement_information_only_enable'])) { + $wancfg['adv_dhcp6_interface_statement_information_only_enable'] = $_POST['adv_dhcp6_interface_statement_information_only_enable']; + } + if (!empty($_POST['adv_dhcp6_interface_statement_script'])) { + $wancfg['adv_dhcp6_interface_statement_script'] = $_POST['adv_dhcp6_interface_statement_script']; + } + + if (isset($_POST['adv_dhcp6_id_assoc_statement_address_enable'])) { + $wancfg['adv_dhcp6_id_assoc_statement_address_enable'] = $_POST['adv_dhcp6_id_assoc_statement_address_enable']; + } + if (!empty($_POST['adv_dhcp6_id_assoc_statement_address'])) { + $wancfg['adv_dhcp6_id_assoc_statement_address'] = $_POST['adv_dhcp6_id_assoc_statement_address']; + } + if (is_numericint($_POST['adv_dhcp6_id_assoc_statement_address_id'])) { + $wancfg['adv_dhcp6_id_assoc_statement_address_id'] = $_POST['adv_dhcp6_id_assoc_statement_address_id']; + } + if (!empty($_POST['adv_dhcp6_id_assoc_statement_address_pltime'])) { + $wancfg['adv_dhcp6_id_assoc_statement_address_pltime'] = $_POST['adv_dhcp6_id_assoc_statement_address_pltime']; + } + if (!empty($_POST['adv_dhcp6_id_assoc_statement_address_vltime'])) { + $wancfg['adv_dhcp6_id_assoc_statement_address_vltime'] = $_POST['adv_dhcp6_id_assoc_statement_address_vltime']; + } + + if (isset($_POST['adv_dhcp6_id_assoc_statement_prefix_enable'])) { + $wancfg['adv_dhcp6_id_assoc_statement_prefix_enable'] = $_POST['adv_dhcp6_id_assoc_statement_prefix_enable']; + } + if (!empty($_POST['adv_dhcp6_id_assoc_statement_prefix'])) { + $wancfg['adv_dhcp6_id_assoc_statement_prefix'] = $_POST['adv_dhcp6_id_assoc_statement_prefix']; + } + if (is_numericint($_POST['adv_dhcp6_id_assoc_statement_prefix_id'])) { + $wancfg['adv_dhcp6_id_assoc_statement_prefix_id'] = $_POST['adv_dhcp6_id_assoc_statement_prefix_id']; + } + if (!empty($_POST['adv_dhcp6_id_assoc_statement_prefix_pltime'])) { + $wancfg['adv_dhcp6_id_assoc_statement_prefix_pltime'] = $_POST['adv_dhcp6_id_assoc_statement_prefix_pltime']; + } + if (!empty($_POST['adv_dhcp6_id_assoc_statement_prefix_vltime'])) { + $wancfg['adv_dhcp6_id_assoc_statement_prefix_vltime'] = $_POST['adv_dhcp6_id_assoc_statement_prefix_vltime']; + } + + if (is_numericint($_POST['adv_dhcp6_prefix_interface_statement_sla_id'])) { + $wancfg['adv_dhcp6_prefix_interface_statement_sla_id'] = $_POST['adv_dhcp6_prefix_interface_statement_sla_id']; + } + if (is_numericint($_POST['adv_dhcp6_prefix_interface_statement_sla_len'])) { + $wancfg['adv_dhcp6_prefix_interface_statement_sla_len'] = $_POST['adv_dhcp6_prefix_interface_statement_sla_len']; + } + + if (!empty($_POST['adv_dhcp6_authentication_statement_authname'])) { + $wancfg['adv_dhcp6_authentication_statement_authname'] = $_POST['adv_dhcp6_authentication_statement_authname']; + } + if (!empty($_POST['adv_dhcp6_authentication_statement_protocol'])) { + $wancfg['adv_dhcp6_authentication_statement_protocol'] = $_POST['adv_dhcp6_authentication_statement_protocol']; + } + if (!empty($_POST['adv_dhcp6_authentication_statement_algorithm'])) { + $wancfg['adv_dhcp6_authentication_statement_algorithm'] = $_POST['adv_dhcp6_authentication_statement_algorithm']; + } + if (!empty($_POST['adv_dhcp6_authentication_statement_rdm'])) { + $wancfg['adv_dhcp6_authentication_statement_rdm'] = $_POST['adv_dhcp6_authentication_statement_rdm']; + } + + if (!empty($_POST['adv_dhcp6_key_info_statement_keyname'])) { + $wancfg['adv_dhcp6_key_info_statement_keyname'] = $_POST['adv_dhcp6_key_info_statement_keyname']; + } + if (!empty($_POST['adv_dhcp6_key_info_statement_realm'])) { + $wancfg['adv_dhcp6_key_info_statement_realm'] = $_POST['adv_dhcp6_key_info_statement_realm']; + } + if (!empty($_POST['adv_dhcp6_key_info_statement_keyid'])) { + $wancfg['adv_dhcp6_key_info_statement_keyid'] = $_POST['adv_dhcp6_key_info_statement_keyid']; + } + if (!empty($_POST['adv_dhcp6_key_info_statement_secret'])) { + $wancfg['adv_dhcp6_key_info_statement_secret'] = $_POST['adv_dhcp6_key_info_statement_secret']; + } + if (!empty($_POST['adv_dhcp6_key_info_statement_expire'])) { + $wancfg['adv_dhcp6_key_info_statement_expire'] = $_POST['adv_dhcp6_key_info_statement_expire']; + } + + if (!empty($_POST['adv_dhcp6_config_advanced'])) { + $wancfg['adv_dhcp6_config_advanced'] = $_POST['adv_dhcp6_config_advanced']; + } + if (!empty($_POST['adv_dhcp6_config_file_override'])) { + $wancfg['adv_dhcp6_config_file_override'] = $_POST['adv_dhcp6_config_file_override']; + } + if (!empty($_POST['adv_dhcp6_config_file_override_path'])) { + $wancfg['adv_dhcp6_config_file_override_path'] = $_POST['adv_dhcp6_config_file_override_path']; + } + + if ($gateway_item) { + $a_gateways[] = $gateway_item; + } + break; + case "6rd": + $wancfg['ipaddrv6'] = "6rd"; + $wancfg['prefix-6rd'] = $_POST['prefix-6rd']; + $wancfg['prefix-6rd-v4plen'] = $_POST['prefix-6rd-v4plen']; + $wancfg['gateway-6rd'] = $_POST['gateway-6rd']; + if ($gateway_item) { + $a_gateways[] = $gateway_item; + } + break; + case "6to4": + $wancfg['ipaddrv6'] = "6to4"; + break; + case "track6": + $wancfg['ipaddrv6'] = "track6"; + $wancfg['track6-interface'] = $_POST['track6-interface']; + if ($_POST['track6-prefix-id--hex'] === "") { + $wancfg['track6-prefix-id'] = 0; + } else if (is_numeric("0x" . $_POST['track6-prefix-id--hex'])) { + $wancfg['track6-prefix-id'] = intval($_POST['track6-prefix-id--hex'], 16); + } else { + $wancfg['track6-prefix-id'] = 0; + } + break; + case "none": + break; + } + handle_pppoe_reset($_POST); + + if ($_POST['blockpriv'] == "yes") { + $wancfg['blockpriv'] = true; + } else { + unset($wancfg['blockpriv']); + } + if ($_POST['blockbogons'] == "yes") { + $wancfg['blockbogons'] = true; + } else { + unset($wancfg['blockbogons']); + } + $wancfg['spoofmac'] = $_POST['spoofmac']; + if (empty($_POST['mtu'])) { + unset($wancfg['mtu']); + } else { + $wancfg['mtu'] = $_POST['mtu']; + } + if (empty($_POST['mss'])) { + unset($wancfg['mss']); + } else { + $wancfg['mss'] = $_POST['mss']; + } + if (empty($_POST['mediaopt'])) { + unset($wancfg['media']); + unset($wancfg['mediaopt']); + } else { + $mediaopts = explode(' ', $_POST['mediaopt']); + if ($mediaopts[0] != '') { + $wancfg['media'] = $mediaopts[0]; + } + if ($mediaopts[1] != '') { + $wancfg['mediaopt'] = $mediaopts[1]; + } else { + unset($wancfg['mediaopt']); + } + } + if (isset($wancfg['wireless'])) { + handle_wireless_post(); + } + + conf_mount_ro(); + write_config(); + + if (file_exists("{$g['tmp_path']}/.interfaces.apply")) { + $toapplylist = unserialize(file_get_contents("{$g['tmp_path']}/.interfaces.apply")); + } else { + $toapplylist = array(); + } + $toapplylist[$if]['ifcfg'] = $old_wancfg; + $toapplylist[$if]['ppps'] = $old_ppps; + file_put_contents("{$g['tmp_path']}/.interfaces.apply", serialize($toapplylist)); + + mark_subsystem_dirty('interfaces'); + + /* regenerate cron settings/crontab file */ + configure_cron(); + + header("Location: interfaces.php?if={$if}"); + exit; + } + +} // end if ($_POST) + +function handle_wireless_post() { + global $_POST, $config, $g, $wancfg, $if, $wl_countries_attr, $wlanbaseif; + if (!is_array($wancfg['wireless'])) { + $wancfg['wireless'] = array(); + } + $wancfg['wireless']['standard'] = $_POST['standard']; + $wancfg['wireless']['mode'] = $_POST['mode']; + $wancfg['wireless']['protmode'] = $_POST['protmode']; + $wancfg['wireless']['ssid'] = $_POST['ssid']; + $wancfg['wireless']['channel'] = $_POST['channel']; + $wancfg['wireless']['authmode'] = $_POST['authmode']; + $wancfg['wireless']['txpower'] = $_POST['txpower']; + $wancfg['wireless']['distance'] = $_POST['distance']; + $wancfg['wireless']['regdomain'] = $_POST['regdomain']; + $wancfg['wireless']['regcountry'] = $_POST['regcountry']; + $wancfg['wireless']['reglocation'] = $_POST['reglocation']; + if (!empty($wancfg['wireless']['regdomain']) && !empty($wancfg['wireless']['regcountry'])) { + foreach ($wl_countries_attr as $wl_country) { + if ($wancfg['wireless']['regcountry'] == $wl_country['ID']) { + $wancfg['wireless']['regdomain'] = $wl_country['rd'][0]['REF']; + break; + } + } + } + if (!is_array($wancfg['wireless']['wpa'])) { + $wancfg['wireless']['wpa'] = array(); + } + $wancfg['wireless']['wpa']['macaddr_acl'] = $_POST['macaddr_acl']; + $wancfg['wireless']['wpa']['auth_algs'] = $_POST['auth_algs']; + $wancfg['wireless']['wpa']['wpa_mode'] = $_POST['wpa_mode']; + $wancfg['wireless']['wpa']['wpa_key_mgmt'] = $_POST['wpa_key_mgmt']; + $wancfg['wireless']['wpa']['wpa_pairwise'] = $_POST['wpa_pairwise']; + $wancfg['wireless']['wpa']['wpa_group_rekey'] = $_POST['wpa_group_rekey']; + $wancfg['wireless']['wpa']['wpa_gmk_rekey'] = $_POST['wpa_gmk_rekey']; + $wancfg['wireless']['wpa']['passphrase'] = $_POST['passphrase']; + $wancfg['wireless']['wpa']['ext_wpa_sw'] = $_POST['ext_wpa_sw']; + $wancfg['wireless']['auth_server_addr'] = $_POST['auth_server_addr']; + $wancfg['wireless']['auth_server_port'] = $_POST['auth_server_port']; + $wancfg['wireless']['auth_server_shared_secret'] = $_POST['auth_server_shared_secret']; + $wancfg['wireless']['auth_server_addr2'] = $_POST['auth_server_addr2']; + $wancfg['wireless']['auth_server_port2'] = $_POST['auth_server_port2']; + $wancfg['wireless']['auth_server_shared_secret2'] = $_POST['auth_server_shared_secret2']; + + if ($_POST['persistcommonwireless'] == "yes") { + if (!is_array($config['wireless'])) { + $config['wireless'] = array(); + } + if (!is_array($config['wireless']['interfaces'])) { + $config['wireless']['interfaces'] = array(); + } + if (!is_array($config['wireless']['interfaces'][$wlanbaseif])) { + $config['wireless']['interfaces'][$wlanbaseif] = array(); + } + } else if (isset($config['wireless']['interfaces'][$wlanbaseif])) { + unset($config['wireless']['interfaces'][$wlanbaseif]); + } + if (isset($_POST['diversity']) && is_numeric($_POST['diversity'])) { + $wancfg['wireless']['diversity'] = $_POST['diversity']; + } else if (isset($wancfg['wireless']['diversity'])) { + unset($wancfg['wireless']['diversity']); + } + if (isset($_POST['txantenna']) && is_numeric($_POST['txantenna'])) { + $wancfg['wireless']['txantenna'] = $_POST['txantenna']; + } else if (isset($wancfg['wireless']['txantenna'])) { + unset($wancfg['wireless']['txantenna']); + } + if (isset($_POST['rxantenna']) && is_numeric($_POST['rxantenna'])) { + $wancfg['wireless']['rxantenna'] = $_POST['rxantenna']; + } else if (isset($wancfg['wireless']['rxantenna'])) { + unset($wancfg['wireless']['rxantenna']); + } + if ($_POST['hidessid_enable'] == "yes") { + $wancfg['wireless']['hidessid']['enable'] = true; + } else if (isset($wancfg['wireless']['hidessid']['enable'])) { + unset($wancfg['wireless']['hidessid']['enable']); + } + if ($_POST['mac_acl_enable'] == "yes") { + $wancfg['wireless']['wpa']['mac_acl_enable'] = true; + } else if (isset($wancfg['wireless']['wpa']['mac_acl_enable'])) { + unset($wancfg['wireless']['wpa']['mac_acl_enable']); + } + if ($_POST['rsn_preauth'] == "yes") { + $wancfg['wireless']['wpa']['rsn_preauth'] = true; + } else { + unset($wancfg['wireless']['wpa']['rsn_preauth']); + } + if ($_POST['ieee8021x'] == "yes") { + $wancfg['wireless']['wpa']['ieee8021x']['enable'] = true; + } else if (isset($wancfg['wireless']['wpa']['ieee8021x']['enable'])) { + unset($wancfg['wireless']['wpa']['ieee8021x']['enable']); + } + if ($_POST['wpa_strict_rekey'] == "yes") { + $wancfg['wireless']['wpa']['wpa_strict_rekey'] = true; + } else if (isset($wancfg['wireless']['wpa']['wpa_strict_rekey'])) { + unset($wancfg['wireless']['wpa']['wpa_strict_rekey']); + } + if ($_POST['debug_mode'] == "yes") { + $wancfg['wireless']['wpa']['debug_mode'] = true; + } else if (isset($wancfg['wireless']['wpa']['debug_mode'])) { + sunset($wancfg['wireless']['wpa']['debug_mode']); + } + if ($_POST['wpa_enable'] == "yes") { + $wancfg['wireless']['wpa']['enable'] = $_POST['wpa_enable'] = true; + } else if (isset($wancfg['wireless']['wpa']['enable'])) { + unset($wancfg['wireless']['wpa']['enable']); + } + if ($_POST['wep_enable'] == "yes") { + if (!is_array($wancfg['wireless']['wep'])) { + $wancfg['wireless']['wep'] = array(); + } + $wancfg['wireless']['wep']['enable'] = $_POST['wep_enable'] = true; + } else if (isset($wancfg['wireless']['wep'])) { + unset($wancfg['wireless']['wep']); + } + if ($_POST['wme_enable'] == "yes") { + if (!is_array($wancfg['wireless']['wme'])) { + $wancfg['wireless']['wme'] = array(); + } + $wancfg['wireless']['wme']['enable'] = $_POST['wme_enable'] = true; + } else if (isset($wancfg['wireless']['wme']['enable'])) { + unset($wancfg['wireless']['wme']['enable']); + } + if ($_POST['puremode'] == "11g") { + if (!is_array($wancfg['wireless']['pureg'])) { + $wancfg['wireless']['pureg'] = array(); + } + $wancfg['wireless']['pureg']['enable'] = true; + } else if ($_POST['puremode'] == "11n") { + if (!is_array($wancfg['wireless']['puren'])) { + $wancfg['wireless']['puren'] = array(); + } + $wancfg['wireless']['puren']['enable'] = true; + } else { + if (isset($wancfg['wireless']['pureg'])) { + unset($wancfg['wireless']['pureg']); + } + if (isset($wancfg['wireless']['puren'])) { + unset($wancfg['wireless']['puren']); + } + } + if ($_POST['apbridge_enable'] == "yes") { + if (!is_array($wancfg['wireless']['apbridge'])) { + $wancfg['wireless']['apbridge'] = array(); + } + $wancfg['wireless']['apbridge']['enable'] = $_POST['apbridge_enable'] = true; + } else if (isset($wancfg['wireless']['apbridge']['enable'])) { + unset($wancfg['wireless']['apbridge']['enable']); + } + if ($_POST['standard'] == "11g Turbo" || $_POST['standard'] == "11a Turbo") { + if (!is_array($wancfg['wireless']['turbo'])) { + $wancfg['wireless']['turbo'] = array(); + } + $wancfg['wireless']['turbo']['enable'] = true; + } else if (isset($wancfg['wireless']['turbo']['enable'])) { + unset($wancfg['wireless']['turbo']['enable']); + } + $wancfg['wireless']['wep']['key'] = array(); + for ($i = 1; $i <= 4; $i++) { + if ($_POST['key' . $i]) { + $newkey = array(); + $newkey['value'] = $_POST['key' . $i]; + if ($_POST['txkey'] == $i) { + $newkey['txkey'] = true; + } + $wancfg['wireless']['wep']['key'][] = $newkey; + } + } + interface_sync_wireless_clones($wancfg, true); +} + +function check_wireless_mode() { + global $_POST, $config, $g, $wlan_modes, $wancfg, $if, $wlanif, $wlanbaseif, $old_wireless_mode, $input_errors; + + if ($wancfg['wireless']['mode'] == $_POST['mode']) { + return; + } + + if (does_interface_exist(interface_get_wireless_clone($wlanbaseif))) { + $clone_count = 1; + } else { + $clone_count = 0; + } + + if (isset($config['wireless']['clone']) && is_array($config['wireless']['clone'])) { + foreach ($config['wireless']['clone'] as $clone) { + if ($clone['if'] == $wlanbaseif) { + $clone_count++; + } + } + } + + if ($clone_count > 1) { + $old_wireless_mode = $wancfg['wireless']['mode']; + $wancfg['wireless']['mode'] = $_POST['mode']; + if (!interface_wireless_clone("{$wlanif}_", $wancfg)) { + $input_errors[] = sprintf(gettext("Unable to change mode to %s. You may already have the maximum number of wireless clones supported in this mode."), $wlan_modes[$wancfg['wireless']['mode']]); + } else { + mwexec("/sbin/ifconfig " . escapeshellarg($wlanif) . "_ destroy"); + } + $wancfg['wireless']['mode'] = $old_wireless_mode; + } +} + +// Find all possible media options for the interface +$mediaopts_list = array(); +$intrealname = $config['interfaces'][$if]['if']; +exec("/sbin/ifconfig -m $intrealname | grep \"media \"", $mediaopts); +foreach ($mediaopts as $mediaopt) { + preg_match("/media (.*)/", $mediaopt, $matches); + if (preg_match("/(.*) mediaopt (.*)/", $matches[1], $matches1)) { + // there is media + mediaopt like "media 1000baseT mediaopt full-duplex" + array_push($mediaopts_list, $matches1[1] . " " . $matches1[2]); + } else { + // there is only media like "media 1000baseT" + array_push($mediaopts_list, $matches[1]); + } +} + +$pgtitle = array(gettext("Interfaces"), $pconfig['descr']); +$shortcut_section = "interfaces"; + +$types4 = array("none" => gettext("None"), "staticv4" => gettext("Static IPv4"), "dhcp" => gettext("DHCP"), "ppp" => gettext("PPP"), "pppoe" => gettext("PPPoE"), "pptp" => gettext("PPTP"), "l2tp" => gettext("L2TP")); +$types6 = array("none" => gettext("None"), "staticv6" => gettext("Static IPv6"), "dhcp6" => gettext("DHCP6"), "slaac" => gettext("SLAAC"), "6rd" => gettext("6rd Tunnel"), "6to4" => gettext("6to4 Tunnel"), "track6" => gettext("Track Interface")); + +$closehead = false; + +// Get the MAC address +$ip = $_SERVER['REMOTE_ADDR']; +$mymac = `/usr/sbin/arp -an | grep '('{$ip}')' | head -n 1 | cut -d" " -f4`; +$mymac = str_replace("\n","",$mymac); + +function build_mediaopts_list() { + global $mediaopts_list; + + $list = ["" => "Default (no preference, typically autoselect)", + " " => "------- Media Supported by this interface -------" + ]; + + foreach ($mediaopts_list as $mediaopt) { + $list[$mediaopt] = $mediaopt; + } + + return($list); +} + +function build_gateway_list() { + global $a_gateways; + + $list = array("none" => "None"); + foreach ($a_gateways as $gateway) { + if (($gateway['interface'] == $if) && (is_ipaddrv4($gateway['gateway']))) { + $list[$gateway['name']] = $gateway['name'] . " - " . $gateway['gateway']; + } + } + + return($list); +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if (is_subsystem_dirty('interfaces')) + print_info_box_np(sprintf(gettext("The %s configuration has been changed."), $wancfg['descr']) . "<br />" . + gettext("You must apply the changes in order for them to take effect. Don't forget to adjust the DHCP Server range if needed after applying.")); + +if ($savemsg) + print_info_box($savemsg, 'success'); + + +require('classes/Form.class.php'); + +$form = new Form(new Form_Button( + 'Submit', + gettext("Save") +)); + +$section = new Form_Section('General configuration'); + +$section->addInput(new Form_Checkbox( + 'enable', + 'Enable', + 'Enable interface', + $pconfig['enable'], + 'yes' +)); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('Enter a description (name) for the interface here.'); + +$section->addInput(new Form_Select( + 'type', + 'IPv4 Configuration Type', + $pconfig['type'], + $types4 +)); + +$section->addInput(new Form_Select( + 'type6', + 'IPv6 Configuration Type', + $pconfig['type6'], + $types6 +)); + +$macaddress = new Form_Input( + 'mac', + 'MAC Address', + 'text', + $pconfig['mac'], + ['placeholder' => 'xx:xx:xx:xx:xx:xx'] +); + +$btnmymac = new Form_Button( + 'btnmymac', + 'Copy My MAC' + ); + +$btnmymac->removeClass('btn-primary')->addClass('btn-success btn-sm'); + +$group = new Form_Group('MAC controls'); +$group->add($macaddress); +$group->add($btnmymac); +$group->setHelp('This field can be used to modify ("spoof") the MAC address of this interface.' . '<br />' . + 'Enter a MAC address in the following format: xx:xx:xx:xx:xx:xx or leave blank'); +$section->add($group); + +$section->addInput(new Form_Input( + 'mtu', + 'MTU', + 'number', + $pconfig['mtu'] +))->setHelp('If you leave this field blank, the adapter\'s default MTU will be used. ' . + 'This is typically 1500 bytes but can vary in some circumstances.'); + +$section->addInput(new Form_Input( + 'mss', + 'MSS', + 'number', + $pconfig['mss'] +))->setHelp('If you enter a value in this field, then MSS clamping for TCP connections to the value entered above minus 40 (TCP/IP ' . + 'header size) will be in effect.'); + +if (count($mediaopts_list) > 0) { + $section->addInput(new Form_Select( + 'mediaopt', + 'Speed and Duplex', + rtrim($mediaopt_from_config), + build_mediaopts_list() + ))->setHelp('Here you can explicitly set speed and duplex mode for this interface.' . '<br />' . + 'WARNING: You MUST leave this set to autoselect (automatically negotiate speed) unless the port this interface connects to has its speed and duplex forced.'); +} + +$form->add($section); + +$section = new Form_Section('Static IPv4 configuration'); + +$section->addInput(new Form_IpAddress( + 'ipaddr', + 'IPv4 Address', + $pconfig['ipaddr'] +))->addMask('subnet', $pconfig['subnet'], 32); + +$group = new Form_Group('IPv4 Upstream gateway'); + +$group->add(new Form_Select( + 'gateway', + 'IPv4 Upstream Gateway', + $pconfig['gateway'], + build_gateway_list() +)); + +$group->add(new Form_Button( + 'addgw', + 'Add a new gateway' +))->removeClass('btn-primary'); + +$group->setHelp('If this interface is an Internet connection, select an existing Gateway from the list or add a new one using the "Add" button.' . '<br />' . + 'On local LANs the upstream gateway should be "none". '); + +$section->add($group); + +$group = new Form_group('New gateway'); + +$group->add(new Form_Checkbox( + 'defaultgw', + null, + 'Default gateway', + ($if == "wan" || $if == "WAN") +)); + +$group->add(new Form_Input( + 'name', + 'Gateway name', + 'text', + $wancfg['descr'] . "GW" +))->setHelp('Name'); + +$group->add(new Form_IpAddress( + 'gatewayip', + 'Gateway IPv4', + null +))->setHelp('IP address'); + +$group->add(new Form_Input( + 'gatewaydescr', + 'Description', + 'text' +))->setHelp('Description'); + +$group->add(new Form_Button( + 'add', + 'Add' +))->removeClass('btn-primary')->addClass('btn-success'); + +$section->add($group); + +$form->add($section); + +$section = new Form_Section('Static IPv6 configuration'); + +$section->addInput(new Form_IpAddress( + 'ipaddrv6', + 'IPv6 address', + $pconfig['ipaddrv6'] +))->addMask('subnetv6', $pconfig['sunbetv6'], 128); + +$group = new Form_Group('IPv6 Upstream gateway'); + +$group->add(new Form_Select( + 'gatewayv6', + 'IPv4 Upstream Gateway', + $pconfig['gateway'], + build_gateway_list() +)); + +$group->add(new Form_Button( + 'addgwv6', + 'Add a new gateway' +))->removeClass('btn-primary'); + +$group->setHelp('If this interface is an Internet connection, select an existing Gateway from the list or add a new one using the "Add" button.' . '<br />' . + 'On local LANs the upstream gateway should be "none". '); + +$group = new Form_group('IPv6 upstream gateway'); + +$group->add(new Form_Checkbox( + 'defaultgwv6', + null, + 'Default gateway', + ($if == "wan" || $if == "WAN") +)); + +$group->add(new Form_Input( + 'namev6', + 'Gateway name', + 'text', + $wancfg['descr'] . "GWv6" +))->setHelp('Name'); + +$group->add(new Form_IpAddress( + 'gatewayipv6', + 'Gateway IPv6', + null +))->setHelp('IP address'); + +$group->add(new Form_Input( + 'gatewaydescrv6', + 'Description', + 'text' +))->setHelp('Description'); + +$group->add(new Form_Button( + 'addv6', + 'Add' +))->removeClass('btn-primary')->addClass('btn-success'); + +$section->add($group); + +$form->add($section); + +// ==== DHCP client configuration ============================= + +$section = new Form_Section('DHCP client configuration'); + +$section->addInput(new Form_Input( + 'dhcphostname', + 'Hostname', + 'text', + $pconfig['dhcphostname'] +))->setHelp('The value in this field is sent as the DHCP client identifier and hostname when requesting a DHCP lease. Some ISPs may require this (for client identification).'); + +$section->addInput(new Form_IpAddress( + 'alias-address', + 'Alias IPv4 address', + $pconfig['alias-address'] +))->addMask('alias-subnet', $pconfig['alias-subnet'], 32)->setHelp('The value in this field is used as a fixed alias IPv4 address by the DHCP client.'); + +$section->addInput(new Form_Input( + 'dhcprejectfrom', + 'Reject leases from', + 'text', + $pconfig['dhcprejectfrom'] +))->setHelp('If there is a certain upstream DHCP server that should be ignored, place the IP address or subnet of the DHCP server to be ignored here. ' . + 'This is useful for rejecting leases from cable modems that offer private IPs when they lose upstream sync.'); + +$group = new Form_Group('Protocol timing'); + +$group->add(new Form_Input( + 'adv_dhcp_pt_timeout', + null, + 'number', + $pconfig['adv_dhcp_pt_timeout'] +))->setHelp('Timeout'); + +$group->add(new Form_Input( + 'adv_dhcp_pt_retry', + null, + 'number', + $pconfig['adv_dhcp_pt_retry'] +))->setHelp('Retry'); + +$group->add(new Form_Input( + 'adv_dhcp_pt_select_timeout', + null, + 'number', + $pconfig['adv_dhcp_pt_select_timeout'] +))->setHelp('Select timeout'); + +$group->add(new Form_Input( + 'adv_dhcp_pt_reboot', + null, + 'number', + $pconfig['adv_dhcp_pt_reboot'] +))->setHelp('Reboot'); + +$group->add(new Form_Input( + 'adv_dhcp_pt_backoff_cutoff', + null, + 'number', + $pconfig['adv_dhcp_pt_backoff_cutoff'] +))->setHelp('Backoff cutoff'); + +$group->add(new Form_Input( + 'adv_dhcp_pt_initial_interval', + null, + 'number', + $pconfig['adv_dhcp_pt_initial_interval'] +))->setHelp('Initial interval'); + +$section->add($group); + +$group = new Form_Group('Presets'); + +$group->add(new Form_Checkbox( + 'adv_dhcp_pt_values', + null, + 'Free BDS default', + null, + 'DHCP' +))->displayAsRadio(); + +$group->add(new Form_Checkbox( + 'adv_dhcp_pt_values', + null, + 'Clear', + null, + 'Clear' +))->displayAsRadio(); + +$group->add(new Form_Checkbox( + 'adv_dhcp_pt_values', + null, + 'pfSense Default', + null, + 'pfSense' +))->displayAsRadio(); + +$group->add(new Form_Checkbox( + 'adv_dhcp_pt_values', + null, + 'Saved Cfg', + null, + 'SavedCfg' +))->displayAsRadio(); + +$group->setHelp('The values in these fields are DHCP protocol timings used when requesting a lease.' . '<br />' . + '<a href="http://www.freebsd.org/cgi/man.cgi?query=dhclient.conf&sektion=5#PROTOCOL_TIMING">' . 'See here more information' . '</a>'); + +$section->add($group); + +$form->add($section); + +$section = new Form_Section('Lease Requirements and Requests'); + +$section->addInput(new Form_Input( + 'adv_dhcp_send_options', + 'Send options', + 'text', + $pconfig['adv_dhcp_send_options'] +))->sethelp('The values in this field are DHCP options to be sent when requesting a DHCP lease. [option declaration [, ...]]' . '<br />' . + 'Value Substitutions: {interface}, {hostname}, {mac_addr_asciiCD}, {mac_addr_hexCD}' . '<br />' . + 'Where C is U(pper) or L(ower) Case, and D is " :-." Delimiter (space, colon, hyphen, or period) (omitted for none).' . '<br />' . + 'Some ISPs may require certain options be or not be sent.'); + +$section->addInput(new Form_Input( + 'adv_dhcp_request_options', + 'Request options', + 'text', + $pconfig['adv_dhcp_request_options'] +))->sethelp('The values in this field are DHCP option 55 to be sent when requesting a DHCP lease. [option [, ...]]' . '<br />' . + 'Some ISPs may require certain options be or not be requested.'); + +$section->addInput(new Form_Input( + 'adv_dhcp_require_options', + 'Request options', + 'text', + $pconfig['adv_dhcp_require_options'] +))->sethelp('The values in this field are DHCP options required by the client when requesting a DHCP lease. [option [, ...]]'); + +$section->addInput(new Form_Input( + 'adv_dhcp_option_modifiers', + 'Option modifiers', + 'text', + $pconfig['adv_dhcp_option_modifiers'] +))->sethelp('The values in this field are DHCP option modifiers applied to obtained DHCP lease. [modifier option declaration [, ...]]' . '<br />' . + 'modifiers: (default, supersede, prepend, append)'); + +$section->addInput(new Form_Input( + 'adv_dhcp_config_file_override_path', + 'Option modifiers', + 'text', + $pconfig['adv_dhcp_config_file_override_path'] +))->sethelp('The value in this field is the full absolute path to a DHCP client configuration file. [/[dirname/[.../]]filename[.ext]]' . '<br />' . + 'Value Substitutions in Config File: {interface}, {hostname}, {mac_addr_asciiCD}, {mac_addr_hexCD}' . '<br />' . + 'Where C is U(pper) or L(ower) Case, and D is ":-." Delimiter (space, colon, hyphen, or period) (omitted for none).' . '<br />' . + 'Some ISPs may require certain options be or not be sent.'); + +$form->add($section); + +// DHCP6 client config + +$section = new Form_Section('DHCP6 client configuration'); +$section->addInput(new Form_Checkbox( + 'dhcp6usev4iface', + 'Use IPv4 connectivity as parent interface', + 'Request a IPv6 prefix/information through the IPv4 connectivity link', + $pconfig['dhcp6usev4iface'] +)); + +$section->addInput(new Form_Checkbox( + 'dhcp6prefixonly', + 'Request only an IPv6 prefix', + 'Only request an IPv6 prefix, do not request an IPv6 address', + $pconfig['dhcp6prefixonly'] +)); + +$section->addInput(new Form_Select( + 'dhcp6-ia-pd-len', + 'DHCPv6 Prefix Delegation size', + $pconfig['dhcp6-ia-pd-len'], + array("none" => "None", 16 => "48", 12 => "52", 8 => "56", 4 => "60", 3 => "61", 2 => "62", 1 => "63", 0 => "64") +))->setHelp('The value in this field is the delegated prefix length provided by the DHCPv6 server. Normally specified by the ISP.'); + +$section->addInput(new Form_Checkbox( + 'dhcp6-ia-pd-send-hint', + 'Send IPv6 prefix hint', + 'Send an IPv6 prefix hint to indicate the desired prefix size for delegation', + $pconfig['dhcp6-ia-pd-send-hint'] +)); + +$form->add($section); + +// DHCP6 client config - Advanced + +$section = new Form_Section('Advanced DHCP6 client configuration'); +$section->addInput(new Form_Checkbox( + 'adv_dhcp6_interface_statement_information_only_enable', + 'Information only', + null, + false +)); + +$section->addInput(new Form_Input( + 'adv_dhcp6_interface_statement_send_options', + 'Send options', + 'text', + $pconfig['adv_dhcp6_interface_statement_send_options'] +))->sethelp('DHCP send options to be sent when requesting a DHCP lease. [option declaration [, ...]]' . '<br />' . + 'Value Substitutions: {interface}, {hostname}, {mac_addr_asciiCD}, {mac_addr_hexCD}' . '<br />' . + 'Where C is U(pper) or L(ower) Case, and D is \" :-.\" Delimiter (space, colon, hyphen, or period) (omitted for none).' . '<br />' . + 'Some DHCP services may require certain options be or not be sent.'); + +$section->addInput(new Form_Input( + 'adv_dhcp6_interface_statement_request_options', + 'Request Options', + 'text', + $pconfig['adv_dhcp6_interface_statement_request_options'] +))->sethelp('DHCP request options to be sent when requesting a DHCP lease. [option [, ...]]' . '<br />' . + 'Some DHCP services may require certain options be or not be requested.'); + +$section->addInput(new Form_Input( + 'adv_dhcp6_interface_statement_script', + 'Scripts', + 'text', + $pconfig['adv_dhcp6_interface_statement_request_options'] +))->sethelp('Absolute path to a script invoked on certain conditions including when a reply message is received.' . '<br />' . + '[/[dirname/[.../]]filename[.ext]].'); + +$group = new Form_Group('Identity Association Statement'); + +$group->add(new Form_Checkbox( + 'adv_dhcp6_id_assoc_statement_address_enable', + null, + 'Non-Temporary Address Allocation', + false +)); + +$group->add(new Form_Input( + 'adv_dhcp6_id_assoc_statement_address_id', + null, + 'text', + $pconfig['adv_dhcp6_id_assoc_statement_address_id'] +))->sethelp('id-assoc na ID'); + +$group->add(new Form_IpAddress( + 'adv_dhcp6_id_assoc_statement_address', + null, + $pconfig['adv_dhcp6_id_assoc_statement_address'] +))->sethelp('IPv6 address'); + +$group->add(new Form_Input( + 'adv_dhcp6_id_assoc_statement_address_pltime', + null, + 'text', + $pconfig['adv_dhcp6_id_assoc_statement_address_pltime'] +))->sethelp('pltime'); + +$group->add(new Form_Input( + 'adv_dhcp6_id_assoc_statement_address_vltime', + null, + 'text', + $pconfig['adv_dhcp6_id_assoc_statement_address_vltime'] +))->sethelp('vltime'); + +$section->add($group); + +// Prefix delegation +$group = new Form_Group(''); + +$group->add(new Form_Checkbox( + 'adv_dhcp6_id_assoc_statement_prefix_enable', + null, + 'Prefix Delegation ', + false +)); + +$group->add(new Form_Input( + 'adv_dhcp6_id_assoc_statement_prefix_id', + null, + 'text', + $pconfig['adv_dhcp6_id_assoc_statement_prefix_id'] +))->sethelp('id-assoc pd ID'); + +$group->add(new Form_IpAddress( + 'adv_dhcp6_id_assoc_statement_prefix', + null, + $pconfig['adv_dhcp6_id_assoc_statement_prefix'] +))->sethelp('IPv6 prefix'); + +$group->add(new Form_Input( + 'adv_dhcp6_id_assoc_statement_prefix_pltime', + null, + 'text', + $pconfig['adv_dhcp6_id_assoc_statement_prefix_pltime'] +))->sethelp('pltime'); + +$group->add(new Form_Input( + 'adv_dhcp6_id_assoc_statement_prefix_vltime', + null, + 'text', + $pconfig['adv_dhcp6_id_assoc_statement_prefix_vltime'] +))->sethelp('vltime'); + +$section->add($group); + +$group = new Form_Group('Prefix interface statement'); + +$group->add(new Form_Input( + 'adv_dhcp6_prefix_interface_statement_sla_id', + null, + 'text', + $pconfig['adv_dhcp6_prefix_interface_statement_sla_id'] +))->sethelp('Prefix Interface sla-id'); + +$group->add(new Form_Input( + 'adv_dhcp6_prefix_interface_statement_sla_len', + null, + 'text', + $pconfig['adv_dhcp6_prefix_interface_statement_sla_len'] +))->sethelp('sla-len'); + +$section->add($group); + +$group = new Form_Group('Authentication statement'); + +$group->add(new Form_Input( + 'adv_dhcp6_authentication_statement_authname', + null, + 'text', + $pconfig['adv_dhcp6_authentication_statement_authname'] +))->sethelp('Authname'); + +$group->add(new Form_Input( + 'adv_dhcp6_authentication_statement_protocol', + null, + 'text', + $pconfig['adv_dhcp6_authentication_statement_protocol'] +))->sethelp('Protocol'); + +$group->add(new Form_Input( + 'adv_dhcp6_authentication_statement_algorithm', + null, + 'text', + $pconfig['adv_dhcp6_authentication_statement_algorithm'] +))->sethelp('Algorithm'); + +$group->add(new Form_Input( + 'adv_dhcp6_authentication_statement_rdm', + null, + 'text', + $pconfig['adv_dhcp6_authentication_statement_rdm'] +))->sethelp('RDM'); + +$section->add($group); + +$group = new Form_Group('Keyinfo statement'); + +$group->add(new Form_Input( + 'adv_dhcp6_key_info_statement_keyname', + null, + 'text', + $pconfig['adv_dhcp6_key_info_statement_keyname'] +))->sethelp('Keyname'); + +$group->add(new Form_Input( + 'adv_dhcp6_key_info_statement_realm', + null, + 'text', + $pconfig['adv_dhcp6_key_info_statement_realm'] +))->sethelp('Realm'); + +$section->add($group); + +$group = new Form_Group(''); + +$group->add(new Form_Input( + 'adv_dhcp6_key_info_statement_keyid', + null, + 'text', + $pconfig['adv_dhcp6_key_info_statement_keyid'] +))->sethelp('KeyID'); + +$group->add(new Form_Input( + 'adv_dhcp6_key_info_statement_secret', + null, + 'text', + $pconfig['adv_dhcp6_key_info_statement_secret'] +))->sethelp('Secret'); + +$group->add(new Form_Input( + 'adv_dhcp6_key_info_statement_expire', + null, + 'text', + $pconfig['adv_dhcp6_key_info_statement_expire'] +))->sethelp('Expire'); + +$section->add($group); + +$section->addInput(new Form_Input( + 'adv_dhcp6_config_file_override_path', + 'Configuration File Override', + 'text', + $pconfig['adv_dhcp6_config_file_override_path'] +))->setHelp('The value in this field is the full absolute path to a DHCP client configuration file. [/[dirname/[.../]]filename[.ext]]' . '<br />' . + 'Value Substitutions in Config File: {interface}, {hostname}, {mac_addr_asciiCD}, {mac_addr_hexCD}' . '<br />' . + 'Where C is U(pper) or L(ower) Case, and D is \" :-.\" Delimiter (space, colon, hyphen, or period) (omitted for none).' . '<br />' . + 'Some ISPs may require certain options be or not be sent.'); + +$form->add($section); + +$section = new Form_Section('6RD Configuration'); + +$section->addInput(new Form_Input( + 'prefix-6rd', + '6RD Prefix', + 'text', + $pconfig['prefix-6rd'] +))->sethelp('6RD IPv6 prefix assigned by your ISP. e.g. "2001:db8::/32"'); + +$section->addInput(new Form_Input( + 'gateway-6rd', + '6RD Border relay', + 'text', + $pconfig['prefix-6rd'] +))->sethelp('6RD IPv4 gateway address assigned by your ISP'); + +$section->addInput(new Form_Select( + 'prefix-6rd-v4plen', + 'DHCPv6 Prefix Delegation size', + $pconfig['prefix-6rd-v4plen'], + array_combine(range(0, 32), range(0,32)) +))->setHelp('6RD IPv4 prefix length. Normally specified by the ISP. A value of 0 means we embed the entire IPv4 address in the 6RD prefix..'); + +$form->add($section); + +// Track IPv6 ointerface section +$section = new Form_Section('Track IPv6 Interface'); + +function build_ipv6interface_list() { + $list = array('' => ''); + + $interfaces = get_configured_interface_with_descr(false, true); + $dynv6ifs = array(); + + foreach ($interfaces as $iface => $ifacename) { + switch ($config['interfaces'][$iface]['ipaddrv6']) { + case "6to4": + case "6rd": + case "dhcp6": + $dynv6ifs[$iface] = array( + 'name' => $ifacename, + 'ipv6_num_prefix_ids' => pow(2, calculate_ipv6_delegation_length($iface)) - 1 + ); + break; + default: + continue; + } + } + + foreach ($dynv6ifs as $iface => $ifacedata) { + $list[$iface] = $ifacedata['name']; + + $section->addInput(new Form_Input( + 'ipv6-num-prefix-ids-' . $iface, + null, + 'hidden', + $ifacedata['ipv6_num_prefix_ids'] + )); + } + + return($list); +} + + + +$section->addInput(new Form_Select( + 'prefix-6rd-v4plen', + 'IPv6 Interface', + $pconfig['prefix-6rd-v4plen'], + build_ipv6interface_list() +))->setHelp('selects the dynamic IPv6 WAN interface to track for configuration'); + +if ($pconfig['track6-prefix-id'] == "") { + $pconfig['track6-prefix-id'] = 0; +} + +$section->addInput(new Form_Input( + 'track6-prefix-id--hex' . $iface, + 'IPv6 Prefix ID', + 'text', + sprintf("%x", $pconfig['track6-prefix-id']) +))->setHelp('The value in this field is the (Delegated) IPv6 prefix ID. This determines the configurable network ID based on the dynamic IPv6 connection. The default value is 0.'); + +$form->add($section); + +/// PPP section + +$section = new Form_Section('PPP Configuration'); + +$section->addInput(new Form_Select( + 'country', + 'Country', + $pconfig['country'], + [] +)); + +$section->addInput(new Form_Select( + 'provider_list', + 'Provider', + $pconfig['provider_list'], + [] +)); + +$section->addInput(new Form_Select( + 'providerplan', + 'Plan', + $pconfig['providerplan'], + [] +))->setHelp('Select to fill in data for your service provider.'); + +$section->addInput(new Form_Input( + 'ppp_username', + 'Username', + 'text', + $pconfig['ppp_username'] +)); + +$section->addInput(new Form_Input( + 'ppp_password', + 'Password', + 'password', + $pconfig['ppp_password'] +)); + +if($pconfig['type'] == 'ppp') { + $section->addInput(new Form_Input( + 'phone', + 'Phone number', + 'text', + $pconfig['phone'] + ))->setHelp('Typically *99# for GSM networks and #777 for CDMA networks'); +} + +$section->addInput(new Form_Input( + 'apn', + 'Access Point Name (APN)', + 'text', + $pconfig['apn'] +)); + +function build_port_list() { + $list = array('' => ''); + + $portlist = glob("/dev/cua*"); + $modems = glob("/dev/modem*"); + $portlist = array_merge($portlist, $modems); + + foreach ($portlist as $port) { + if (preg_match("/\.(lock|init)$/", $port)) { + continue; + } + + $list[trim($port)] = $port; + } + + return($list); +} +$section->addInput(new Form_Select( + 'port', + 'Modem port', + $pconfig['port'], + build_port_list() +)); + +if (isset($pconfig['pppid'])) { + $section->addInput(new Form_StaticText( + 'Advanced PPP', + '<a href="/interfaces_ppps_edit.php?id=' . htmlspecialchars($pconfig['pppid']). '" class="navlnk">' . gettext("Click here to edit PPP configuration") . '</a>' + )); +} else { + $section->addInput(new Form_StaticText( + 'Advanced PPP', + '<a href="/interfaces_ppps_edit.php" class="navlnk">' . gettext("Click here to create a PPP configuration") . '</a>' + )); +} + +$form->add($section); + +// PPPoE configuration +$section = new Form_Section('PPPoE Configuration'); + +$section->addInput(new Form_Input( + 'pppoe_username', + 'Username', + 'text', + $pconfig['pppoe_username'] +)); + +$section->addInput(new Form_Input( + 'pppoe_password', + 'Password', + 'password', + $pconfig['pppoe_password'] +)); + +$section->addInput(new Form_Input( + 'provider', + 'Service name', + 'text', + $pconfig['provider'] +))->setHelp('This field can usually be left empty'); + +$section->addInput(new Form_Checkbox( + 'pppoe_dialondemand', + 'Dial on demand', + 'Enable Dial-On-Demand mode ', + $pconfig['pppoe_dialondemand'], + 'enable' +)); + +$section->addInput(new Form_Input( + 'pppoe_idletimeout', + 'Idle timeout', + 'number', + $pconfig['pppoe_idletimeout'], + [min => 0] +))->setHelp('If no qualifying outgoing packets are transmitted for the specified number of seconds, the connection is brought down. ' . + 'An idle timeout of zero disables this feature.'); + +$section->addInput(new Form_Select( + 'pppoe-reset-type', + 'Periodic reset', + $pconfig['pppoe-reset-type'], + ['' => 'Disabled', 'custom' => 'Custom', 'preset' => 'Pre-set'] +))->setHelp('Select a reset timing type'); + +$group = new Form_Group('Custom reest'); + +$group->add(new Form_Input( + 'pppoe_resethour', + null, + 'number', + $pconfig['pppoe_resethour'], + [min => 0, max => 23] +))->setHelp('Hour (0-23)'); + +$group->add(new Form_Input( + 'pppoe_resetminute', + null, + 'number', + $pconfig['pppoe_resetminute'], + [min => 0, max => 59] +))->setHelp('Minutes (0-59)'); + +// ToDo: Need a date-picker here +$group->add(new Form_Input( + 'pppoe_resetdate', + null, + 'text', + $pconfig['pppoe_resetdate'] +))->setHelp('Specific date (mm/dd/yyyy)'); + +$group->setHelp('If you leave the date field empty, the reset will be executed each day at the time you specified using the minutes and hour field'); + +$section->add($group); + +$group = new Form_MultiCheckboxGroup('cron based reset'); + +$group->add(new Form_MultiCheckbox( + 'pppoe_pr_preset_val', + null, + 'Reset at each month ("0 0 1 * *")', + $pconfig['pppoe_monthly'], + 'monthly' +))->displayAsRadio(); + +$group->add(new Form_MultiCheckbox( + 'pppoe_pr_preset_val', + null, + 'Reset at each month ("0 0 * * 0")', + $pconfig['pppoe_weekly'], + 'weekly' +))->displayAsRadio(); + +$group->add(new Form_MultiCheckbox( + 'pppoe_pr_preset_val', + null, + 'Reset at each month ("0 0 * * *")', + $pconfig['pppoe_daily'], + 'daily' +))->displayAsRadio(); + +$group->add(new Form_MultiCheckbox( + 'pppoe_pr_preset_val', + null, + 'Reset at each month ("0 * * * *")', + $pconfig['pppoe_hourly'], + 'hourly' +))->displayAsRadio(); + +$section->add($group); + +if (isset($pconfig['pppid'])) { + $section->addInput(new Form_StaticText( + 'Advanced and MLPPP', + '<a href="/interfaces_ppps_edit.php?id=' . htmlspecialchars($pconfig['pppid']) . '" class="navlnk">Click here for additional PPPoE configuration options. Save first if you made changes.</a>' + )); +} else { + $section->addInput(new Form_StaticText( + 'Advanced and MLPPP', + '<a href="/interfaces_ppps_edit.php" class="navlnk">Click here for additional PPPoE configuration options and for MLPPP configuration.</a>' + )); +} + +$form->add($section); + +// PPTP & L2TP Configuration section +$section = new Form_Section('PPTP/L2TP Configuraion'); + +$section->addInput(new Form_Input( + 'pptp_username', + 'Username', + 'text', + $pconfig['pptp_username'] +)); + +$section->addInput(new Form_Input( + 'pptp_password', + 'Password', + 'password', + $pconfig['pptp_password'] +)); + +$section->addInput(new Form_IpAddress( + 'pptp_local', + 'Local IP address', + $pconfig['pptp_local'] +))->addMask('pptp_subnet', $pconfig['pptp_subnet'][0]); + +$section->addInput(new Form_IpAddress( + 'pptp_remote', + 'Remote IP address', + $pconfig['pptp_remote'] +)); + +$section->addInput(new Form_Checkbox( + 'pptp_dialondemand', + 'Dial on demand', + 'Enable Dial-On-Demand mode ', + $pconfig['pptp_dialondemand'], + 'enable' +))->setHelp('This option causes the interface to operate in dial-on-demand mode, allowing you to have a virtual full time connection. ' . + 'The interface is configured, but the actual connection of the link is delayed until qualifying outgoing traffic is detected.'); + +$section->addInput(new Form_Input( + 'pptp_idletimeout', + 'Idle timeout (seconds)', + 'number', + $pconfig['pptp_idletimeout'], + [min => 0] +))->setHelp('If no qualifying outgoing packets are transmitted for the specified number of seconds, the connection is brought down. ' . + 'An idle timeout of zero disables this feature.'); + +if (isset($pconfig['pppid'])) { + $section->addInput(new Form_StaticText( + 'Advanced and MLPPP', + '<a href="/interfaces_ppps_edit.php?id=' . htmlspecialchars($pconfig['pppid']) . '" class="navlnk">Click here for additional PPTP and L2TP configuration options. Save first if you made changes.</a>' + )); +} else { + $section->addInput(new Form_StaticText( + 'Advanced and MLPPP', + '<a href="/interfaces_ppps_edit.php" class="navlnk">Click here for additional PPTP and L2TP configuration options.</a>' + )); +} + +$form->add($section); + +// Wireless interface +if (true || isset($wancfg['wireless'])) { // DEBUG + +$section = new Form_Section('Common wireless configuration - Settings apply to all wireless networks on ' . $wlanbaseif . '.'); + +$form->add($section); +} + +print($form); +?> + <form action="interfaces.php" method="post" name="iform" id="iform"> + <table width="100%" border="0" cellpadding="6" cellspacing="0" summary="tabs"> + + <?php + /* Wireless interface? */ + if (isset($wancfg['wireless'])): + ?> + <tr> + <td colspan="2" valign="top" class="listtopic"><?=gettext("Common wireless configuration - Settings apply to all wireless networks on"); ?><?=$wlanbaseif?>.</td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("Persist common settings")?></td> + <td class="vtable"> + <input name="persistcommonwireless" type="checkbox" value="yes" class="formfld" id="persistcommonwireless" <?php if ($pconfig['persistcommonwireless']) echo "checked=\"checked\""?> /> + <br /><?=gettext("Enabling this preserves the common wireless configuration through interface deletions and reassignments.")?> + </td> + </tr> + <tr> + <td valign="top" class="vncellreq"><?=gettext("Standard"); ?></td> + <td class="vtable"> + <select name="standard" class="formselect" id="standard"> + <?php + $rowIndex = 0; + echo "<option "; + if ($pconfig['standard'] == "auto") { + echo "selected=\"selected\" "; + } + echo "value=\"auto\">auto</option>\n"; + foreach ($wl_modes as $wl_standard => $wl_channels) { + $rowIndex++; + echo "<option "; + if ($pconfig['standard'] == "$wl_standard") { + echo "selected=\"selected\" "; + } + if ($pconfig['standard'] == "") { + if ($wl_standard == "11ng") { + echo "selected=\"selected\" "; + } + } + echo "value=\"$wl_standard\">802.$wl_standard</option>\n"; + } + if ($rowIndex == 0) { + echo "<option></option>"; + } + ?> + </select> + </td> + </tr> + <?php if (isset($wl_modes['11g'])): ?> + <tr> + <td valign="top" class="vncellreq">802.11g OFDM <?=gettext("Protection Mode"); ?></td> + <td class="vtable"> + <select name="protmode" class="formselect" id="protmode"> + <option <?php if ($pconfig['protmode'] == 'off') echo "selected=\"selected\""?> value="off"><?=gettext("Protection mode off"); ?></option> + <option <?php if ($pconfig['protmode'] == 'cts') echo "selected=\"selected\""?> value="cts"><?=gettext("Protection mode CTS to self"); ?></option> + <option <?php if ($pconfig['protmode'] == 'rtscts') echo "selected=\"selected\""?> value="rtscts"><?=gettext("Protection mode RTS and CTS"); ?></option> + </select> + <br /> + <?=gettext("For IEEE 802.11g, use the specified technique for protecting OFDM frames in a mixed 11b/11g network."); ?> + <br /> + </td> + </tr> + <?php else: ?> + <input name="protmode" type="hidden" id="protmode" value="off" /> + <?php endif; ?> + <?php /* txpower is disabled because of issues with it. + <tr> + <td valign="top" class="vncellreq"><?=gettext("Transmit power"); ?></td> + <td class="vtable"> + <select name="txpower" class="formselect" id="txpower"> + <? + for ($x = 99; $x > 0; $x--) { + if ($pconfig["txpower"] == $x) { + $SELECTED = " selected=\"selected\""; + } else { + $SELECTED = ""; + } + echo "<option {$SELECTED}>{$x}</option>\n"; + } + ?> + </select><br /> + <?=gettext("Note: Typically only a few discreet power settings are available and the driver will use the setting closest to the specified value. Not all adapters support changing the transmit power setting."); ?> + </td> + </tr>*/ + ?> + <tr> + <td valign="top" class="vncellreq"><?=gettext("Channel"); ?></td> + <td class="vtable"> + <select name="channel" class="formselect" id="channel"> + <option <?php if ($pconfig['channel'] == 0) echo "selected=\"selected\""; ?> value="0"><?=gettext("Auto"); ?></option> + <?php + foreach ($wl_modes as $wl_standard => $wl_channels) { + if ($wl_standard == "11g") { + $wl_standard = "11b/g"; + } else if ($wl_standard == "11ng") { + $wl_standard = "11b/g/n"; + } else if ($wl_standard == "11na") { + $wl_standard = "11a/n"; + } + foreach ($wl_channels as $wl_channel) { + echo "<option "; + if ($pconfig['channel'] == "$wl_channel") { + echo "selected=\"selected\" "; + } + echo "value=\"$wl_channel\">$wl_standard - $wl_channel"; + if (isset($wl_chaninfo[$wl_channel])) { + echo " ({$wl_chaninfo[$wl_channel][1]} @ {$wl_chaninfo[$wl_channel][2]} / {$wl_chaninfo[$wl_channel][3]})"; + } + echo "</option>\n"; + } + } + ?> + </select> + <br /> + <?=gettext("Legend: wireless standards - channel # (frequency @ max TX power / TX power allowed in reg. domain)"); ?> + <br /> + <?=gettext("Note: Not all channels may be supported by your card. Auto may override the wireless standard selected above."); ?> + </td> + </tr> + <?php if (isset($wl_sysctl["{$wl_sysctl_prefix}.diversity"]) || isset($wl_sysctl["{$wl_sysctl_prefix}.txantenna"]) || isset($wl_sysctl["{$wl_sysctl_prefix}.rxantenna"])): ?> + <tr> + <td valign="top" class="vncell"><?=gettext("Antenna settings"); ?></td> + <td class="vtable"> + <table border="0" cellpadding="0" cellspacing="0" summary="antenna settings"> + <tr> + <?php if (isset($wl_sysctl["{$wl_sysctl_prefix}.diversity"])): ?> + <td> + <?=gettext("Diversity"); ?><br /> + <select name="diversity" class="formselect" id="diversity"> + <option <?php if (!isset($pconfig['diversity'])) echo "selected=\"selected\""; ?> value=""><?=gettext("Default"); ?></option> + <option <?php if ($pconfig['diversity'] === '0') echo "selected=\"selected\""; ?> value="0"><?=gettext("Off"); ?></option> + <option <?php if ($pconfig['diversity'] === '1') echo "selected=\"selected\""; ?> value="1"><?=gettext("On"); ?></option> + </select> + </td> + <td> </td> + <?php endif; ?> + <?php if (isset($wl_sysctl["{$wl_sysctl_prefix}.txantenna"])): ?> + <td> + <?=gettext("Transmit antenna"); ?><br /> + <select name="txantenna" class="formselect" id="txantenna"> + <option <?php if (!isset($pconfig['txantenna'])) echo "selected=\"selected\""; ?> value=""><?=gettext("Default"); ?></option> + <option <?php if ($pconfig['txantenna'] === '0') echo "selected=\"selected\""; ?> value="0"><?=gettext("Auto"); ?></option> + <option <?php if ($pconfig['txantenna'] === '1') echo "selected=\"selected\""; ?> value="1"><?=gettext("#1"); ?></option> + <option <?php if ($pconfig['txantenna'] === '2') echo "selected=\"selected\""; ?> value="2"><?=gettext("#2"); ?></option> + </select> + </td> + <td> </td> + <?php endif; ?> + <?php if (isset($wl_sysctl["{$wl_sysctl_prefix}.rxantenna"])): ?> + <td> + <?=gettext("Receive antenna"); ?><br /> + <select name="rxantenna" class="formselect" id="rxantenna"> + <option <?php if (!isset($pconfig['rxantenna'])) echo "selected=\"selected\""; ?> value=""><?=gettext("Default"); ?></option> + <option <?php if ($pconfig['rxantenna'] === '0') echo "selected=\"selected\""; ?> value="0"><?=gettext("Auto"); ?></option> + <option <?php if ($pconfig['rxantenna'] === '1') echo "selected=\"selected\""; ?> value="1"><?=gettext("#1"); ?></option> + <option <?php if ($pconfig['rxantenna'] === '2') echo "selected=\"selected\""; ?> value="2"><?=gettext("#2"); ?></option> + </select> + </td> + <?php endif; ?> + </tr> + </table> + <br /> + <?=gettext("Note: The antenna numbers do not always match up with the labels on the card."); ?> + </td> + </tr> + <?php endif; ?> + <?php if (isset($wl_sysctl["{$wl_sysctl_prefix}.slottime"]) && isset($wl_sysctl["{$wl_sysctl_prefix}.acktimeout"]) && isset($wl_sysctl["{$wl_sysctl_prefix}.ctstimeout"])): ?> + <tr> + <td valign="top" class="vncell"><?=gettext("Distance setting"); ?></td> + <td class="vtable"> + <input name="distance" type="text" class="formfld unknown" id="distance" size="5" value="<?=htmlspecialchars($pconfig['distance'])?>" /> + <br /> + <?=gettext("Note: This field can be used to tune ACK/CTS timers to fit the distance between AP and Client"); ?><br /> + <?=gettext("(measured in meters)"); ?> + </td> + </tr> + <?php endif; ?> + <tr> + <td valign="top" class="vncell"><?=gettext("Regulatory settings"); ?></td> + <td class="vtable"> + <?=gettext("Regulatory domain"); ?><br /> + <select name="regdomain" class="formselect" id="regdomain"> + <option <?php if (empty($pconfig['regdomain'])) echo "selected=\"selected\""; ?> value=""><?=gettext("Default"); ?></option> + <?php + foreach ($wl_regdomains as $wl_regdomain_key => $wl_regdomain) { + echo "<option "; + if ($pconfig['regdomain'] == $wl_regdomains_attr[$wl_regdomain_key]['ID']) { + echo "selected=\"selected\" "; + } + echo "value=\"{$wl_regdomains_attr[$wl_regdomain_key]['ID']}\">{$wl_regdomain['name']}</option>\n"; + } + ?> + </select> + <br /> + <?=gettext("Note: Some cards have a default that is not recognized and require changing the regulatory domain to one in this list for the changes to other regulatory settings to work."); ?> + <br /><br /> + <?=gettext("Country (listed with country code and regulatory domain)"); ?><br /> + <select name="regcountry" class="formselect" id="regcountry"> + <option <?php if (empty($pconfig['regcountry'])) echo "selected=\"selected\""; ?> value=""><?=gettext("Default"); ?></option> + <?php + foreach ($wl_countries as $wl_country_key => $wl_country) { + echo "<option "; + if ($pconfig['regcountry'] == $wl_countries_attr[$wl_country_key]['ID']) { + echo "selected=\"selected\" "; + } + echo "value=\"{$wl_countries_attr[$wl_country_key]['ID']}\">{$wl_country['name']} -- ({$wl_countries_attr[$wl_country_key]['ID']}, " . strtoupper($wl_countries_attr[$wl_country_key]['rd'][0]['REF']) . ")</option>\n"; + } + ?> + </select> + <br /> + <?=gettext("Note: Any country setting other than \"Default\" will override the regulatory domain setting"); ?>. + <br /><br /> + <?=gettext("Location"); ?><br /> + <select name="reglocation" class="formselect" id="reglocation"> + <option <?php if (empty($pconfig['reglocation'])) echo "selected=\"selected\""; ?> value=""><?=gettext("Default"); ?></option> + <option <?php if ($pconfig['reglocation'] == 'indoor') echo "selected=\"selected\""; ?> value="indoor"><?=gettext("Indoor"); ?></option> + <option <?php if ($pconfig['reglocation'] == 'outdoor') echo "selected=\"selected\""; ?> value="outdoor"><?=gettext("Outdoor"); ?></option> + <option <?php if ($pconfig['reglocation'] == 'anywhere') echo "selected=\"selected\""; ?> value="anywhere"><?=gettext("Anywhere"); ?></option> + </select> + <br /><br /> + <?=gettext("These settings may affect which channels are available and the maximum transmit power allowed on those channels. Using the correct settings to comply with local regulatory requirements is recommended."); ?> + <br /> + <?=gettext("Note: All wireless networks on this interface will be temporarily brought down when changing regulatory settings. Some of the regulatory domains or country codes may not be allowed by some cards. These settings may not be able to add additional channels that are not already supported."); ?> + </td> + </tr> + <tr> + <td colspan="2" valign="top" height="16"></td> + </tr> + <tr> + <td colspan="2" valign="top" class="listtopic"><?=gettext("Network-specific wireless configuration")?></td> + </tr> + <tr> + <td valign="top" class="vncellreq"><?=gettext("Mode"); ?></td> + <td class="vtable"> + <select name="mode" class="formselect" id="mode"> + <option <?php if ($pconfig['mode'] == 'bss') echo "selected=\"selected\""?> value="bss"><?=gettext("Infrastructure (BSS)"); ?></option> + <option <?php if ($pconfig['mode'] == 'adhoc') echo "selected=\"selected\""?> value="adhoc"><?=gettext("Ad-hoc (IBSS)"); ?></option> + <option <?php if ($pconfig['mode'] == 'hostap') echo "selected=\"selected\""?> value="hostap"><?=gettext("Access Point"); ?></option> + </select> + </td> + </tr> + <tr> + <td valign="top" class="vncellreq"><?=gettext("SSID"); ?></td> + <td class="vtable"> + <input name="ssid" type="text" class="formfld unknown" id="ssid" size="20" value="<?=htmlspecialchars($pconfig['ssid']); ?>" /> + <br /> + <?=gettext("Note: Only required in Access Point mode. If left blank in Ad-hoc or Infrastructure mode, this interface will connect to any available SSID"); ?> + </td> + </tr> + <?php if (isset($wl_modes['11ng']) || isset($wl_modes['11na'])): ?> + <tr> + <td valign="top" class="vncell"><?=gettext("Minimum wireless standard"); ?></td> + <td class="vtable"> + <select name="puremode" class="formselect" id="puremode"> + <option <?php if ($pconfig['puremode'] == 'any') echo "selected=\"selected\""?> value="any"><?=gettext("Any"); ?></option> + <?php if (isset($wl_modes['11g'])): ?> + <option <?php if ($pconfig['puremode'] == '11g') echo "selected=\"selected\""?> value="11g"><?=gettext("802.11g"); ?></option> + <?php endif; ?> + <option <?php if ($pconfig['puremode'] == '11n') echo "selected=\"selected\""?> value="11n"><?=gettext("802.11n"); ?></option> + </select> + <br /> + <?=gettext("When operating as an access point, allow only stations capable of the selected wireless standard to associate (stations not capable are not permitted to associate)."); ?> + </td> + </tr> + <?php elseif (isset($wl_modes['11g'])): ?> + <tr> + <td valign="top" class="vncell"><?=gettext("802.11g only"); ?></td> + <td class="vtable"> + <input name="puremode" type="checkbox" value="11g" class="formfld" id="puremode" <?php if ($pconfig['puremode'] == '11g') echo "checked=\"checked\""?> /> + <br /><?=gettext("When operating as an access point in 802.11g mode, allow only 11g-capable stations to associate (11b-only stations are not permitted to associate)."); ?> + </td> + </tr> + <?php endif; ?> + <tr> + <td valign="top" class="vncell"><?=gettext("Allow intra-BSS communication"); ?></td> + <td class="vtable"> + <input name="apbridge_enable" type="checkbox" value="yes" class="formfld" id="apbridge_enable" <?php if ($pconfig['apbridge_enable']) echo "checked=\"checked\""?> /> + <br /> + <?=gettext("When operating as an access point, enable this if you want to pass packets between wireless clients directly."); ?> + <br /> + <?=gettext("Disabling the internal bridging is useful when traffic is to be processed with packet filtering."); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("Enable WME"); ?></td> + <td class="vtable"> + <input name="wme_enable" type="checkbox" class="formfld" id="wme_enable" value="yes" <?php if ($pconfig['wme_enable']) echo "checked=\"checked\""?> /> + <br /><?=gettext("Setting this option will force the card to use WME (wireless QoS)."); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("Enable Hide SSID"); ?></td> + <td class="vtable"> + <input name="hidessid_enable" type="checkbox" class="formfld" id="hidessid_enable" value="yes" <?php if ($pconfig['hidessid_enable']) echo "checked=\"checked\""?> /> + <br /> + <?=gettext("Setting this option will force the card to NOT broadcast its SSID"); ?> + <br /> + <?=gettext("(this might create problems for some clients)."); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("WEP"); ?></td> + <td class="vtable"> + <input name="wep_enable" type="checkbox" id="wep_enable" value="yes" <?php if ($pconfig['wep_enable']) echo "checked=\"checked\""; ?> /> + <strong><?=gettext("Enable WEP"); ?></strong> + <table border="0" cellspacing="0" cellpadding="0" summary="wep"> + <tr> + <td> </td> + <td> </td> + <td> <?=gettext("TX key"); ?> </td> + </tr> + <tr> + <td><?=gettext("Key 1:"); ?> </td> + <td> + <input name="key1" type="text" class="formfld unknown" id="key1" size="30" value="<?=htmlspecialchars($pconfig['key1'])?>" /> + </td> + <td align="center"> + <input name="txkey" type="radio" value="1" <?php if ($pconfig['txkey'] == 1) echo "checked=\"checked\""?> /> + </td> + </tr> + <tr> + <td><?=gettext("Key 2:"); ?> </td> + <td> + <input name="key2" type="text" class="formfld unknown" id="key2" size="30" value="<?=htmlspecialchars($pconfig['key2'])?>" /> + </td> + <td align="center"> + <input name="txkey" type="radio" value="2" <?php if ($pconfig['txkey'] == 2) echo "checked=\"checked\""?> /> + </td> + </tr> + <tr> + <td><?=gettext("Key 3:"); ?> </td> + <td> + <input name="key3" type="text" class="formfld unknown" id="key3" size="30" value="<?=htmlspecialchars($pconfig['key3'])?>" /> + </td> + <td align="center"> + <input name="txkey" type="radio" value="3" <?php if ($pconfig['txkey'] == 3) echo "checked=\"checked\""?> /> + </td> + </tr> + <tr> + <td><?=gettext("Key 4:"); ?> </td> + <td> + <input name="key4" type="text" class="formfld unknown" id="key4" size="30" value="<?=htmlspecialchars($pconfig['key4'])?>" /> + </td> + <td align="center"> + <input name="txkey" type="radio" value="4" <?php if ($pconfig['txkey'] == 4) echo "checked=\"checked\""?> /> + </td> + </tr> + </table> + <br /> + <?=gettext("40 (64) bit keys may be entered as 5 ASCII characters or 10 hex digits preceded by '0x'."); ?><br /> + <?=gettext("104 (128) bit keys may be entered as 13 ASCII characters or 26 hex digits preceded by '0x'."); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("WPA"); ?></td> + <td class="vtable"> + <input name="wpa_enable" type="checkbox" class="formfld" id="wpa_enable" value="yes" <?php if ($pconfig['wpa_enable']) echo "checked=\"checked\""; ?> /> + <strong><?=gettext("Enable WPA"); ?></strong> + <br /><br /> + <table border="0" cellspacing="0" cellpadding="0" summary="wpa"> + <tr> + <td> </td> + <td> <?=gettext("WPA Pre-Shared Key"); ?> </td> + </tr> + <tr> + <td><?=gettext("PSK:"); ?> </td> + <td> + <input name="passphrase" type="text" class="formfld unknown" id="passphrase" size="66" value="<?=htmlspecialchars($pconfig['passphrase'])?>" /> + </td> + </tr> + </table> + <br /><?=gettext("WPA Passphrase must be between 8 and 63 characters long."); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("WPA Mode"); ?></td> + <td class="vtable"> + <select name="wpa_mode" class="formselect" id="wpa_mode"> + <option <?php if ($pconfig['wpa_mode'] == '1') echo "selected=\"selected\""?> value="1"><?=gettext("WPA"); ?></option> + <option <?php if ($pconfig['wpa_mode'] == '2' || !isset($pconfig['wpa_mode'])) echo "selected=\"selected\""?> value="2"><?=gettext("WPA2"); ?></option> + <option <?php if ($pconfig['wpa_mode'] == '3') echo "selected=\"selected\""?> value="3"><?=gettext("Both"); ?></option> + </select> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("WPA Key Management Mode"); ?></td> + <td class="vtable"> + <select name="wpa_key_mgmt" class="formselect" id="wpa_key_mgmt"> + <option <?php if ($pconfig['wpa_key_mgmt'] == 'WPA-PSK') echo "selected=\"selected\""?> value="WPA-PSK"><?=gettext("Pre-Shared Key"); ?></option> + <option <?php if ($pconfig['wpa_key_mgmt'] == 'WPA-EAP') echo "selected=\"selected\""?> value="WPA-EAP"><?=gettext("Extensible Authentication Protocol"); ?></option> + <option <?php if ($pconfig['wpa_key_mgmt'] == 'WPA-PSK WPA-EAP') echo "selected=\"selected\""?> value="WPA-PSK WPA-EAP"><?=gettext("Both"); ?></option> + </select> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("Authentication"); ?></td> + <td class="vtable"> + <select name="auth_algs" class="formselect" id="auth_algs"> + <option <?php if ($pconfig['auth_algs'] == '1') echo "selected=\"selected\""?> value="1"><?=gettext("Open System Authentication"); ?></option> + <option <?php if ($pconfig['auth_algs'] == '2') echo "selected=\"selected\""?> value="2"><?=gettext("Shared Key Authentication"); ?></option> + <option <?php if ($pconfig['auth_algs'] == '3') echo "selected=\"selected\""?> value="3"><?=gettext("Both"); ?></option> + </select> + <br /><?=gettext("Note: Shared Key Authentication requires WEP."); ?><br /> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("WPA Pairwise"); ?></td> + <td class="vtable"> + <select name="wpa_pairwise" class="formselect" id="wpa_pairwise"> + <option <?php if ($pconfig['wpa_pairwise'] == 'CCMP TKIP') echo "selected=\"selected\""?> value="CCMP TKIP"><?=gettext("Both"); ?></option> + <option <?php if ($pconfig['wpa_pairwise'] == 'CCMP' || !isset($pconfig['wpa_pairwise'])) echo "selected=\"selected\""?> value="CCMP"><?=gettext("AES (recommended)"); ?></option> + <option <?php if ($pconfig['wpa_pairwise'] == 'TKIP') echo "selected=\"selected\""?> value="TKIP"><?=gettext("TKIP"); ?></option> + </select> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("Key Rotation"); ?></td> + <td class="vtable"> + <input name="wpa_group_rekey" type="text" class="formfld unknown" id="wpa_group_rekey" size="30" value="<?=htmlspecialchars($pconfig['wpa_group_rekey']) ? $pconfig['wpa_group_rekey'] : "60"?>" /> + <br /><?=gettext("Specified in seconds. Allowed values are 1-9999. Must be shorter than Master Key Regeneration time."); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("Master Key Regeneration"); ?></td> + <td class="vtable"> + <input name="wpa_gmk_rekey" type="text" class="formfld" id="wpa_gmk_rekey" size="30" value="<?=htmlspecialchars($pconfig['wpa_gmk_rekey']) ? $pconfig['wpa_gmk_rekey'] : "3600"?>" /> + <br /><?=gettext("Specified in seconds. Allowed values are 1-9999. Must be longer than Key Rotation time."); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("Strict Key Regeneration"); ?></td> + <td class="vtable"> + <input name="wpa_strict_rekey" type="checkbox" value="yes" class="formfld" id="wpa_strict_rekey" <?php if ($pconfig['wpa_strict_rekey']) echo "checked=\"checked\""; ?> /> + <br /><?=gettext("Setting this option will force the AP to rekey whenever a client disassociates."); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("Enable IEEE802.1X Authentication"); ?></td> + <td class="vtable"> + <input name="ieee8021x" type="checkbox" value="yes" class="formfld" id="ieee8021x" <?php if ($pconfig['ieee8021x']) echo "checked=\"checked\""?> /> + <br /><?=gettext("Setting this option will enable 802.1X authentication."); ?> + <br /><span class="red"><strong><?=gettext("NOTE"); ?>:</strong></span><?=gettext("this option requires checking the \"Enable WPA box\"."); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("802.1X Authentication Server IP Address"); ?></td> + <td class="vtable"> + <input name="auth_server_addr" id="auth_server_addr" type="text" class="formfld unknown" size="66" value="<?=htmlspecialchars($pconfig['auth_server_addr'])?>" /> + <br /><?=gettext("Enter the IP address of the 802.1X Authentication Server. This is commonly a Radius server (FreeRadius, Internet Authentication Services, etc.)"); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("802.1X Authentication Server Port"); ?></td> + <td class="vtable"> + <input name="auth_server_port" id="auth_server_port" type="text" class="formfld unknown" size="66" value="<?=htmlspecialchars($pconfig['auth_server_port'])?>" /> + <br /><?=gettext("Leave blank for the default port 1812."); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("802.1X Authentication Server Shared Secret"); ?></td> + <td class="vtable"> + <input name="auth_server_shared_secret" id="auth_server_shared_secret" type="text" class="formfld unknown" size="66" value="<?=htmlspecialchars($pconfig['auth_server_shared_secret'])?>" /> + <br /> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("Secondary 802.1X Authentication Server IP Address"); ?></td> + <td class="vtable"> + <input name="auth_server_addr2" id="auth_server_addr2" type="text" class="formfld unknown" size="66" value="<?=htmlspecialchars($pconfig['auth_server_addr2'])?>" /> + <br /><?=gettext("Enter the IP address of the 802.1X Authentication Server. This is commonly a Radius server (FreeRadius, Internet Authentication Services, etc.)"); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("Secondary 802.1X Authentication Server Port"); ?></td> + <td class="vtable"> + <input name="auth_server_port2" id="auth_server_port2" type="text" class="formfld unknown" size="66" value="<?=htmlspecialchars($pconfig['auth_server_port2'])?>" /> + <br /><?=gettext("Leave blank for the default port 1812."); ?> + </td> + </tr> + <tr> + <td valign="top" class="vncell"><?=gettext("Secondary 802.1X Authentication Server Shared Secret"); ?></td> + <td class="vtable"> + <input name="auth_server_shared_secret2" id="auth_server_shared_secret2" type="text" class="formfld unknown" size="66" value="<?=htmlspecialchars($pconfig['auth_server_shared_secret2'])?>" /> + <br /> + </td> + </tr> + <tr> + <td valign="top" class="vncell">802.1X <?=gettext("Authentication Roaming Preauth"); ?></td> + <td class="vtable"> + <input name="rsn_preauth" id="rsn_preauth" type="checkbox" class="formfld unknown" size="66" value="yes" <?php if ($pconfig['rsn_preauth']) echo "checked=\"checked\""; ?> /> + <br /> + </td> + </tr> + <tr> + <td colspan="2" valign="top" height="16"></td> + </tr> + <?php endif; ?> + <tr> + <td colspan="2" valign="top" class="listtopic"><?=gettext("Private networks"); ?></td> + </tr> + <tr> + <td valign="middle" class="vncell"> </td> + <td class="vtable"> + <a name="rfc1918"></a> + <input name="blockpriv" type="checkbox" id="blockpriv" value="yes" <?php if ($pconfig['blockpriv']) echo "checked=\"checked\""; ?> /> + <strong><?=gettext("Block private networks"); ?></strong><br /> + <?=gettext("When set, this option blocks traffic from IP addresses that are reserved " . + "for private networks as per RFC 1918 (10/8, 172.16/12, 192.168/16) as"); ?> + <?=gettext("well as loopback addresses (127/8)."); ?> <?=gettext("You should generally " . + "leave this option turned on, unless your WAN network lies in such " . + "a private address space, too."); ?> + </td> + </tr> + <tr> + <td valign="middle" class="vncell"> </td> + <td class="vtable"> + <input name="blockbogons" type="checkbox" id="blockbogons" value="yes" <?php if ($pconfig['blockbogons']) echo "checked=\"checked\""; ?> /> + <strong><?=gettext("Block bogon networks"); ?></strong><br /> + <?=gettext("When set, this option blocks traffic from IP addresses that are reserved " . + "(but not RFC 1918) or not yet assigned by IANA."); ?> + <?=gettext("Bogons are prefixes that should never appear in the Internet routing table, " . + "and obviously should not appear as the source address in any packets you receive."); ?> + <br /><br /> + <?=gettext("Note: The update frequency can be changed under System->Advanced Firewall/NAT settings.")?> + </td> + </tr> + </table><!-- End "allcfg" table --> + </div><!-- End "allcfg" div --> + + <table width="100%" border="0" cellpadding="6" cellspacing="0" summary="buttons"> + <tr> + <td width="22%" valign="top"> + + </td> + <td width="78%"> + <br /> + <input id="save" name="Submit" type="submit" class="formbtn" value="<?=gettext("Save"); ?>" /> + <input id="cancel" type="button" class="formbtn" value="<?=gettext("Cancel")?>" onclick="window.location.href='<?=$referer?>'" /> + <input name="referer" type="hidden" value="<?=$referer?>" /> + <input name="if" type="hidden" id="if" value="<?=htmlspecialchars($if)?>" /> + <?php if ($wancfg['if'] == $a_ppps[$pppid]['if']) : ?> + <input name="ppp_port" type="hidden" value="<?=htmlspecialchars($pconfig['port'])?>" /> + <?php endif; ?> + <input name="ptpid" type="hidden" value="<?=htmlspecialchars($pconfig['ptpid'])?>" /> + </td> + </tr> + </table> + + </form> + +<script type="text/javascript"> + //<![CDATA[ +events.push(function(){ + +/* + function updateType(t) { + switch (t) { + case "none": { + jQuery('#staticv4, #dhcp, #pppoe, #pptp, #ppp').hide(); + break; + } + case "staticv4": { + jQuery('#none, #dhcp, #pppoe, #pptp, #ppp').hide(); + break; + } + case "dhcp": { + jQuery('#none, #staticv4, #pppoe, #pptp, #ppp').hide(); + break; + } + case "ppp": { + jQuery('#none, #staticv4, #dhcp, #pptp, #pppoe').hide(); + country_list(); + break; + } + case "pppoe": { + jQuery('#none, #staticv4, #dhcp, #pptp, #ppp').hide(); + break; + } + case "l2tp": + case "pptp": { + jQuery('#none, #staticv4, #dhcp, #pppoe, #ppp').hide(); + jQuery('#pptp').show(); + break; + } + } + if (t != "l2tp" && t != "pptp") { + jQuery('#'+t).show(); + } + } + + function updateTypeSix(t) { + if (!isNaN(t[0])) t = '_' + t; + switch (t) { + case "none": { + jQuery('#staticv6, #dhcp6, #_6rd, #_6to4, #track6, #slaac').hide(); + break; + } + case "staticv6": { + jQuery('#none, #dhcp6, #_6rd, #_6to4, #track6, #slaac').hide(); + break; + } + case "slaac": { + jQuery('#none, #staticv6, #_6rd, #_6to4, #track6, #dhcp6').hide(); + break; + } + case "dhcp6": { + jQuery('#none, #staticv6, #_6rd, #_6to4, #track6, #slaac').hide(); + break; + } + case "_6rd": { + jQuery('#none, #dhcp6, #staticv6, #_6to4, #track6, #slaac').hide(); + break; + } + case "_6to4": { + jQuery('#none, #dhcp6, #staticv6, #_6rd, #track6, #slaac').hide(); + break; + } + case "track6": { + jQuery('#none, #dhcp6, #staticv6, #_6rd, #_6to4, #slaac').hide(); + update_track6_prefix(); + break; + } + } + if (t != "l2tp" && t != "pptp") { + jQuery('#'+t).show(); + } + } + + function show_allcfg(obj) { + if (obj.checked) { + jQuery('#allcfg').show(); + } else { + jQuery('#allcfg').hide(); + } + } + + function show_reset_settings(reset_type) { + if (reset_type == 'preset') { + jQuery('#pppoepresetwrap').show(); + jQuery('#pppoecustomwrap').hide(); + } else if (reset_type == 'custom') { + jQuery('#pppoecustomwrap').show(); + jQuery('#pppoepresetwrap').hide(); + } else { + jQuery('#pppoecustomwrap').hide(); + jQuery('#pppoepresetwrap').hide(); + } + } + + function show_mon_config() { + jQuery("#showmonbox").html(''); + jQuery('#showmon').css('display', 'block'); + } + + function openwindow(url) { + var oWin = window.open(url, "pfSensePop", "width=620,height=400,top=150,left=150"); + if (oWin == null || typeof(oWin) == "undefined") { + return false; + } else { + return true; + } + } + + function country_list() { + jQuery('#country').children().remove(); + jQuery('#provider_list').children().remove(); + jQuery('#providerplan').children().remove(); + jQuery.ajax("getserviceproviders.php",{ + success: function(response) { + var responseTextArr = response.split("\n"); + responseTextArr.sort(); + responseTextArr.each( function(value) { + var option = new Element('option'); + country = value.split(":"); + option.text = country[0]; + option.value = country[1]; + jQuery('#country').append(option); + }); + } + }); + jQuery('#trcountry').css('display', "table-row"); + } + + function providers_list() { + jQuery('#provider_list').children().remove(); + jQuery('#providerplan').children().remove(); + jQuery.ajax("getserviceproviders.php",{ + type: 'post', + data: {country : jQuery('#country').val()}, + success: function(response) { + var responseTextArr = response.split("\n"); + responseTextArr.sort(); + responseTextArr.each( function(value) { + var option = new Element('option'); + option.text = value; + option.value = value; + jQuery('#provider_list').append(option); + }); + } + }); + jQuery('#trprovider').css("display", "table-row"); + jQuery('#trproviderplan').css("display", "none"); + } + + function providerplan_list() { + jQuery('#providerplan').children().remove(); + jQuery('#providerplan').append( new Element('option') ); + jQuery.ajax("getserviceproviders.php",{ + type: 'post', + data: {country : jQuery('#country').val(), provider : jQuery('#provider_list').val()}, + success: function(response) { + var responseTextArr = response.split("\n"); + responseTextArr.sort(); + responseTextArr.each( function(value) { + if (value != "") { + providerplan = value.split(":"); + + var option = new Element('option'); + option.text = providerplan[0] + " - " + providerplan[1]; + option.value = providerplan[1]; + jQuery('#providerplan').append(option); + } + }); + } + }); + jQuery('#trproviderplan').css("display", "table-row"); + } + + function prefill_provider() { + jQuery.ajax("getserviceproviders.php",{ + type: 'post', + data: {country : jQuery('#country').val(), provider : jQuery('#provider_list').val(), plan : jQuery('#providerplan').val()}, + success: function(data, textStatus, response) { + var xmldoc = response.responseXML; + var provider = xmldoc.getElementsByTagName('connection')[0]; + jQuery('#ppp_username').val(''); + jQuery('#ppp_password').val(''); + if (provider.getElementsByTagName('apn')[0].firstChild.data == "CDMA") { + jQuery('#phone').val('#777'); + jQuery('#apn').val(''); + } else { + jQuery('#phone').val('*99#'); + jQuery('#apn').val(provider.getElementsByTagName('apn')[0].firstChild.data); + } + ppp_username = provider.getElementsByTagName('ppp_username')[0].firstChild.data; + ppp_password = provider.getElementsByTagName('ppp_password')[0].firstChild.data; + jQuery('#ppp_username').val(ppp_username); + jQuery('#ppp_password').val(ppp_password); + } + }); + } + + function update_track6_prefix() { + var iface = jQuery("#track6-interface").val(); + if (iface == null) { + return; + } + var track6_prefix_ids = jQuery('#ipv6-num-prefix-ids-' + iface).val(); + if (track6_prefix_ids == null) { + return; + } + track6_prefix_ids = parseInt(track6_prefix_ids).toString(16); + jQuery('#track6-prefix-id-range').html('(<b>hexadecimal</b> from 0 to ' + track6_prefix_ids + ')'); + } + + var gatewayip; + var name; + var gatewayipv6; + var namev6; + function show_add_gateway() { + document.getElementById("addgateway").style.display = ''; + document.getElementById("addgwbox").style.display = 'none'; + document.getElementById("gateway").style.display = 'none'; + document.getElementById("save").style.display = 'none'; + document.getElementById("cancel").style.display = 'none'; + document.getElementById("gwsave").style.display = ''; + document.getElementById("gwcancel").style.display = ''; + jQuery('#notebox').html(""); + } + function show_add_gateway_v6() { + document.getElementById("addgatewayv6").style.display = ''; + document.getElementById("addgwboxv6").style.display = 'none'; + document.getElementById("gatewayv6").style.display = 'none'; + document.getElementById("save").style.display = 'none'; + document.getElementById("cancel").style.display = 'none'; + document.getElementById("gwsave").style.display = ''; + document.getElementById("gwcancel").style.display = ''; + jQuery('#noteboxv6').html(""); + } + function hide_add_gateway() { + document.getElementById("addgateway").style.display = 'none'; + document.getElementById("addgwbox").style.display = ''; + document.getElementById("gateway").style.display = ''; + document.getElementById("save").style.display = ''; + document.getElementById("cancel").style.display = ''; + document.getElementById("gwsave").style.display = ''; + document.getElementById("gwcancel").style.display = ''; + jQuery('#status').html(''); + } + function hide_add_gateway_v6() { + document.getElementById("addgatewayv6").style.display = 'none'; + document.getElementById("addgwboxv6").style.display = ''; + document.getElementById("gatewayv6").style.display = ''; + document.getElementById("save").style.display = ''; + document.getElementById("cancel").style.display = ''; + document.getElementById("gwsave").style.display = ''; + document.getElementById("gwcancel").style.display = ''; + jQuery('#statusv6').html(''); + } + function hide_add_gatewaysave() { + document.getElementById("addgateway").style.display = 'none'; + jQuery('#status').html('<img src="/themes/<?=$g['theme']?>/images/misc/loader.gif" alt="loader" /> One moment please...'); + var iface = jQuery('#if').val(); + name = jQuery('#name').val(); + var descr = jQuery('#gatewaydescr').val(); + gatewayip = jQuery('#gatewayip').val(); + + var defaultgw = ''; + if (jQuery('#defaultgw').is(':checked')) { + defaultgw = '&defaultgw=on'; + } + var url = "system_gateways_edit.php"; + var pars = 'isAjax=true&ipprotocol=inet' + defaultgw + '&interface=' + escape(iface) + '&name=' + escape(name) + '&descr=' + escape(descr) + '&gateway=' + escape(gatewayip); + jQuery.ajax( + url, + { + type: 'post', + data: pars, + error: report_failure, + success: save_callback + }); + } + + function hide_add_gatewaysave_v6() { + document.getElementById("addgatewayv6").style.display = 'none'; + jQuery('#statusv6').html('<img src="/themes/<?=$g['theme']?>/images/misc/loader.gif" alt="loader" /> One moment please...'); + var iface = jQuery('#if').val(); + name = jQuery('#namev6').val(); + var descr = jQuery('#gatewaydescrv6').val(); + gatewayip = jQuery('#gatewayipv6').val(); + var defaultgw = ''; + if (jQuery('#defaultgwv6').is(':checked')) { + defaultgw = '&defaultgw=on'; + } + var url_v6 = "system_gateways_edit.php"; + var pars_v6 = 'isAjax=true&ipprotocol=inet6' + defaultgw + '&interface=' + escape(iface) + '&name=' + escape(name) + '&descr=' + escape(descr) + '&gateway=' + escape(gatewayip); + jQuery.ajax( + url_v6, + { + type: 'post', + data: pars_v6, + error: report_failure_v6, + success: save_callback_v6 + }); + } + + function addOption(selectbox, text, value) + { + var optn = document.createElement("OPTION"); + optn.text = text; + optn.value = value; + selectbox.append(optn); + selectbox.prop('selectedIndex', selectbox.children().length - 1); + jQuery('#notebox').html("<p><strong><?=gettext("NOTE:"); ?><\/strong><?=gettext("You can manage Gateways"); ?><a target='_blank' href='system_gateways.php'><?=gettext("here"); ?><\/a>.<\/p>"); + } + + function addOption_v6(selectbox, text, value) + { + var optn = document.createElement("OPTION"); + optn.text = text; + optn.value = value; + selectbox.append(optn); + selectbox.prop('selectedIndex', selectbox.children().length - 1); + jQuery('#noteboxv6').html("<p><strong><?=gettext("NOTE:"); ?><\/strong><?=gettext("You can manage Gateways"); ?><a target='_blank' href='system_gateways.php'><?=gettext("here"); ?><\/a>.<\/p>"); + } + + function report_failure(request, textStatus, errorThrown) { + if (textStatus === "error" && request.getResponseHeader("Content-Type") === "text/plain") { + alert(request.responseText); + } else { + alert("Sorry, we could not create your IPv4 gateway at this time."); + } + hide_add_gateway(); + } + + function report_failure_v6(request, textStatus, errorThrown) { + if (textStatus === "error" && request.getResponseHeader("Content-Type") === "text/plain") { + alert(request.responseText); + } else { + alert("Sorry, we could not create your IPv6 gateway at this time."); + } + hide_add_gateway_v6(); + } + + function save_callback(response) { + if (response) { + document.getElementById("addgateway").style.display = 'none'; + hide_add_gateway(); + var gwtext = escape(name) + " - " + gatewayip; + addOption(jQuery('#gateway'), gwtext, name); + // Auto submit form? + //document.iform.submit(); + //jQuery('#status').html('<img src="/themes/<?=$g['theme']?>/images/misc/loader.gif" alt="loader /">'); + } else { + report_failure(); + } + } + + function show_advanced_media() { + document.getElementById("showadvmediabox").innerHTML=''; + aodiv = document.getElementById('showmediaadv'); + aodiv.style.display = "block"; + } + + function save_callback_v6(response_v6) { + if (response_v6) { + document.getElementById("addgatewayv6").style.display = 'none'; + hide_add_gateway_v6(); + var gwtext_v6 = escape(name) + " - " + gatewayip; + addOption_v6(jQuery('#gatewayv6'), gwtext_v6, name); + // Auto submit form? + //document.iform.submit(); + //jQuery('#statusv6').html('<img src="/themes/<?=$g['theme']?>/images/misc/loader.gif" alt="loader" />'); + } else { + report_failure_v6(); + } + } + + + function customdhcpptcheckradiobuton(T, BUTTON) { + for (var i = 0; i < T.length; i++) { + T[i].checked = false; + if (T[i].value == BUTTON) T[i].checked = true; + } + T.value = BUTTON; + } + + function customdhcpptsetvalues(T, FORM) { + // timeout, retry, select-timeout, reboot, backoff-cutoff, initial-interval + if (T.value == "DHCP") customdhcpptsetvaluesnow(T, FORM, "60", "300", "0", "10", "120", "10"); + if (T.value == "pfSense") customdhcpptsetvaluesnow(T, FORM, "60", "15", "0", "", "", "1"); + if (T.value == "SavedCfg") customdhcpptsetvaluesnow(T, FORM, "<?=htmlspecialchars($pconfig['adv_dhcp_pt_timeout'])?>", "<?=htmlspecialchars($pconfig['adv_dhcp_pt_retry'])?>", "<?=htmlspecialchars($pconfig['adv_dhcp_pt_select_timeout'])?>", "<?=htmlspecialchars($pconfig['adv_dhcp_pt_reboot'])?>", "<?=htmlspecialchars($pconfig['adv_dhcp_pt_backoff_cutoff'])?>", "<?=htmlspecialchars($pconfig['adv_dhcp_pt_initial_interval'])?>"); + if (T.value == "Clear") customdhcpptsetvaluesnow(T, FORM, "", "", "", "", "", ""); + } + + function customdhcpptsetvaluesnow(T, FORM, timeout, retry, selecttimeout, reboot, backoffcutoff, initialinterval) { + FORM.adv_dhcp_pt_timeout.value = timeout; + FORM.adv_dhcp_pt_retry.value = retry; + FORM.adv_dhcp_pt_select_timeout.value = selecttimeout; + FORM.adv_dhcp_pt_reboot.value = reboot; + FORM.adv_dhcp_pt_backoff_cutoff.value = backoffcutoff; + FORM.adv_dhcp_pt_initial_interval.value = initialinterval; + + FORM.adv_dhcp_pt_values.value = T.value; + } + + <!-- Set the adv_dhcp_pt_values radio button from saved config --> + var RADIOBUTTON_VALUE = "<?=htmlspecialchars($pconfig['adv_dhcp_pt_values'])?>"; + if (RADIOBUTTON_VALUE == "") RADIOBUTTON_VALUE = "SavedCfg"; + //customdhcpptcheckradiobuton(document.iform.adv_dhcp_pt_values, RADIOBUTTON_VALUE); + + function show_adv_dhcp_config(T) { + + if (T.checked) { + T.value = "Selected"; + } else { + T.value = ""; + } + + if (document.iform.adv_dhcp_config_file_override.checked) { + show_hide_adv_dhcp('none', 'none', ''); + } else if (document.iform.adv_dhcp_config_advanced.checked) { + show_hide_adv_dhcp('', '', 'none'); + } else { + show_hide_adv_dhcp('', 'none', 'none'); + } + } + + function show_adv_dhcp6_config(T) { + + if (T.checked) { + T.value = "Selected"; + } else { + T.value = ""; + } + + if (document.iform.adv_dhcp6_config_file_override.checked) { + show_hide_adv_dhcp6('none', 'none', ''); + } else if (document.iform.adv_dhcp6_config_advanced.checked) { + show_hide_adv_dhcp6('none', '', 'none'); + } else { + show_hide_adv_dhcp6('', 'none', 'none'); + } + } + + function show_hide_adv_dhcp6(basic, advanced, override) { + document.getElementById("basicdhcp6_use_pppoeinterface").style.display = basic; + document.getElementById("basicdhcp6_show_dhcp6_prefix_delegation_size").style.display = basic; + document.getElementById("basicdhcp6_show_dhcp6_prefix_send_hint").style.display = basic; + document.getElementById("basicdhcp6_show_dhcp6_prefix_only").style.display = basic; + + document.getElementById("show_adv_dhcp6_interface_statement").style.display = advanced; + document.getElementById("show_adv_dhcp6_id_assoc_statement").style.display = advanced; + + document.getElementById("show_adv_dhcp6_id_assoc_statement_address").style.display = 'none'; + if (document.iform.adv_dhcp6_id_assoc_statement_address_enable.checked) { + document.getElementById("show_adv_dhcp6_id_assoc_statement_address").style.display = advanced; + } + + document.getElementById("show_adv_dhcp6_id_assoc_statement_prefix").style.display = 'none'; + document.getElementById("show_adv_dhcp6_prefix_interface_statement").style.display = 'none'; + if (document.iform.adv_dhcp6_id_assoc_statement_prefix_enable.checked) { + document.getElementById("show_adv_dhcp6_id_assoc_statement_prefix").style.display = advanced; + document.getElementById("show_adv_dhcp6_prefix_interface_statement").style.display = advanced; + } + + document.getElementById("show_adv_dhcp6_authentication_statement").style.display = advanced; + document.getElementById("show_adv_dhcp6_key_info_statement").style.display = advanced; + + document.getElementById("show_adv_dhcp6_config_file_override").style.display = override; + } + + <!-- Set the adv_dhcp6_config_advanced checkbox from saved config --> + if ("<?=htmlspecialchars($pconfig['adv_dhcp6_config_advanced'])?>" == "Selected") { + document.iform.adv_dhcp6_config_advanced.checked = true; + } + show_adv_dhcp6_config(document.iform.adv_dhcp6_config_advanced); + + <!-- Set the adv_dhcp6_config_file_override checkbox from saved config --> + if ("<?=htmlspecialchars($pconfig['adv_dhcp6_config_file_override'])?>" == "Selected") { + document.iform.adv_dhcp6_config_file_override.checked = true; + } + show_adv_dhcp6_config(document.iform.adv_dhcp6_config_file_override); + + <!-- Set the adv_dhcp6_interface_statement_information_only_enable checkbox from saved config --> + if ("<?=htmlspecialchars($pconfig['adv_dhcp6_interface_statement_information_only_enable'])?>" == "Selected") { + document.iform.adv_dhcp6_interface_statement_information_only_enable.checked = true; + } + show_adv_dhcp6_config(document.iform.adv_dhcp6_interface_statement_information_only_enable); + + <!-- Set the adv_dhcp6_id_assoc_statement_address_enable checkbox from saved config --> + if ("<?=htmlspecialchars($pconfig['adv_dhcp6_id_assoc_statement_address_enable'])?>" == "Selected") { + document.iform.adv_dhcp6_id_assoc_statement_address_enable.checked = true; + } + show_adv_dhcp6_config(document.iform.adv_dhcp6_id_assoc_statement_address_enable); + + <!-- Set the adv_dhcp6_id_assoc_statement_prefix_enable checkbox from saved config --> + if ("<?=htmlspecialchars($pconfig['adv_dhcp6_id_assoc_statement_prefix_enable'])?>" == "Selected") { + document.iform.adv_dhcp6_id_assoc_statement_prefix_enable.checked = true; + } + show_adv_dhcp6_config(document.iform.adv_dhcp6_id_assoc_statement_prefix_enable); + + function show_hide_adv_dhcp(basic, advanced, override) { + + document.getElementById("show_basic_dhcphostname").style.display = basic; + document.getElementById("show_basic_dhcpalias-address").style.display = basic; + document.getElementById("show_basic_dhcprejectlease").style.display = basic; + + document.getElementById("show_adv_dhcp_protocol_timing").style.display = advanced; + document.getElementById("show_adv_dhcp_lease_requirements_and_requests").style.display = advanced; + document.getElementById("show_adv_dhcp_option_modifiers").style.display = advanced; + + document.getElementById("show_adv_dhcp_config_file_override").style.display = override; + } + + <!-- Set the adv_dhcp_config_advanced checkbox from saved config --> + if ("<?=htmlspecialchars($pconfig['adv_dhcp_config_advanced'])?>" == "Selected") { + document.iform.adv_dhcp_config_advanced.checked = true; + } + show_adv_dhcp_config(document.iform.adv_dhcp_config_advanced); + + <!-- Set the adv_dhcp_config_file_override checkbox from saved config --> + if ("<?=htmlspecialchars($pconfig['adv_dhcp_config_file_override'])?>" == "Selected") { + document.iform.adv_dhcp_config_file_override.checked = true; + } + show_adv_dhcp_config(document.iform.adv_dhcp_config_file_override); +*/ + // Make the ‘Copy My MAC’ button a plain button, not a submit button + $("#btnmymac").prop('type','button'); + + // On click, copy the hidden 'mymac' text to the 'mac' input + $("#btnmymac").click(function() { + $('#mac').val('<?=$mymac?>'); + }); + + $('#country').on('change', function() { + window.location = 'interfaces_ppps_edit.php?id=' + $('#id').val() + '&country=' + this.value + '&type=' + $('#type').val(); + }); + + $('#provider').on('change', function() { + window.location = 'interfaces_ppps_edit.php?id=' + $('#id').val() + '&provider=' + this.value + '&type=' + $('#type').val() + '&country=' + $('#country').val(); + }); + +<?php + echo "show_allcfg(document.iform.enable);"; + echo "updateType('{$pconfig['type']}');\n"; + echo "updateTypeSix('{$pconfig['type6']}');\n"; + ?> + }); + //]]> + </script> + + + <?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/interfaces_assign.php b/src/usr/local/www/interfaces_assign.php new file mode 100644 index 0000000..1e38812 --- /dev/null +++ b/src/usr/local/www/interfaces_assign.php @@ -0,0 +1,559 @@ +<?php +/* + interfaces_assign.php + part of m0n0wall (http://m0n0.ch/wall) + Written by Jim McBeath based on existing m0n0wall files + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /bin/rm + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-assignnetworkports +##|*NAME=Interfaces: Assign network ports page +##|*DESCR=Allow access to the 'Interfaces: Assign network ports' page. +##|*MATCH=interfaces_assign.php* +##|-PRIV + +$pgtitle = array(gettext("Interfaces"), gettext("Assign network ports")); +$shortcut_section = "interfaces"; + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("ipsec.inc"); +require("vpn.inc"); +require("captiveportal.inc"); +require_once("rrd.inc"); + +function interface_assign_description($portinfo, $portname) { + global $ovpn_descrs; + if ($portinfo['isvlan']) { + $descr = sprintf(gettext('VLAN %1$s on %2$s'), $portinfo['tag'], $portinfo['if']); + if ($portinfo['descr']) { + $descr .= " (" . $portinfo['descr'] . ")"; + } + } elseif ($portinfo['iswlclone']) { + $descr = $portinfo['cloneif']; + if ($portinfo['descr']) { + $descr .= " (" . $portinfo['descr'] . ")"; + } + } elseif ($portinfo['isppp']) { + $descr = $portinfo['descr']; + } elseif ($portinfo['isbridge']) { + $descr = strtoupper($portinfo['bridgeif']); + if ($portinfo['descr']) { + $descr .= " (" . $portinfo['descr'] . ")"; + } + } elseif ($portinfo['isgre']) { + $descr = "GRE {$portinfo['remote-addr']}"; + if ($portinfo['descr']) { + $descr .= " (" . $portinfo['descr'] . ")"; + } + } elseif ($portinfo['isgif']) { + $descr = "GIF {$portinfo['remote-addr']}"; + if ($portinfo['descr']) { + $descr .= " (" . $portinfo['descr'] . ")"; + } + } elseif ($portinfo['islagg']) { + $descr = strtoupper($portinfo['laggif']); + if ($portinfo['descr']) { + $descr .= " (" . $portinfo['descr'] . ")"; + } + } elseif ($portinfo['isqinq']) { + $descr = $portinfo['descr']; + } elseif (substr($portname, 0, 4) == 'ovpn') { + $descr = $portname . " (" . $ovpn_descrs[substr($portname, 5)] . ")"; + } else { + $descr = $portname . " (" . $portinfo['mac'] . ")"; + } + + return htmlspecialchars($descr); +} + +/* + In this file, "port" refers to the physical port name, + while "interface" refers to LAN, WAN, or OPTn. +*/ + +/* get list without VLAN interfaces */ +$portlist = get_interface_list(); + +/* add wireless clone interfaces */ +if (is_array($config['wireless']['clone']) && count($config['wireless']['clone'])) { + foreach ($config['wireless']['clone'] as $clone) { + $portlist[$clone['cloneif']] = $clone; + $portlist[$clone['cloneif']]['iswlclone'] = true; + } +} + +/* add VLAN interfaces */ +if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) { + foreach ($config['vlans']['vlan'] as $vlan) { + $portlist[$vlan['vlanif']] = $vlan; + $portlist[$vlan['vlanif']]['isvlan'] = true; + } +} + +/* add Bridge interfaces */ +if (is_array($config['bridges']['bridged']) && count($config['bridges']['bridged'])) { + foreach ($config['bridges']['bridged'] as $bridge) { + $portlist[$bridge['bridgeif']] = $bridge; + $portlist[$bridge['bridgeif']]['isbridge'] = true; + } +} + +/* add GIF interfaces */ +if (is_array($config['gifs']['gif']) && count($config['gifs']['gif'])) { + foreach ($config['gifs']['gif'] as $gif) { + $portlist[$gif['gifif']] = $gif; + $portlist[$gif['gifif']]['isgif'] = true; + } +} + +/* add GRE interfaces */ +if (is_array($config['gres']['gre']) && count($config['gres']['gre'])) { + foreach ($config['gres']['gre'] as $gre) { + $portlist[$gre['greif']] = $gre; + $portlist[$gre['greif']]['isgre'] = true; + } +} + +/* add LAGG interfaces */ +if (is_array($config['laggs']['lagg']) && count($config['laggs']['lagg'])) { + foreach ($config['laggs']['lagg'] as $lagg) { + $portlist[$lagg['laggif']] = $lagg; + $portlist[$lagg['laggif']]['islagg'] = true; + /* LAGG members cannot be assigned */ + $lagifs = explode(',', $lagg['members']); + foreach ($lagifs as $lagif) { + if (isset($portlist[$lagif])) { + unset($portlist[$lagif]); + } + } + } +} + +/* add QinQ interfaces */ +if (is_array($config['qinqs']['qinqentry']) && count($config['qinqs']['qinqentry'])) { + foreach ($config['qinqs']['qinqentry'] as $qinq) { + $portlist["vlan{$qinq['tag']}"]['descr'] = "VLAN {$qinq['tag']}"; + $portlist["vlan{$qinq['tag']}"]['isqinq'] = true; + /* QinQ members */ + $qinqifs = explode(' ', $qinq['members']); + foreach ($qinqifs as $qinqif) { + $portlist["vlan{$qinq['tag']}_{$qinqif}"]['descr'] = "QinQ {$qinqif}"; + $portlist["vlan{$qinq['tag']}_{$qinqif}"]['isqinq'] = true; + } + } +} + +/* add PPP interfaces */ +if (is_array($config['ppps']['ppp']) && count($config['ppps']['ppp'])) { + foreach ($config['ppps']['ppp'] as $pppid => $ppp) { + $portname = $ppp['if']; + $portlist[$portname] = $ppp; + $portlist[$portname]['isppp'] = true; + $ports_base = basename($ppp['ports']); + if (isset($ppp['descr'])) { + $portlist[$portname]['descr'] = strtoupper($ppp['if']). "({$ports_base}) - {$ppp['descr']}"; + } else if (isset($ppp['username'])) { + $portlist[$portname]['descr'] = strtoupper($ppp['if']). "({$ports_base}) - {$ppp['username']}"; + } else { + $portlist[$portname]['descr'] = strtoupper($ppp['if']). "({$ports_base})"; + } + } +} + +$ovpn_descrs = array(); +if (is_array($config['openvpn'])) { + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as $s) { + $ovpn_descrs[$s['vpnid']] = $s['description']; + } + } + if (is_array($config['openvpn']['openvpn-client'])) { + foreach ($config['openvpn']['openvpn-client'] as $c) { + $ovpn_descrs[$c['vpnid']] = $c['description']; + } + } +} + +if (isset($_POST['if_add'])) { + /* Be sure this port is not being used */ + $portused = false; + foreach ($config['interfaces'] as $ifname => $ifdata) { + if ($ifdata['if'] == $_POST['if_add']) { + $portused = true; + break; + } + } + + if ($portused === false) { + /* find next free optional interface number */ + if (!$config['interfaces']['lan']) { + $newifname = gettext("lan"); + $descr = gettext("LAN"); + } else { + for ($i = 1; $i <= count($config['interfaces']); $i++) { + if (!$config['interfaces']["opt{$i}"]) { + break; + } + } + $newifname = 'opt' . $i; + $descr = "OPT" . $i; + } + + $config['interfaces'][$newifname] = array(); + $config['interfaces'][$newifname]['descr'] = $descr; + $config['interfaces'][$newifname]['if'] = $_POST['if_add']; + if (preg_match($g['wireless_regex'], $_POST['if_add'])) { + $config['interfaces'][$newifname]['wireless'] = array(); + interface_sync_wireless_clones($config['interfaces'][$newifname], false); + } + + uksort($config['interfaces'], "compare_interface_friendly_names"); + + /* XXX: Do not remove this. */ + unlink_if_exists("{$g['tmp_path']}/config.cache"); + + write_config(); + + $savemsg = gettext("Interface has been added."); + } + +} else if (isset($_POST['apply'])) { + if (file_exists("/var/run/interface_mismatch_reboot_needed")) { + system_reboot(); + $rebootingnow = true; + } else { + write_config(); + + $retval = filter_configure(); + $savemsg = get_std_save_message($retval); + + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message($retval); + } else { + $savemsg = $retval; + } + } + +} else if (isset($_POST['Submit'])) { + + unset($input_errors); + + /* input validation */ + + /* Build a list of the port names so we can see how the interfaces map */ + $portifmap = array(); + foreach ($portlist as $portname => $portinfo) { + $portifmap[$portname] = array(); + } + + /* Go through the list of ports selected by the user, + build a list of port-to-interface mappings in portifmap */ + foreach ($_POST as $ifname => $ifport) { + if (($ifname == 'lan') || ($ifname == 'wan') || (substr($ifname, 0, 3) == 'opt')) { + $portifmap[$ifport][] = strtoupper($ifname); + } + } + + /* Deliver error message for any port with more than one assignment */ + foreach ($portifmap as $portname => $ifnames) { + if (count($ifnames) > 1) { + $errstr = sprintf(gettext('Port %1$s '. + ' was assigned to %2$s' . + ' interfaces:'), $portname, count($ifnames)); + + foreach ($portifmap[$portname] as $ifn) { + $errstr .= " " . convert_friendly_interface_to_friendly_descr(strtolower($ifn)) . " (" . $ifn . ")"; + } + + $input_errors[] = $errstr; + } else if (count($ifnames) == 1 && preg_match('/^bridge[0-9]/', $portname) && is_array($config['bridges']['bridged']) && count($config['bridges']['bridged'])) { + foreach ($config['bridges']['bridged'] as $bridge) { + if ($bridge['bridgeif'] != $portname) { + continue; + } + + $members = explode(",", strtoupper($bridge['members'])); + foreach ($members as $member) { + if ($member == $ifnames[0]) { + $input_errors[] = sprintf(gettext("You cannot set port %s to interface %s because this interface is a member of %s."), $portname, $member, $portname); + break; + } + } + } + } + } + + if (is_array($config['vlans']['vlan'])) { + foreach ($config['vlans']['vlan'] as $vlan) { + if (does_interface_exist($vlan['if']) == false) { + $input_errors[] = "Vlan parent interface {$vlan['if']} does not exist anymore so vlan id {$vlan['tag']} cannot be created please fix the issue before continuing."; + } + } + } + + if (!$input_errors) { + /* No errors detected, so update the config */ + foreach ($_POST as $ifname => $ifport) { + + if (($ifname == 'lan') || ($ifname == 'wan') || (substr($ifname, 0, 3) == 'opt')) { + + if (!is_array($ifport)) { + $reloadif = false; + if (!empty($config['interfaces'][$ifname]['if']) && $config['interfaces'][$ifname]['if'] <> $ifport) { + interface_bring_down($ifname); + /* Mark this to be reconfigured in any case. */ + $reloadif = true; + } + $config['interfaces'][$ifname]['if'] = $ifport; + if (isset($portlist[$ifport]['isppp'])) { + $config['interfaces'][$ifname]['ipaddr'] = $portlist[$ifport]['type']; + } + + if (substr($ifport, 0, 3) == 'gre' || substr($ifport, 0, 3) == 'gif') { + unset($config['interfaces'][$ifname]['ipaddr']); + unset($config['interfaces'][$ifname]['subnet']); + unset($config['interfaces'][$ifname]['ipaddrv6']); + unset($config['interfaces'][$ifname]['subnetv6']); + } + + /* check for wireless interfaces, set or clear ['wireless'] */ + if (preg_match($g['wireless_regex'], $ifport)) { + if (!is_array($config['interfaces'][$ifname]['wireless'])) { + $config['interfaces'][$ifname]['wireless'] = array(); + } + } else { + unset($config['interfaces'][$ifname]['wireless']); + } + + /* make sure there is a descr for all interfaces */ + if (!isset($config['interfaces'][$ifname]['descr'])) { + $config['interfaces'][$ifname]['descr'] = strtoupper($ifname); + } + + if ($reloadif == true) { + if (preg_match($g['wireless_regex'], $ifport)) { + interface_sync_wireless_clones($config['interfaces'][$ifname], false); + } + /* Reload all for the interface. */ + interface_configure($ifname, true); + } + } + } + } + + write_config(); + + enable_rrd_graphing(); + } +} else { + unset($delbtn); + if (!empty($_POST['del'])) + $delbtn = key($_POST['del']); + + if (isset($delbtn)) { + $id = $delbtn; + + if (link_interface_to_group($id)) { + $input_errors[] = gettext("The interface is part of a group. Please remove it from the group to continue"); + } else if (link_interface_to_bridge($id)) { + $input_errors[] = gettext("The interface is part of a bridge. Please remove it from the bridge to continue"); + } else if (link_interface_to_gre($id)) { + $input_errors[] = gettext("The interface is part of a gre tunnel. Please delete the tunnel to continue"); + } else if (link_interface_to_gif($id)) { + $input_errors[] = gettext("The interface is part of a gif tunnel. Please delete the tunnel to continue"); + } else { + unset($config['interfaces'][$id]['enable']); + $realid = get_real_interface($id); + interface_bring_down($id); /* down the interface */ + + unset($config['interfaces'][$id]); /* delete the specified OPTn or LAN*/ + + if (is_array($config['dhcpd']) && is_array($config['dhcpd'][$id])) { + unset($config['dhcpd'][$id]); + services_dhcpd_configure(); + } + + if (count($config['filter']['rule']) > 0) { + foreach ($config['filter']['rule'] as $x => $rule) { + if ($rule['interface'] == $id) { + unset($config['filter']['rule'][$x]); + } + } + } + if (is_array($config['nat']['rule']) && count($config['nat']['rule']) > 0) { + foreach ($config['nat']['rule'] as $x => $rule) { + if ($rule['interface'] == $id) { + unset($config['nat']['rule'][$x]['interface']); + } + } + } + + write_config(); + + /* If we are in firewall/routing mode (not single interface) + * then ensure that we are not running DHCP on the wan which + * will make a lot of ISP's unhappy. + */ + if ($config['interfaces']['lan'] && $config['dhcpd']['wan']) { + unset($config['dhcpd']['wan']); + } + + link_interface_to_vlans($realid, "update"); + + $savemsg = gettext("Interface has been deleted."); + } + } +} + +/* Create a list of unused ports */ +$unused_portlist = array(); +foreach ($portlist as $portname => $portinfo) { + $portused = false; + foreach ($config['interfaces'] as $ifname => $ifdata) { + if ($ifdata['if'] == $portname) { + $portused = true; + break; + } + } + if ($portused === false) { + $unused_portlist[$portname] = $portinfo; + } +} + +include("head.inc"); + +if (file_exists("/var/run/interface_mismatch_reboot_needed")) { + if ($_POST) { + if ($rebootingnow) { + $savemsg = gettext("The system is now rebooting. Please wait."); + } else { + $savemsg = gettext("Reboot is needed. Please apply the settings in order to reboot."); + } + } else { + $savemsg = gettext("Interface mismatch detected. Please resolve the mismatch and click 'Apply changes'. The firewall will reboot afterwards."); + } +} + +if (file_exists("/tmp/reload_interfaces")) { + echo "<p>\n"; + print_info_box_np(gettext("The interface configuration has been changed.<br />You must apply the changes in order for them to take effect.")); + echo "<br /></p>\n"; +} elseif ($savemsg) { + print_info_box($savemsg); +} + +pfSense_handle_custom_code("/usr/local/pkg/interfaces_assign/pre_input_errors"); + +if ($input_errors) + print_input_errors($input_errors); + +$tab_array = array(); +$tab_array[0] = array(gettext("Interface assignments"), true, "interfaces_assign.php"); +$tab_array[1] = array(gettext("Interface Groups"), false, "interfaces_groups.php"); +$tab_array[2] = array(gettext("Wireless"), false, "interfaces_wireless.php"); +$tab_array[3] = array(gettext("VLANs"), false, "interfaces_vlan.php"); +$tab_array[4] = array(gettext("QinQs"), false, "interfaces_qinq.php"); +$tab_array[5] = array(gettext("PPPs"), false, "interfaces_ppps.php"); +$tab_array[7] = array(gettext("GRE"), false, "interfaces_gre.php"); +$tab_array[8] = array(gettext("GIF"), false, "interfaces_gif.php"); +$tab_array[9] = array(gettext("Bridges"), false, "interfaces_bridge.php"); +$tab_array[10] = array(gettext("LAGG"), false, "interfaces_lagg.php"); +display_top_tabs($tab_array); +?> +<form action="interfaces_assign.php" method="post"> + <div class="table-responsive"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th><?=gettext("Interface")?></th> + <th><?=gettext("Network port")?></th> + </tr> + </thead> + <tbody> +<?php + foreach ($config['interfaces'] as $ifname => $iface): + if ($iface['descr']) + $ifdescr = $iface['descr']; + else + $ifdescr = strtoupper($ifname); +?> + <tr> + <td><a href="/interfaces.php?if=<?=$ifname?>"><?=$ifdescr?></a></td> + <td> + <select name="<?=$ifname?>" id="<?=$ifname?>" class="form-control"> +<?php foreach ($portlist as $portname => $portinfo):?> + <option value="<?=$portname?>" <?=($portname == $iface['if']) ? ' selected="selected"': ''?>> + <?=interface_assign_description($portinfo, $portname)?> + </option> +<?php endforeach;?> + </select> + </td> + <td> +<?php if ($ifname != 'wan'):?> + <input type="submit" name="del[<?=$ifname?>]" class="btn btn-danger" value="<?=gettext("delete interface")?>"/> +<?php endif;?> + </td> + </tr> +<?php endforeach; + if (count($config['interfaces']) < count($portlist)): +?> + <tr> + <th> + <?=gettext("Available network ports:")?> + </th> + <td> + <select name="if_add" id="if_add" class="form-control"> +<?php foreach ($unused_portlist as $portname => $portinfo):?> + <option value="<?=$portname?>" <?=($portname == $iface['if']) ? ' selected="selected"': ''?>> + <?=interface_assign_description($portinfo, $portname)?> + </option> +<?php endforeach;?> + </select> + </td> + <td> + <input type="submit" name="add" title="<?=gettext("add selected interface")?>" value="add interface" class="btn btn-success" /> + </td> + </tr> +<?php endif;?> + </tbody> + </table> + </div> + + <input name="Submit" type="submit" class="btn btn-default" value="<?=gettext("Save")?>" /><br /><br /> +</form> + +<p class="alert alert-info"><?=gettext("Interfaces that are configured as members of a lagg(4) interface will not be shown.")?></p> + +<?php include("foot.inc")?>
\ No newline at end of file diff --git a/src/usr/local/www/interfaces_bridge.php b/src/usr/local/www/interfaces_bridge.php new file mode 100644 index 0000000..df28e78 --- /dev/null +++ b/src/usr/local/www/interfaces_bridge.php @@ -0,0 +1,167 @@ +<?php +/* $Id$ */ +/* + interfaces_bridge.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2008 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /bin/rm + pfSense_MODULE: interfaces_assign +*/ + +##|+PRIV +##|*IDENT=page-interfaces-bridge +##|*NAME=Interfaces: Bridge page +##|*DESCR=Allow access to the 'Interfaces: Bridge' page. +##|*MATCH=interfaces_bridge.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['bridges']['bridged'])) { + $config['bridges']['bridged'] = array(); +} + +$a_bridges = &$config['bridges']['bridged'] ; + +function bridge_inuse($num) { + global $config, $a_bridges; + + $iflist = get_configured_interface_list(false, true); + + foreach ($iflist as $if) { + if ($config['interfaces'][$if]['if'] == $a_bridges[$num]['bridgeif']) { + return true; + } + } + + return false; +} + +if ($_GET['act'] == "del") { + if (!isset($_GET['id'])) { + $input_errors[] = gettext("Wrong parameters supplied"); + } else if (empty($a_bridges[$_GET['id']])) { + $input_errors[] = gettext("Wrong index supplied"); + /* check if still in use */ + } else if (bridge_inuse($_GET['id'])) { + $input_errors[] = gettext("This bridge cannot be deleted because it is assigned as an interface."); + } else { + if (!does_interface_exist($a_bridges[$_GET['id']]['bridgeif'])) { + log_error("Bridge interface does not exist, skipping ifconfig destroy."); + } else { + mwexec("/sbin/ifconfig " . $a_bridges[$_GET['id']]['bridgeif'] . " destroy"); + } + + unset($a_bridges[$_GET['id']]); + + write_config(); + + header("Location: interfaces_bridge.php"); + exit; + } +} + +$pgtitle = array(gettext("Interfaces"),gettext("Bridge")); +$shortcut_section = "interfaces"; +include("head.inc"); +if ($input_errors) + print_input_errors($input_errors); ?> + +<?php +$tab_array = array(); +$tab_array[] = array(gettext("Interface assignments"), false, "interfaces_assign.php"); +$tab_array[] = array(gettext("Interface Groups"), false, "interfaces_groups.php"); +$tab_array[] = array(gettext("Wireless"), false, "interfaces_wireless.php"); +$tab_array[] = array(gettext("VLANs"), false, "interfaces_vlan.php"); +$tab_array[] = array(gettext("QinQs"), false, "interfaces_qinq.php"); +$tab_array[] = array(gettext("PPPs"), false, "interfaces_ppps.php"); +$tab_array[] = array(gettext("GRE"), false, "interfaces_gre.php"); +$tab_array[] = array(gettext("GIF"), false, "interfaces_gif.php"); +$tab_array[] = array(gettext("Bridges"), true, "interfaces_bridge.php"); +$tab_array[] = array(gettext("LAGG"), false, "interfaces_lagg.php"); +display_top_tabs($tab_array); +?> + +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Interface"); ?></th> + <th><?=gettext("Members"); ?></th> + <th><?=gettext("Description"); ?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php + +$i = 0; +$ifdescrs = get_configured_interface_with_descr(); + +foreach ($a_bridges as $bridge) { +?> + <tr> + <td> + <?=htmlspecialchars(strtoupper($bridge['bridgeif']))?> + </td> + <td> +<?php + $members = explode(',', $bridge['members']); + $j = 0; + foreach ($members as $member) { + if (isset($ifdescrs[$member])) { + echo $ifdescrs[$member]; + $j++; + } + if ($j > 0 && $j < count($members)) + echo ", "; + } +?> + </td> + <td> + <?=htmlspecialchars($bridge['descr'])?> + </td> + <td> + <a href="interfaces_bridge_edit.php?id=<?=$i?>" class="btn btn-default btn-xs"><?=gettext("Edit")?></a> + <a href="interfaces_bridge.php?act=del&id=<?=$i?>" class="btn btn-danger btn-xs"><?=gettext("Delete")?></a> + </td> + </tr> +<?php + $i++; +} +?> + </tbody> + </table> + + <nav class="action-buttons"> + <a href="interfaces_bridge_edit.php" class="btn btn-success"><?=gettext("Add")?></a> + </nav> + +</div> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/interfaces_bridge_edit.php b/src/usr/local/www/interfaces_bridge_edit.php new file mode 100644 index 0000000..c38afb6 --- /dev/null +++ b/src/usr/local/www/interfaces_bridge_edit.php @@ -0,0 +1,561 @@ +<?php +/* $Id$ */ +/* + interfaces_bridge_edit.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2008 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-bridge-edit +##|*NAME=Interfaces: Bridge edit page +##|*DESCR=Allow access to the 'Interfaces: Bridge : Edit' page. +##|*MATCH=interfaces_bridge_edit.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['bridges']['bridged'])) + $config['bridges']['bridged'] = array(); + +$a_bridges = &$config['bridges']['bridged']; + +$ifacelist = get_configured_interface_with_descr(); + +foreach ($ifacelist as $bif => $bdescr) { + if (substr(get_real_interface($bif), 0, 3) == "gre") { + unset($ifacelist[$bif]); + } +} + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_bridges[$id]) { + $pconfig['enablestp'] = isset($a_bridges[$id]['enablestp']); + $pconfig['descr'] = $a_bridges[$id]['descr']; + $pconfig['bridgeif'] = $a_bridges[$id]['bridgeif']; + $pconfig['members'] = $a_bridges[$id]['members']; + $pconfig['maxaddr'] = $a_bridges[$id]['maxaddr']; + $pconfig['timeout'] = $a_bridges[$id]['timeout']; + if ($a_bridges[$id]['static']) { + $pconfig['static'] = $a_bridges[$id]['static']; + } + if ($a_bridges[$id]['private']) { + $pconfig['private'] = $a_bridges[$id]['private']; + } + if (isset($a_bridges[$id]['stp'])) { + $pconfig['stp'] = $a_bridges[$id]['stp']; + } + $pconfig['maxage'] = $a_bridges[$id]['maxage']; + $pconfig['fwdelay'] = $a_bridges[$id]['fwdelay']; + $pconfig['hellotime'] = $a_bridges[$id]['hellotime']; + $pconfig['priority'] = $a_bridges[$id]['priority']; + $pconfig['proto'] = $a_bridges[$id]['proto']; + $pconfig['holdcnt'] = $a_bridges[$id]['holdcnt']; + + if (!empty($a_bridges[$id]['ifpriority'])) { + $pconfig['ifpriority'] = explode(",", $a_bridges[$id]['ifpriority']); + $ifpriority = array(); + foreach ($pconfig['ifpriority'] as $cfg) { + list ($key, $value) = explode(":", $cfg); + $embprioritycfg[$key] = $value; + foreach ($embprioritycfg as $key => $value) { + $ifpriority[$key] = $value; + } + } + $pconfig['ifpriority'] = $ifpriority; + } + + if (!empty($a_bridges[$id]['ifpathcost'])) { + $pconfig['ifpathcost'] = explode(",", $a_bridges[$id]['ifpathcost']); + $ifpathcost = array(); + foreach ($pconfig['ifpathcost'] as $cfg) { + list ($key, $value) = explode(":", $cfg); + $embpathcfg[$key] = $value; + foreach ($embpathcfg as $key => $value) { + $ifpathcost[$key] = $value; + } + } + $pconfig['ifpathcost'] = $ifpathcost; + } + + $pconfig['span'] = $a_bridges[$id]['span']; + if (isset($a_bridges[$id]['edge'])) { + $pconfig['edge'] = $a_bridges[$id]['edge']; + } + if (isset($a_bridges[$id]['autoedge'])) { + $pconfig['autoedge'] = $a_bridges[$id]['autoedge']; + } + if (isset($a_bridges[$id]['ptp'])) { + $pconfig['ptp'] = $a_bridges[$id]['ptp']; + } + if (isset($a_bridges[$id]['autoptp'])) { + $pconfig['autoptp'] = $a_bridges[$id]['autoptp']; + } +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "members"); + $reqdfieldsn = array(gettext("Member Interfaces")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ($_POST['maxage'] && !is_numeric($_POST['maxage'])) { + $input_errors[] = gettext("Maxage needs to be an integer between 6 and 40."); + } + if ($_POST['maxaddr'] && !is_numeric($_POST['maxaddr'])) { + $input_errors[] = gettext("Maxaddr needs to be an integer."); + } + if ($_POST['timeout'] && !is_numeric($_POST['timeout'])) { + $input_errors[] = gettext("Timeout needs to be an integer."); + } + if ($_POST['fwdelay'] && !is_numeric($_POST['fwdelay'])) { + $input_errors[] = gettext("Forward Delay needs to be an integer between 4 and 30."); + } + if ($_POST['hellotime'] && !is_numeric($_POST['hellotime'])) { + $input_errors[] = gettext("Hello time for STP needs to be an integer between 1 and 2."); + } + if ($_POST['priority'] && !is_numeric($_POST['priority'])) { + $input_errors[] = gettext("Priority for STP needs to be an integer between 0 and 61440."); + } + if ($_POST['holdcnt'] && !is_numeric($_POST['holdcnt'])) { + $input_errors[] = gettext("Transmit Hold Count for STP needs to be an integer between 1 and 10."); + } + foreach ($ifacelist as $ifn => $ifdescr) { + if ($_POST[$ifn] <> "" && !is_numeric($_POST[$ifn])) { + $input_errors[] = "{$ifdescr} " . gettext("interface priority for STP needs to be an integer between 0 and 240."); + } + } + + $i = 0; + + foreach ($ifacelist as $ifn => $ifdescr) { + if ($_POST["{$ifn}{$i}"] <> "" && !is_numeric($_POST["{$ifn}{$i}"])) { + $input_errors[] = "{$ifdescr} " . gettext("interface path cost for STP needs to be an integer between 1 and 200000000."); + } + $i++; + } + + if (!is_array($_POST['members']) || count($_POST['members']) < 1) { + $input_errors[] = gettext("You must select at least one member interface for a bridge."); + } + + if (is_array($_POST['members'])) { + foreach ($_POST['members'] as $ifmembers) { + if (empty($config['interfaces'][$ifmembers])) { + $input_errors[] = gettext("A member interface passed does not exist in configuration"); + } + if (is_array($config['interfaces'][$ifmembers]['wireless']) && + $config['interfaces'][$ifmembers]['wireless']['mode'] != "hostap") { + $input_errors[] = gettext("Bridging a wireless interface is only possible in hostap mode."); + } + if ($_POST['span'] != "none" && $_POST['span'] == $ifmembers) { + $input_errors[] = gettext("Span interface cannot be part of the bridge. Remove the span interface from bridge members to continue."); + } + } + } + + if (!$input_errors) { + $bridge = array(); + $bridge['members'] = implode(',', $_POST['members']); + $bridge['enablestp'] = $_POST['enablestp'] ? true : false; + $bridge['descr'] = $_POST['descr']; + $bridge['maxaddr'] = $_POST['maxaddr']; + $bridge['timeout'] = $_POST['timeout']; + if ($_POST['static']) { + $bridge['static'] = implode(',', $_POST['static']); + } + if ($_POST['private']) { + $bridge['private'] = implode(',', $_POST['private']); + } + if (isset($_POST['stp'])) { + $bridge['stp'] = implode(',', $_POST['stp']); + } + $bridge['maxage'] = $_POST['maxage']; + $bridge['fwdelay'] = $_POST['fwdelay']; + $bridge['hellotime'] = $_POST['hellotime']; + $bridge['priority'] = $_POST['priority']; + $bridge['proto'] = $_POST['proto']; + $bridge['holdcnt'] = $_POST['holdcnt']; + $i = 0; + $ifpriority = ""; + $ifpathcost = ""; + + foreach ($ifacelist as $ifn => $ifdescr) { + if ($_POST[$ifn] <> "") { + if ($i > 0) { + $ifpriority .= ","; + } + $ifpriority .= $ifn.":".$_POST[$ifn]; + } + if ($_POST["{$ifn}0"] <> "") { + if ($i > 0) { + $ifpathcost .= ","; + } + $ifpathcost .= $ifn.":".$_POST["{$ifn}0"]; + } + $i++; + } + + $bridge['ifpriority'] = $ifpriority; + $bridge['ifpathcost'] = $ifpathcost; + + if ($_POST['span'] != "none") { + $bridge['span'] = $_POST['span']; + } else { + unset($bridge['span']); + } + if (isset($_POST['edge'])) { + $bridge['edge'] = implode(',', $_POST['edge']); + } + if (isset($_POST['autoedge'])) { + $bridge['autoedge'] = implode(',', $_POST['autoedge']); + } + if (isset($_POST['ptp'])) { + $bridge['ptp'] = implode(',', $_POST['ptp']); + } + if (isset($_POST['autoptp'])) { + $bridge['autoptp'] = implode(',', $_POST['autoptp']); + } + + + $bridge['bridgeif'] = $_POST['bridgeif']; + interface_bridge_configure($bridge); + if ($bridge['bridgeif'] == "" || !stristr($bridge['bridgeif'], "bridge")) { + $input_errors[] = gettext("Error occurred creating interface, please retry."); + } else { + if (isset($id) && $a_bridges[$id]) { + $a_bridges[$id] = $bridge; + } else { + $a_bridges[] = $bridge; + } + + write_config(); + + $confif = convert_real_interface_to_friendly_interface_name($bridge['bridgeif']); + if ($confif <> "") { + interface_configure($confif); + } + + header("Location: interfaces_bridge.php"); + exit; + } + } +} + +function build_spanport_list() { + global $ifacelist; + + $splist = array('none' => 'None'); + + foreach ($ifacelist as $ifn => $ifdescr) + $splist[$ifn] = $ifdescr; + + return($splist); +} + +function build_member_list() { + global $pconfig, $ifacelist; + + $memberlist = array('list' => array(), + 'selected' => array()); + + $members_array = explode(',', $pconfig['members']); + foreach ($ifacelist as $ifn => $ifinfo) { + $memberlist['list'][$ifn] = $ifinfo; + + if (in_array($ifn, $members_array)) + array_push($memberlist['selected'], $ifn); + } + unset($members_array); + return($memberlist); +} + +function build_port_list($selecton) { + global $pconfig, $ifacelist; + + $portlist = array('list' => array(), + 'selected' => array()); + + foreach ($ifacelist as $ifn => $ifdescr) { + $portlist['list'][$ifn] = $ifdescr; + + if (stristr($selecton, $ifn)) + array_push($portlist['selected'], $ifn); + } + + return($portlist); +} + +$pgtitle = array(gettext("Interfaces"),gettext("Bridge"),gettext("Edit")); +$shortcut_section = "interfaces"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Bridge Configuration'); + +$memberslist = build_member_list(); + +$section->addInput(new Form_Select( + 'members', + 'Member Interfaces', + $memberslist['selected'], + $memberslist['list'], + true // Allow multiples +))->setHelp('Interfaces participating in the bridge'); + +$section->addInput(new Form_Input( + 'Descr', + 'Description', + 'text', + $pconfig['descr'] +)); + +$section->addInput(new Form_Checkbox( + 'showadvanced', + 'Advanced', + 'Show advanced options', + $pconfig['showadvanced'] +))->toggles('.toggle-advanced'); + +$form->add($section); + +$section = new Form_Section('Advanced Configuration'); + +// Set initial toggle state manually for now +if($pconfig['showadvanced']) + $section->addClass('toggle-advanced in'); +else + $section->addClass('toggle-advanced collapse'); + +$section->addInput(new Form_Input( + 'maxaddr', + 'Cache Size', + 'text', + $pconfig['maxaddr'] +))->setHelp('Set the size of the bridge address cache. The default is 100 entries'); + +$section->addInput(new Form_Input( + 'timeout', + 'Cache expire time', + 'text', + $pconfig['timeout'] +))->setHelp('Set the timeout of address cache entries to this number of seconds. If seconds is zero, then address cache entries will not be expired. The default is 240 seconds'); + +$section->addInput(new Form_Select( + 'span', + 'Span Port', + $pconfig['span'], + build_spanport_list() +))->setHelp('Add the interface named by interface as a span port on the bridge. Span ports transmit a copy of every frame received by the bridge.' . + 'This is most useful for snooping a bridged network passively on another host connected to one of the span ports of the bridge. <br />' . + '%sThe span interface cannot be part of the bridge member interfaces.%s', ['<strong>', '</strong>']); + +$edgelist = build_port_list($pconfig['edge']); + +$section->addInput(new Form_Select( + 'edge[]', + 'Edge Ports', + $edgelist['selected'], + $edgelist['list'], + true +))->setHelp('Set interface as an edge port. An edge port connects directly to end stations and cannot create bridging loops in the network; this allows it to transition straight to forwarding.'); + +$edgelist = build_port_list($pconfig['autoedge']); + +$section->addInput(new Form_Select( + 'autoedge[]', + 'Auto Edge Ports', + $edgelist['selected'], + $edgelist['list'], + true +))->setHelp('Allow interface to automatically detect edge status. This is the default for all interfaces added to a bridge.' . + '%sThis will disable the autoedge status of interfaces. %s', ['<strong>', '</strong>']); + +$edgelist = build_port_list($pconfig['ptp']); + +$section->addInput(new Form_Select( + 'ptp', + 'PTP Ports', + $edgelist['selected'], + $edgelist['list'], + true +))->setHelp('Set the interface as a point-to-point link. This is required for straight transitions to forwarding and should be enabled on a direct link to another RSTP-capable switch.'); + +$edgelist = build_port_list($pconfig['autoptp']); + +$section->addInput(new Form_Select( + 'autoptp[]', + 'Auto PTP Ports', + $edgelist['selected'], + $edgelist['list'], + true +))->setHelp('Automatically detect the point-to-point status on interface by checking the full duplex link status. This is the default for interfaces added to the bridge.' . + '%sThe interfaces selected here will be removed from default autoedge status. %s', ['<strong>', '</strong>']); + +$edgelist = build_port_list($pconfig['static']); + +$section->addInput(new Form_Select( + 'static[]', + 'Sticky Ports', + $edgelist['selected'], + $edgelist['list'], + true +))->setHelp('Mark an interface as a "sticky" interface. Dynamically learned address entries are treated as static once entered into the cache. ' . + 'Sticky entries are never aged out of the cache or replaced, even if the address is seen on a different interface.'); + +$edgelist = build_port_list($pconfig['private']); + +$section->addInput(new Form_Select( + 'private[]', + 'Private Ports', + $edgelist['selected'], + $edgelist['list'], + true +))->setHelp('Mark an interface as a "private" interface. A private interface does not forward any traffic to any other port that is also a private interface. '); + +// STP section +// ToDo: - Should disable spanning tree section when not checked +$section->addInput(new Form_Checkbox( + 'enablestp', + 'Enable RSTP/STP', + null, + $pconfig['enablestp'] +)); + +// Show the spanning tree section +$form->add($section); +$section = new Form_Section('RSTP/STP'); +if($pconfig['showadvanced']) + $section->addClass('toggle-advanced in'); +else + $section->addClass('toggle-advanced collapse'); + +$section->addInput(new Form_Select( + 'proto', + 'Protocol', + $pconfig['proto'], + array('rstp' => 'RSTP', + 'stp' => 'STP') +))->setHelp('Protocol used for spanning tree.'); + +$edgelist = build_port_list($pconfig['stp']); + +$section->addInput(new Form_Select( + 'stp[]', + 'STP Interfaces', + $edgelist['selected'], + $edgelist['list'], + true +))->setHelp('Enable Spanning Tree Protocol on interface. The if_bridge(4) driver has support for the IEEE 802.1D Spanning Tree Protocol (STP).' . + 'STP is used to detect and remove loops in a network topology.'); + +$section->addInput(new Form_Input( + 'maxage', + 'Valid time', + 'number', + $pconfig['maxage'], + ['placeholder' => 20, 'min' => 6, 'max' => 40] +))->setHelp('Set the time that a Spanning Tree Protocol configuration is valid. The default is 20 seconds. The minimum is 6 seconds and the maximum is 40 seconds.'); + +$section->addInput(new Form_Input( + 'fwdelay', + 'Forward time', + 'number', + $pconfig['fwdelay'], + ['placeholder' => 15, 'min' => 4, 'max' => 30] +))->setHelp('Set the time that must pass before an interface begins forwarding packets when Spanning Tree is enabled. The default is 15 seconds. The minimum is 4 seconds and the maximum is 30 seconds. '); + +$section->addInput(new Form_Input( + 'hellotime', + 'Hello time', + 'number', + $pconfig['hellotime'], + ['placeholder' => 2, 'min' => 1, 'max' => 2, 'step' => '0.1'] +))->setHelp('Set the time in seconds between broadcasting of Spanning Tree Protocol configuration messages. The hello time may only be changed when operating in legacy STP mode.' . + 'The default is 2 seconds. The minimum is 1 second and the maximum is 2 seconds.'); + +$section->addInput(new Form_Input( + 'priority', + 'Priority', + 'text', + $pconfig['priority'], + ['placeholder' => 32768, 'min' => 0, 'max' => 61440] +))->setHelp('Set the bridge priority for Spanning Tree. The default is 32768. The minimum is 0 and the maximum is 61440. '); + +$section->addInput(new Form_Input( + 'holdcnt', + 'Hold Count', + 'number', + $pconfig['holdcnt'], + ['placeholder' => 6, 'min' => 1, 'max' => 10] +))->setHelp('Set the transmit hold count for Spanning Tree. This is the number of packets transmitted before being rate limited. The default is 6. The minimum is 1 and the maximum is 10.'); + +foreach ($ifacelist as $ifn => $ifdescr) { + $section->addInput(new Form_Input( + $ifn, + $ifdescr . ' Priority', + 'number', + $pconfig[$ifn], + ['placeholder' => 128, 'min' => 0, 'max' => 240, 'step' => 16] + ))->setHelp('Set the Spanning Tree priority of interface to value. The default is 128. The minimum is 0 and the maximum is 240. Increments of 16.'); +} + +$i = 0; +foreach ($ifacelist as $ifn => $ifdescr) { + $section->addInput(new Form_Input( + $ifn . $i, + $ifdescr . ' Path cost', + 'number', + $ifpathcost[$ifn], + [ 'placeholder' => 0, 'min' => 1, 'max' => 200000000] + ))->setHelp('Set the Spanning Tree path cost of interface to value. The default is calculated from the link speed. '. + 'To change a previously selected path cost back to automatic, set the cost to 0. The minimum is 1 and the maximum is 200000000.'); + $i++; +} + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/interfaces_gif.php b/src/usr/local/www/interfaces_gif.php new file mode 100644 index 0000000..8b62154 --- /dev/null +++ b/src/usr/local/www/interfaces_gif.php @@ -0,0 +1,142 @@ +<?php +/* $Id$ */ +/* + interfaces_gif.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2008 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-gif +##|*NAME=Interfaces: GIF page +##|*DESCR=Allow access to the 'Interfaces: GIF' page. +##|*MATCH=interfaces_gif.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['gifs']['gif'])) { + $config['gifs']['gif'] = array(); +} + +$a_gifs = &$config['gifs']['gif'] ; + +function gif_inuse($num) { + global $config, $a_gifs; + + $iflist = get_configured_interface_list(false, true); + foreach ($iflist as $if) { + if ($config['interfaces'][$if]['if'] == $a_gifs[$num]['gifif']) { + return true; + } + } + + return false; +} + +if ($_GET['act'] == "del") { + if (!isset($_GET['id'])) { + $input_errors[] = gettext("Wrong parameters supplied"); + } else if (empty($a_gifs[$_GET['id']])) { + $input_errors[] = gettext("Wrong index supplied"); + /* check if still in use */ + } else if (gif_inuse($_GET['id'])) { + $input_errors[] = gettext("This gif TUNNEL cannot be deleted because it is still being used as an interface."); + } else { + mwexec("/sbin/ifconfig " . $a_gifs[$_GET['id']]['gifif'] . " destroy"); + unset($a_gifs[$_GET['id']]); + + write_config(); + + header("Location: interfaces_gif.php"); + exit; + } +} + +$pgtitle = array(gettext("Interfaces"), gettext("GIF")); +$shortcut_section = "interfaces"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +$tab_array = array(); +$tab_array[] = array(gettext("Interface assignments"), false, "interfaces_assign.php"); +$tab_array[] = array(gettext("Interface Groups"), false, "interfaces_groups.php"); +$tab_array[] = array(gettext("Wireless"), false, "interfaces_wireless.php"); +$tab_array[] = array(gettext("VLANs"), false, "interfaces_vlan.php"); +$tab_array[] = array(gettext("QinQs"), false, "interfaces_qinq.php"); +$tab_array[] = array(gettext("PPPs"), false, "interfaces_ppps.php"); +$tab_array[] = array(gettext("GRE"), false, "interfaces_gre.php"); +$tab_array[] = array(gettext("GIF"), true, "interfaces_gif.php"); +$tab_array[] = array(gettext("Bridges"), false, "interfaces_bridge.php"); +$tab_array[] = array(gettext("LAGG"), false, "interfaces_lagg.php"); +display_top_tabs($tab_array); +?> + +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Interface"); ?></th> + <th><?=gettext("Tunnel to …"); ?></th> + <th><?=gettext("Description"); ?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php foreach ($a_gifs as $i => $gif): ?> + <tr> + <td> + <?=htmlspecialchars(convert_friendly_interface_to_friendly_descr($gif['if']))?> + </td> + <td> + <?=htmlspecialchars($gif['remote-addr'])?> + </td> + <td> + <?=htmlspecialchars($gif['descr'])?> + </td> + <td> + <a href="interfaces_gif_edit.php?id=<?=$i?>" class="btn btn-default btn-xs"><?=gettext("Edit")?></a> + <a href="interfaces_gif.php?act=del&id=<?=$i?>" class="btn btn-danger btn-xs"><?=gettext("Delete")?></a> + </td> + </tr> +<?php endforeach; ?> + </tbody> + </table> +</div> + +<nav class="action-buttons"> + <a href="interfaces_gif_edit.php" class="btn btn-success"> + <?=gettext("Add")?> + </a> +</nav> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/interfaces_gif_edit.php b/src/usr/local/www/interfaces_gif_edit.php new file mode 100644 index 0000000..7600717 --- /dev/null +++ b/src/usr/local/www/interfaces_gif_edit.php @@ -0,0 +1,229 @@ +<?php +/* $Id$ */ +/* + interfaces_gif_edit.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2008 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-gif-edit +##|*NAME=Interfaces: GIF: Edit page +##|*DESCR=Allow access to the 'Interfaces: GIF: Edit' page. +##|*MATCH=interfaces_gif_edit.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['gifs']['gif'])) + $config['gifs']['gif'] = array(); + +$a_gifs = &$config['gifs']['gif']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_gifs[$id]) { + $pconfig['if'] = $a_gifs[$id]['if']; + if (!empty($a_gifs[$id]['ipaddr'])) { + $pconfig['if'] = $pconfig['if'] . '|' . $a_gifs[$id]['ipaddr']; + } + $pconfig['gifif'] = $a_gifs[$id]['gifif']; + $pconfig['remote-addr'] = $a_gifs[$id]['remote-addr']; + $pconfig['tunnel-remote-net'] = $a_gifs[$id]['tunnel-remote-net']; + $pconfig['tunnel-local-addr'] = $a_gifs[$id]['tunnel-local-addr']; + $pconfig['tunnel-remote-addr'] = $a_gifs[$id]['tunnel-remote-addr']; + $pconfig['link1'] = isset($a_gifs[$id]['link1']); + $pconfig['link0'] = isset($a_gifs[$id]['link0']); + $pconfig['descr'] = $a_gifs[$id]['descr']; +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "if remote-addr tunnel-local-addr tunnel-remote-addr tunnel-remote-net"); + $reqdfieldsn = array(gettext("Parent interface"), gettext("gif remote address"), gettext("gif tunnel local address"), gettext("gif tunnel remote address"), gettext("gif tunnel remote netmask")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ((!is_ipaddr($_POST['tunnel-local-addr'])) || + (!is_ipaddr($_POST['tunnel-remote-addr'])) || + (!is_ipaddr($_POST['remote-addr']))) { + $input_errors[] = gettext("The tunnel local and tunnel remote fields must have valid IP addresses."); + } + + $alias = strstr($_POST['if'], '|'); + if ((is_ipaddrv4($alias) && !is_ipaddrv4($_POST['remote-addr'])) || + (is_ipaddrv6($alias) && !is_ipaddrv6($_POST['remote-addr']))) { + $input_errors[] = gettext("The alias IP address family has to match the family of the remote peer address."); + } + + foreach ($a_gifs as $gif) { + if (isset($id) && ($a_gifs[$id]) && ($a_gifs[$id] === $gif)) { + continue; + } + + /* FIXME: needs to perform proper subnet checks in the future */ + if (($gif['if'] == $interface) && ($gif['tunnel-remote-addr'] == $_POST['tunnel-remote-addr'])) { + $input_errors[] = sprintf(gettext("A gif with the network %s is already defined."), $gif['tunnel-remote-addr']); + break; + } + } + + if (!$input_errors) { + $gif = array(); + list($gif['if'], $gif['ipaddr']) = explode("|", $_POST['if']); + $gif['tunnel-local-addr'] = $_POST['tunnel-local-addr']; + $gif['tunnel-remote-addr'] = $_POST['tunnel-remote-addr']; + $gif['tunnel-remote-net'] = $_POST['tunnel-remote-net']; + $gif['remote-addr'] = $_POST['remote-addr']; + $gif['descr'] = $_POST['descr']; + $gif['link1'] = isset($_POST['link1']); + $gif['link0'] = isset($_POST['link0']); + $gif['gifif'] = $_POST['gifif']; + $gif['gifif'] = interface_gif_configure($gif); + + if ($gif['gifif'] == "" || !stristr($gif['gifif'], "gif")) + $input_errors[] = gettext("Error occurred creating interface, please retry."); + else { + if (isset($id) && $a_gifs[$id]) + $a_gifs[$id] = $gif; + else + $a_gifs[] = $gif; + + write_config(); + + $confif = convert_real_interface_to_friendly_interface_name($gif['gifif']); + + if ($confif != "") + interface_configure($confif); + + header("Location: interfaces_gif.php"); + exit; + } + } +} + +function build_parent_list() { + $parentlist = array(); + $portlist = get_possible_listen_ips(); + foreach ($portlist as $ifn => $ifinfo) + $parentlist[$ifn] = $ifinfo; + + return($parentlist); +} + +$pgtitle = array(gettext("Interfaces"),gettext("GIF"),gettext("Edit")); +$shortcut_section = "interfaces"; +include("head.inc"); +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('GIF Configuration'); + +$section->addInput(new Form_Select( + 'if', + 'Parent Interface', + $pconfig['if'], + build_parent_list() +))->setHelp('This interface serves as the local address to be used for the GIF tunnel.'); + +$section->addInput(new Form_IpAddress( + 'remote-addr', + 'GIF Remote Address', + $pconfig['remote-addr'] +))->setHelp('Peer address where encapsulated gif packets will be sent.'); + +$section->addInput(new Form_IpAddress( + 'tunnel-local-addr', + 'GIF tunnel local address', + $pconfig['tunnel-local-addr'] +))->setHelp('Local gif tunnel endpoint.'); + +$section->addInput(new Form_IpAddress( + 'tunnel-remote-addr', + 'GIF tunnel remote address', + $pconfig['tunnel-remote-addr'] +))->setHelp('Remote GIF address endpoint.'); + +$section->addInput(new Form_Select( + 'tunnel-remote-net', + 'GIF tunnel remote subnet', + $pconfig['tunnel-remote-net'], + array_combine(range(128, 1, -1), range(128, 1, -1)) +))->setHelp('The subnet is used for determining the network that is tunnelled'); + +$section->addInput(new Form_Checkbox( + 'link0', + 'Route Caching', + 'Specify if route caching can be enabled. (Be careful with these settings on dynamic networks.)', + $pconfig['link0'] +)); + +$section->addInput(new Form_Checkbox( + 'link1', + 'ECN friendly behavior', + 'ECN friendly behavior violates RFC2893. This should be used in mutual agreement with the peer. ', + $pconfig['link1'] +)); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$section->addInput(new Form_Input( + 'gifif', + null, + 'hidden', + $pconfig['gifif'] +)); + +if (isset($id) && $a_gifs[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); +print($form); + +include("foot.inc"); diff --git a/src/usr/local/www/interfaces_gre.php b/src/usr/local/www/interfaces_gre.php new file mode 100644 index 0000000..7d77c66 --- /dev/null +++ b/src/usr/local/www/interfaces_gre.php @@ -0,0 +1,145 @@ +<?php +/* $Id$ */ +/* + interfaces_gre.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2008 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-gre +##|*NAME=Interfaces: GRE page +##|*DESCR=Allow access to the 'Interfaces: GRE' page. +##|*MATCH=interfaces_gre.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); + +if (!is_array($config['gres']['gre'])) { + $config['gres']['gre'] = array(); +} + +$a_gres = &$config['gres']['gre'] ; + +function gre_inuse($num) { + global $config, $a_gres; + + $iflist = get_configured_interface_list(false, true); + foreach ($iflist as $if) { + if ($config['interfaces'][$if]['if'] == $a_gres[$num]['greif']) { + return true; + } + } + + return false; +} + +if ($_GET['act'] == "del") { + if (!isset($_GET['id'])) { + $input_errors[] = gettext("Wrong parameters supplied"); + } else if (empty($a_gres[$_GET['id']])) { + $input_errors[] = gettext("Wrong index supplied"); + /* check if still in use */ + } else if (gre_inuse($_GET['id'])) { + $input_errors[] = gettext("This GRE tunnel cannot be deleted because it is still being used as an interface."); + } else { + mwexec("/sbin/ifconfig " . $a_gres[$_GET['id']]['greif'] . " destroy"); + unset($a_gres[$_GET['id']]); + + write_config(); + + header("Location: interfaces_gre.php"); + exit; + } +} + +$pgtitle = array(gettext("Interfaces"), gettext("GRE")); +$shortcut_section = "interfaces"; +include("head.inc"); +if ($input_errors) + print_input_errors($input_errors); + +$tab_array = array(); +$tab_array[] = array(gettext("Interface assignments"), false, "interfaces_assign.php"); +$tab_array[] = array(gettext("Interface Groups"), false, "interfaces_groups.php"); +$tab_array[] = array(gettext("Wireless"), false, "interfaces_wireless.php"); +$tab_array[] = array(gettext("VLANs"), false, "interfaces_vlan.php"); +$tab_array[] = array(gettext("QinQs"), false, "interfaces_qinq.php"); +$tab_array[] = array(gettext("PPPs"), false, "interfaces_ppps.php"); +$tab_array[] = array(gettext("GRE"), true, "interfaces_gre.php"); +$tab_array[] = array(gettext("GIF"), false, "interfaces_gif.php"); +$tab_array[] = array(gettext("Bridges"), false, "interfaces_bridge.php"); +$tab_array[] = array(gettext("LAGG"), false, "interfaces_lagg.php"); +display_top_tabs($tab_array); +?> +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Interface"); ?></th> + <th><?=gettext("Tunnel to …"); ?></th> + <th><?=gettext("Description"); ?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php foreach ($a_gres as $i => $gre): ?> + <tr> + <td> + <?=htmlspecialchars(convert_friendly_interface_to_friendly_descr($gre['if']))?> + </td> + <td> + <?=htmlspecialchars($gre['remote-addr'])?> + </td> + <td> + <?=htmlspecialchars($gre['descr'])?> + </td> + <td> + <a href="interfaces_gre_edit.php?id=<?=$i?>" class="btn btn-default btn-xs"> + <?=gettext("Edit")?> + </a> + <a href="interfaces_gre.php?act=del&id=<?=$i?>" class="btn btn-danger btn-xs"> + <?=gettext("Delete")?> + </a> + </td> + </tr> +<?php endforeach; ?> + </tbody> + </table> +</div> + +<nav class="action-buttons"> + <a href="interfaces_gre_edit.php" class="btn btn-success"> + <?=gettext("Add")?> + </a> +</nav> +<?php +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/interfaces_gre_edit.php b/src/usr/local/www/interfaces_gre_edit.php new file mode 100644 index 0000000..8a6497b --- /dev/null +++ b/src/usr/local/www/interfaces_gre_edit.php @@ -0,0 +1,231 @@ +<?php +/* $Id$ */ +/* + interfaces_gre_edit.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2008 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-gre-edit +##|*NAME=Interfaces: GRE: Edit page +##|*DESCR=Allow access to the 'Interfaces: GRE: Edit' page. +##|*MATCH=interfaces_gre_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); + +if (!is_array($config['gres']['gre'])) + $config['gres']['gre'] = array(); + +$a_gres = &$config['gres']['gre']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_gres[$id]) { + $pconfig['if'] = $a_gres[$id]['if']; + $pconfig['greif'] = $a_gres[$id]['greif']; + $pconfig['remote-addr'] = $a_gres[$id]['remote-addr']; + $pconfig['tunnel-remote-net'] = $a_gres[$id]['tunnel-remote-net']; + $pconfig['tunnel-local-addr'] = $a_gres[$id]['tunnel-local-addr']; + $pconfig['tunnel-remote-addr'] = $a_gres[$id]['tunnel-remote-addr']; + $pconfig['link1'] = isset($a_gres[$id]['link1']); + $pconfig['link2'] = isset($a_gres[$id]['link2']); + $pconfig['link0'] = isset($a_gres[$id]['link0']); + $pconfig['descr'] = $a_gres[$id]['descr']; +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "if remote-addr tunnel-local-addr tunnel-remote-addr tunnel-remote-net"); + $reqdfieldsn = array(gettext("Parent interface"), gettext("Remote tunnel endpoint IP address"), gettext("Local tunnel IP address"), gettext("Remote tunnel IP address"), gettext("Remote tunnel network")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ((!is_ipaddr($_POST['tunnel-local-addr'])) || + (!is_ipaddr($_POST['tunnel-remote-addr'])) || + (!is_ipaddr($_POST['remote-addr']))) { + $input_errors[] = gettext("The tunnel local and tunnel remote fields must have valid IP addresses."); + } + + foreach ($a_gres as $gre) { + if (isset($id) && ($a_gres[$id]) && ($a_gres[$id] === $gre)) { + continue; + } + + if (($gre['if'] == $_POST['if']) && ($gre['tunnel-remote-addr'] == $_POST['tunnel-remote-addr'])) { + $input_errors[] = sprintf(gettext("A GRE tunnel with the network %s is already defined."), $gre['remote-network']); + break; + } + } + + if (!$input_errors) { + $gre = array(); + $gre['if'] = $_POST['if']; + $gre['tunnel-local-addr'] = $_POST['tunnel-local-addr']; + $gre['tunnel-remote-addr'] = $_POST['tunnel-remote-addr']; + $gre['tunnel-remote-net'] = $_POST['tunnel-remote-net']; + $gre['remote-addr'] = $_POST['remote-addr']; + $gre['descr'] = $_POST['descr']; + $gre['link1'] = isset($_POST['link1']); + $gre['link2'] = isset($_POST['link2']); + $gre['link0'] = isset($_POST['link0']); + $gre['greif'] = $_POST['greif']; + + $gre['greif'] = interface_gre_configure($gre); + if ($gre['greif'] == "" || !stristr($gre['greif'], "gre")) { + $input_errors[] = gettext("Error occurred creating interface, please retry."); + } else { + if (isset($id) && $a_gres[$id]) { + $a_gres[$id] = $gre; + } else { + $a_gres[] = $gre; + } + + write_config(); + + $confif = convert_real_interface_to_friendly_interface_name($gre['greif']); + + if ($confif != "") + interface_configure($confif); + + header("Location: interfaces_gre.php"); + exit; + } + } +} + +function build_parent_list() { + $parentlist = array(); + $portlist = get_possible_listen_ips(); + foreach ($portlist as $ifn => $ifinfo) + $parentlist[$ifn] = $ifinfo; + + return($parentlist); +} + +$pgtitle = array(gettext("Interfaces"),gettext("GRE"),gettext("Edit")); +$shortcut_section = "interfaces"; +include("head.inc"); +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('GRE Configuration'); + +$section->addInput(new Form_Select( + 'if', + 'Parent Interface', + $pconfig['if'], + build_parent_list() +))->setHelp('This interface serves as the local address to be used for the GRE tunnel.'); + +$section->addInput(new Form_IpAddress( + 'remote-addr', + 'GRE Remote Address', + $pconfig['remote-addr'] +))->setHelp('Peer address where encapsulated GRE packets will be sent.'); + +$section->addInput(new Form_IpAddress( + 'tunnel-local-addr', + 'GRE tunnel local address', + $pconfig['tunnel-local-addr'] +))->setHelp('Local GRE tunnel endpoint.'); + +$section->addInput(new Form_IpAddress( + 'tunnel-remote-addr', + 'GRE tunnel remote address', + $pconfig['tunnel-remote-addr'] +))->setHelp('Remote GRE address endpoint.'); + +$section->addInput(new Form_Select( + 'tunnel-remote-net', + 'GRE tunnel remote subnet', + $pconfig['tunnel-remote-net'], + array_combine(range(128, 1, -1), range(128, 1, -1)) +))->setHelp('The subnet is used for determining the network that is tunnelled'); + +$section->addInput(new Form_Checkbox( + 'link0', + 'Route Caching', + 'Specify if route caching can be enabled. (Be careful with these settings on dynamic networks.)', + $pconfig['link0'] +)); + +$section->addInput(new Form_Checkbox( + 'link1', + 'ECN friendly behavior', + 'ECN friendly behavior violates RFC2893. This should be used in mutual agreement with the peer. ', + $pconfig['link1'] +)); + +$section->addInput(new Form_Checkbox( + 'link2', + 'WCCP Version', + 'Check this box for WCCP encapsulation version 2, or leave unchecked for version 1.', + $pconfig['link2'] +)); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$section->addInput(new Form_Input( + 'greif', + null, + 'hidden', + $pconfig['greif'] +)); + +if (isset($id) && $a_gres[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/interfaces_groups.php b/src/usr/local/www/interfaces_groups.php new file mode 100644 index 0000000..a6a2c32 --- /dev/null +++ b/src/usr/local/www/interfaces_groups.php @@ -0,0 +1,146 @@ +<?php +/* + interfaces_groups.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2009 Ermal Luçi + Copyright (C) 2004 Scott Ullrich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-groups +##|*NAME=Interfaces: Groups page +##|*DESCR=Create interface groups +##|*MATCH=interfaces_groups.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); + +if (!is_array($config['ifgroups']['ifgroupentry'])) { + $config['ifgroups']['ifgroupentry'] = array(); +} + +$a_ifgroups = &$config['ifgroups']['ifgroupentry']; + +if ($_GET['act'] == "del") { + if ($a_ifgroups[$_GET['id']]) { + $members = explode(" ", $a_ifgroups[$_GET['id']]['members']); + foreach ($members as $ifs) { + $realif = get_real_interface($ifs); + if ($realif) { + mwexec("/sbin/ifconfig {$realif} -group " . $a_ifgroups[$_GET['id']]['ifname']); + } + } + unset($a_ifgroups[$_GET['id']]); + write_config(); + header("Location: interfaces_groups.php"); + exit; + } +} + +$pgtitle = array(gettext("Interfaces"), gettext("Groups")); +$shortcut_section = "interfaces"; + +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("Interface assignments"), false, "interfaces_assign.php"); +$tab_array[] = array(gettext("Interface Groups"), true, "interfaces_groups.php"); +$tab_array[] = array(gettext("Wireless"), false, "interfaces_wireless.php"); +$tab_array[] = array(gettext("VLANs"), false, "interfaces_vlan.php"); +$tab_array[] = array(gettext("QinQs"), false, "interfaces_qinq.php"); +$tab_array[] = array(gettext("PPPs"), false, "interfaces_ppps.php"); +$tab_array[] = array(gettext("GRE"), false, "interfaces_gre.php"); +$tab_array[] = array(gettext("GIF"), false, "interfaces_gif.php"); +$tab_array[] = array(gettext("Bridges"), false, "interfaces_bridge.php"); +$tab_array[] = array(gettext("LAGG"), false, "interfaces_lagg.php"); +display_top_tabs($tab_array); + +print_info_box(gettext('Interface Groups allow you to setup rules for multiple interfaces without duplicating the rules.<br />' . + 'If you remove members from an interface group, the group rules are no longer applicable to that interface.')); +?> +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext('Name');?></th> + <th><?=gettext('Members');?></th> + <th><?=gettext('Description');?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php foreach ($a_ifgroups as $i => $ifgroupentry): ?> + <tr> + <td> + <?=htmlspecialchars($ifgroupentry['ifname']); ?> + </td> + <td> +<?php + $members_arr = explode(" ", $ifgroupentry['members']); + $iflist = get_configured_interface_with_descr(false, true); + $memberses_arr = array(); + foreach ($members_arr as $memb) + $memberses_arr[] = $iflist[$memb] ? $iflist[$memb] : $memb; + + unset($iflist); + $memberses = implode(", ", $memberses_arr); + echo $memberses; + if(count($members_arr) >= 10) { + echo '…'; + } +?> + </td> + <td> + <?=htmlspecialchars($ifgroupentry['descr']);?> + </td> + <td> + <a class="btn btn-default btn-sm" role="button" href="interfaces_groups_edit.php?id=<?=$i; ?>"> + <?=gettext('Edit'); ?> + </a> + <a class="btn btn-danger btn-sm" role="button" href="interfaces_groups.php?act=del&id=<?=$i; ?>"> + <?=gettext("Delete"); ?> + </a> + </td> + </tr> +<?php endforeach; ?> + </tbody> + </table> +</div> + +<nav class="action-buttons"> + <a class="btn btn-success btn-sm" href="interfaces_groups_edit.php" role="button"> + <?=gettext("Add Group");?> + </a> +</nav> + +<?php + +include("fend.inc");
\ No newline at end of file diff --git a/src/usr/local/www/interfaces_groups_edit.php b/src/usr/local/www/interfaces_groups_edit.php new file mode 100644 index 0000000..8e7b6da --- /dev/null +++ b/src/usr/local/www/interfaces_groups_edit.php @@ -0,0 +1,257 @@ +<?php +/* + interfaces_groups_edit.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2009 Ermal Luçi + Copyright (C) 2004 Scott Ullrich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-groups-edit +##|*NAME=Interfaces: Groups: Edit page +##|*DESCR=Allow access to the 'Interfaces: Groups: Edit' page. +##|*MATCH=interfaces_groups_edit.php* +##|-PRIV + + +require("guiconfig.inc"); +require_once("functions.inc"); + +$pgtitle = array(gettext("Interfaces"), gettext("Groups"), gettext("Edit")); +$shortcut_section = "interfaces"; + +if (!is_array($config['ifgroups']['ifgroupentry'])) { + $config['ifgroups']['ifgroupentry'] = array(); +} + +$a_ifgroups = &$config['ifgroups']['ifgroupentry']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_ifgroups[$id]) { + $pconfig['ifname'] = $a_ifgroups[$id]['ifname']; + $pconfig['members'] = $a_ifgroups[$id]['members']; + $pconfig['descr'] = html_entity_decode($a_ifgroups[$id]['descr']); +} + +$interface_list = get_configured_interface_with_descr(); +$interface_list_disabled = get_configured_interface_with_descr(false, true); + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + if (!isset($id)) { + foreach ($a_ifgroups as $groupentry) { + if ($groupentry['ifname'] == $_POST['ifname']) { + $input_errors[] = gettext("Group name already exists!"); + } + } + } + if (preg_match("/([^a-zA-Z])+/", $_POST['ifname'], $match)) { + $input_errors[] = gettext("Only letters A-Z are allowed as the group name."); + } + + foreach ($iflist as $gif => $gdescr) { + if ($gdescr == $_POST['ifname'] || $gif == $_POST['ifname']) { + $input_errors[] = "The specified group name is already used by an interface. Please choose another name."; + } + } + $members = ""; + $isfirst = 0; + /* item is a normal ifgroupentry type */ + for ($x = 0; $x < 9999; $x++) { + if ($_POST["members{$x}"] <> "") { + if ($isfirst > 0) { + $members .= " "; + } + $members .= $_POST["members{$x}"]; + $isfirst++; + } + } + + $members = isset($_POST['members']) ? join(' ', $_POST['members']) : ""; + + if (!$input_errors) { + $ifgroupentry = array(); + $ifgroupentry['members'] = $members; + $ifgroupentry['descr'] = $_POST['descr']; + + // Edit group name + if (isset($id) && $a_ifgroups[$id] && $_POST['ifname'] != $a_ifgroups[$id]['ifname']) { + if (!empty($config['filter']) && is_array($config['filter']['rule'])) { + foreach ($config['filter']['rule'] as $ridx => $rule) { + if (isset($rule['floating'])) { + $rule_ifs = explode(",", $rule['interface']); + $rule_changed = false; + foreach ($rule_ifs as $rule_if_id => $rule_if) { + if ($rule_if == $a_ifgroups[$id]['ifname']) { + $rule_ifs[$rule_if_id] = $_POST['ifname']; + $rule_changed = true; + } + } + if ($rule_changed) { + $config['filter']['rule'][$ridx]['interface'] = implode(",", $rule_ifs); + } + } else { + if ($rule['interface'] == $a_ifgroups[$id]['ifname']) { + $config['filter']['rule'][$ridx]['interface'] = $_POST['ifname']; + } + } + } + } + if (!empty($config['nat']) && is_array($config['nat']['rule'])) { + foreach ($config['nat']['rule'] as $ridx => $rule) { + if ($rule['interface'] == $a_ifgroups[$id]['ifname']) { + $config['nat']['rule'][$ridx]['interface'] = $_POST['ifname']; + } + } + } + $omembers = explode(" ", $a_ifgroups[$id]['members']); + if (count($omembers) > 0) { + foreach ($omembers as $ifs) { + $realif = get_real_interface($ifs); + if ($realif) { + mwexec("/sbin/ifconfig {$realif} -group " . $a_ifgroups[$id]['ifname']); + } + } + } + $ifgroupentry['ifname'] = $_POST['ifname']; + $a_ifgroups[$id] = $ifgroupentry; + + // Edit old group + } else if (isset($id) && $a_ifgroups[$id]) { + $omembers = explode(" ", $a_ifgroups[$id]['members']); + $nmembers = explode(" ", $members); + $delmembers = array_diff($omembers, $nmembers); + if (count($delmembers) > 0) { + foreach ($delmembers as $ifs) { + $realif = get_real_interface($ifs); + if ($realif) { + mwexec("/sbin/ifconfig {$realif} -group " . $a_ifgroups[$id]['ifname']); + } + } + } + $ifgroupentry['ifname'] = $_POST['ifname']; + $a_ifgroups[$id] = $ifgroupentry; + + // Create new group + } else { + $ifgroupentry['ifname'] = $_POST['ifname']; + $a_ifgroups[] = $ifgroupentry; + } + + write_config(); + interface_group_setup($ifgroupentry); + + header("Location: interfaces_groups.php"); + exit; + } else { + $pconfig['descr'] = $_POST['descr']; + $pconfig['members'] = $members; + } +} + +include("head.inc"); + +if ($input_errors) { + print_input_errors($input_errors); +} + +?> +<div id="inputerrors"></div> +<?php +$tab_array = array(); +$tab_array[0] = array(gettext("Interface assignments"), false, "interfaces_assign.php"); +$tab_array[1] = array(gettext("Interface Groups"), true, "interfaces_groups.php"); +$tab_array[2] = array(gettext("Wireless"), false, "interfaces_wireless.php"); +$tab_array[3] = array(gettext("VLANs"), false, "interfaces_vlan.php"); +$tab_array[4] = array(gettext("QinQs"), false, "interfaces_qinq.php"); +$tab_array[5] = array(gettext("PPPs"), false, "interfaces_ppps.php"); +$tab_array[7] = array(gettext("GRE"), false, "interfaces_gre.php"); +$tab_array[8] = array(gettext("GIF"), false, "interfaces_gif.php"); +$tab_array[9] = array(gettext("Bridges"), false, "interfaces_bridge.php"); +$tab_array[10] = array(gettext("LAGG"), false, "interfaces_lagg.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); +$form = new Form; +$section = new Form_Section('Interface Group Edit'); + +$section->addInput(new Form_Input( + 'ifname', + 'Group Name', + 'text', + $pconfig['ifname'], + ['placeholder' => 'Group Name'] +))->setWidth(6)->setHelp('No numbers or spaces are allowed. '. + 'Only characters in a-zA-Z'); + +$section->addInput(new Form_Input( + 'descr', + 'Group Description', + 'text', + $pconfig['descr'], + ['placeholder' => 'Group Description'] +))->setWidth(6)->setHelp('You may enter a group decsription '. + 'here for your reference (not parsed)'); + +$section->addInput(new Form_Select( + 'members[]', + 'Group Members', + explode(' ', $pconfig['members']), + $interface_list, + true +))->setWidth(6)->setHelp('NOTE: Rules for WAN type '. + 'interfaces in groups do not contain the reply-to mechanism upon which '. + 'Multi-WAN typically relies. '. + '<a href="https://doc.pfsense.org/index.php/ifgroups">More Information</a>'); + +if (isset($id) && $a_ifgroups[$id]) { + $form->addGlobal(new Form_Input( + 'id', + 'id', + 'hidden', + $id + )); +} + +$form->add($section); +print $form; + +unset($interface_list); +unset($interface_list_disabled); +include("fend.inc"); +?> diff --git a/src/usr/local/www/interfaces_lagg.php b/src/usr/local/www/interfaces_lagg.php new file mode 100644 index 0000000..6724e1e --- /dev/null +++ b/src/usr/local/www/interfaces_lagg.php @@ -0,0 +1,154 @@ +<?php +/* $Id$ */ +/* + interfaces_lagg.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2008 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-lagg +##|*NAME=Interfaces: LAGG: page +##|*DESCR=Allow access to the 'Interfaces: LAGG' page. +##|*MATCH=interfaces_lagg.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['laggs']['lagg'])) { + $config['laggs']['lagg'] = array(); +} + +$a_laggs = &$config['laggs']['lagg'] ; + +function lagg_inuse($num) { + global $config, $a_laggs; + + $iflist = get_configured_interface_list(false, true); + foreach ($iflist as $if) { + if ($config['interfaces'][$if]['if'] == $a_laggs[$num]['laggif']) { + return true; + } + } + + if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) { + foreach ($config['vlans']['vlan'] as $vlan) { + if ($vlan['if'] == $a_laggs[$num]['laggif']) { + return true; + } + } + } + return false; +} + +if ($_GET['act'] == "del") { + if (!isset($_GET['id'])) { + $input_errors[] = gettext("Wrong parameters supplied"); + } else if (empty($a_laggs[$_GET['id']])) { + $input_errors[] = gettext("Wrong index supplied"); + /* check if still in use */ + } else if (lagg_inuse($_GET['id'])) { + $input_errors[] = gettext("This LAGG interface cannot be deleted because it is still being used."); + } else { + mwexec_bg("/sbin/ifconfig " . $a_laggs[$_GET['id']]['laggif'] . " destroy"); + unset($a_laggs[$_GET['id']]); + + write_config(); + + header("Location: interfaces_lagg.php"); + exit; + } +} + +$pgtitle = array(gettext("Interfaces"), gettext("LAGG")); +$shortcut_section = "interfaces"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +$tab_array = array(); +$tab_array[] = array(gettext("Interface assignments"), false, "interfaces_assign.php"); +$tab_array[] = array(gettext("Interface Groups"), false, "interfaces_groups.php"); +$tab_array[] = array(gettext("Wireless"), false, "interfaces_wireless.php"); +$tab_array[] = array(gettext("VLANs"), false, "interfaces_vlan.php"); +$tab_array[] = array(gettext("QinQs"), false, "interfaces_qinq.php"); +$tab_array[] = array(gettext("PPPs"), false, "interfaces_ppps.php"); +$tab_array[] = array(gettext("GRE"), false, "interfaces_gre.php"); +$tab_array[] = array(gettext("GIF"), false, "interfaces_gif.php"); +$tab_array[] = array(gettext("Bridges"), false, "interfaces_bridge.php"); +$tab_array[] = array(gettext("LAGG"), true, "interfaces_lagg.php"); +display_top_tabs($tab_array); +?> +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Interface"); ?></th> + <th><?=gettext("Members"); ?></th> + <th><?=gettext("Description"); ?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php + +$i = 0; + +foreach ($a_laggs as $lagg) { +?> + <tr> + <td> + <?=htmlspecialchars(strtoupper($lagg['laggif']))?> + </td> + <td> + <?=htmlspecialchars($lagg['members'])?> + </td> + <td> + <?=htmlspecialchars($lagg['descr'])?> + </td> + <td> + <a href="interfaces_lagg_edit.php?id=<?=$i?>" class="btn btn-default btn-xs"><?=gettext("Edit")?></a> + <a href="interfaces_lagg.php?act=del&id=<?=$i?>" class="btn btn-danger btn-xs"><?=gettext("Delete")?></a> + </td> + </tr> +<?php + $i++; +} +?> + </tbody> + </table> + + <nav class="action-buttons"> + <a href="interfaces_lagg_edit.php" class="btn btn-success"><?=gettext("Add")?></a> + </nav> +</div> +<?php +include("foot.inc"); diff --git a/src/usr/local/www/interfaces_lagg_edit.php b/src/usr/local/www/interfaces_lagg_edit.php new file mode 100644 index 0000000..3a03172 --- /dev/null +++ b/src/usr/local/www/interfaces_lagg_edit.php @@ -0,0 +1,262 @@ +<?php +/* $Id$ */ +/* + interfaces_lagg_edit.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2008 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-lagg-edit +##|*NAME=Interfaces: LAGG: Edit page +##|*DESCR=Allow access to the 'Interfaces: LAGG: Edit' page. +##|*MATCH=interfaces_lagg_edit.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['laggs']['lagg'])) + $config['laggs']['lagg'] = array(); + +$a_laggs = &$config['laggs']['lagg']; + +$portlist = get_interface_list(); +$laggprotos = array("none", "lacp", "failover", "fec", "loadbalance", "roundrobin"); +$laggprotosuc = array("NONE", "LACP", "FAILOVER", "FEC", "LOADBALANCE", "ROUNDROBIN"); + +$protohelp = +'<ul>' . + '<li>' . + '<strong>' . gettext($laggprotos[0]) . '</strong><br />' . + gettext('This protocol is intended to do nothing: it disables any ' . + 'traffic without disabling the lagg interface itself') . + '</li>' . + '<li>' . + '<strong>' . gettext($laggprotos[1]) . '</strong><br />' . + gettext('Supports the IEEE 802.3ad Link Aggregation Control Protocol ' . + '(LACP) and the Marker Protocol. LACP will negotiate a set ' . + 'of aggregable links with the peer in to one or more Link ' . + 'Aggregated Groups. Each LAG is composed of ports of the ' . + 'same speed, set to full-duplex operation. The traffic will ' . + 'be balanced across the ports in the LAG with the greatest ' . + 'total speed, in most cases there will only be one LAG which ' . + 'contains all ports. In the event of changes in physical ' . + 'connectivity, Link Aggregation will quickly converge to a ' . + 'new configuration.') . + '</li>' . + '<li>' . + '<strong>' . gettext($laggprotos[2]) . '</strong><br />' . + gettext('Sends and receives traffic only through the master port. If ' . + 'the master port becomes unavailable, the next active port is ' . + 'used. The first interface added is the master port; any ' . + 'interfaces added after that are used as failover devices.') . + '</li>' . + '<li>' . + '<strong>' . gettext($laggprotos[3]) . '</strong><br />' . + gettext('Supports Cisco EtherChannel. This is a static setup and ' . + 'does not negotiate aggregation with the peer or exchange ' . + 'frames to monitor the link.') . + '</li>' . + '<li>' . + '<strong>' . gettext($laggprotos[4]) . '</strong><br />' . + gettext('Balances outgoing traffic across the active ports based on ' . + 'hashed protocol header information and accepts incoming ' . + 'traffic from any active port. This is a static setup and ' . + 'does not negotiate aggregation with the peer or exchange ' . + 'frames to monitor the link. The hash includes the Ethernet ' . + 'source and destination address, and, if available, the VLAN ' . + 'tag, and the IP source and destination address') . + '</li>' . + '<li>' . + '<strong>' . gettext($laggprotos[5]) . '</strong><br />' . + gettext('Distributes outgoing traffic using a round-robin scheduler ' . + 'through all active ports and accepts incoming traffic from ' . + 'any active port') . + '</li>' . +'</ul>'; + +$realifchecklist = array(); +/* add LAGG interfaces */ +if (is_array($config['laggs']['lagg']) && count($config['laggs']['lagg'])) { + foreach ($config['laggs']['lagg'] as $lagg) { + unset($portlist[$lagg['laggif']]); + $laggiflist = explode(",", $lagg['members']); + foreach ($laggiflist as $tmpif) { + $realifchecklist[get_real_interface($tmpif)] = $tmpif; + } + } +} + +$checklist = get_configured_interface_list(false, true); + +foreach ($checklist as $tmpif) { + $realifchecklist[get_real_interface($tmpif)] = $tmpif; +} + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} + +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_laggs[$id]) { + $pconfig['laggif'] = $a_laggs[$id]['laggif']; + $pconfig['members'] = $a_laggs[$id]['members']; + $laggiflist = explode(",", $a_laggs[$id]['members']); + foreach ($laggiflist as $tmpif) { + unset($realifchecklist[get_real_interface($tmpif)]); + } + $pconfig['proto'] = $a_laggs[$id]['proto']; + $pconfig['descr'] = $a_laggs[$id]['descr']; +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "members proto"); + $reqdfieldsn = array(gettext("Member interfaces"), gettext("Lagg protocol")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (is_array($_POST['members'])) { + foreach ($_POST['members'] as $member) { + if (!does_interface_exist($member)) { + $input_errors[] = gettext("Interface supplied as member is invalid"); + } + } + } else if (!does_interface_exist($_POST['members'])) { + $input_errors[] = gettext("Interface supplied as member is invalid"); + } + + if (!in_array($_POST['proto'], $laggprotos)) { + $input_errors[] = gettext("Protocol supplied is invalid"); + } + + if (!$input_errors) { + $lagg = array(); + $lagg['members'] = implode(',', $_POST['members']); + $lagg['descr'] = $_POST['descr']; + $lagg['laggif'] = $_POST['laggif']; + $lagg['proto'] = $_POST['proto']; + if (isset($id) && $a_laggs[$id]) { + $lagg['laggif'] = $a_laggs[$id]['laggif']; + } + + $lagg['laggif'] = interface_lagg_configure($lagg); + if ($lagg['laggif'] == "" || !stristr($lagg['laggif'], "lagg")) { + $input_errors[] = gettext("Error occurred creating interface, please retry."); + } else { + if (isset($id) && $a_laggs[$id]) { + $a_laggs[$id] = $lagg; + } else { + $a_laggs[] = $lagg; + } + + write_config(); + + $confif = convert_real_interface_to_friendly_interface_name($lagg['laggif']); + if ($confif != "") + interface_configure($confif); + + header("Location: interfaces_lagg.php"); + exit; + } + } +} + +function build_member_list() { + global $pconfig, $portlist, $realifchecklist; + + $memberlist = array('list' => array(), + 'selected' => array()); + + $members_array = explode(',', $pconfig['members']); + foreach ($portlist as $ifn => $ifinfo) { + if (array_key_exists($ifn, $realifchecklist)) + continue; + + $memberlist['list'][$ifn] = $ifn . '(' . $ifinfo['mac'] . ')'; + + if (stristr($pconfig['members'], $ifn)) + array_push($memberlist['selected'], $ifn); + } + + return($memberlist); +} + +$pgtitle = array(gettext("Interfaces"),gettext("LAGG"),gettext("Edit")); +$shortcut_section = "interfaces"; +include("head.inc"); +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('LAGG Configuration'); + +$memberslist = build_member_list(); + +$section->addInput(new Form_Select( + 'members[]', + 'Parent Interfaces', + $memberslist['selected'], + $memberslist['list'], + true // Allow multiples +))->setHelp('Choose the members that will be used for the link aggregation.'); + +$section->addInput(new Form_Select( + 'proto', + 'LAGG Protocol', + $pconfig['proto'], + array_combine($laggprotos, $laggprotosuc) +))->setHelp($protohelp); + +$section->addInput(new Form_Input( + 'laggif', + null, + 'hidden', + $pconfig['laggif'] +)); + +if (isset($id) && $a_laggs[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/interfaces_ppps.php b/src/usr/local/www/interfaces_ppps.php new file mode 100644 index 0000000..966a6ca --- /dev/null +++ b/src/usr/local/www/interfaces_ppps.php @@ -0,0 +1,153 @@ +<?php +/* $Id$ */ +/* + interfaces_ppps.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-ppps +##|*NAME=Interfaces: ppps page +##|*DESCR=Allow access to the 'Interfaces: ppps' page. +##|*MATCH=interfaces_ppps.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); + +function ppp_inuse($num) { + global $config, $g; + + $iflist = get_configured_interface_list(false, true); + if (!is_array($config['ppps']['ppp'])) { + return false; + } + + foreach ($iflist as $if) { + if ($config['interfaces'][$if]['if'] == $config['ppps']['ppp'][$num]['if']) { + return true; + } + } + + return false; +} + +if ($_GET['act'] == "del") { + /* check if still in use */ + if (ppp_inuse($_GET['id'])) { + $input_errors[] = gettext("This point-to-point link cannot be deleted because it is still being used as an interface."); + } elseif (is_array($config['ppps']['ppp']) && is_array($config['ppps']['ppp'][$_GET['id']])) { + + unset($config['ppps']['ppp'][$_GET['id']]['pppoe-reset-type']); + handle_pppoe_reset($config['ppps']['ppp'][$_GET['id']]); + unset($config['ppps']['ppp'][$_GET['id']]); + write_config(); + header("Location: interfaces_ppps.php"); + exit; + } +} + +if (!is_array($config['ppps']['ppp'])) { + $config['ppps']['ppp'] = array(); +} +$a_ppps = $config['ppps']['ppp']; + +$pgtitle = array(gettext("Interfaces"),gettext("PPPs")); +$shortcut_section = "interfaces"; +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("Interface assignments"), false, "interfaces_assign.php"); +$tab_array[] = array(gettext("Interface Groups"), false, "interfaces_groups.php"); +$tab_array[] = array(gettext("Wireless"), false, "interfaces_wireless.php"); +$tab_array[] = array(gettext("VLANs"), false, "interfaces_vlan.php"); +$tab_array[] = array(gettext("QinQs"), false, "interfaces_qinq.php"); +$tab_array[] = array(gettext("PPPs"), true, "interfaces_ppps.php"); +$tab_array[] = array(gettext("GRE"), false, "interfaces_gre.php"); +$tab_array[] = array(gettext("GIF"), false, "interfaces_gif.php"); +$tab_array[] = array(gettext("Bridges"), false, "interfaces_bridge.php"); +$tab_array[] = array(gettext("LAGG"), false, "interfaces_lagg.php"); +display_top_tabs($tab_array); +?> +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Interface"); ?></th> + <th><?=gettext("Interface(s)/Port(s)"); ?></th> + <th><?=gettext("Description"); ?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php + +$i = 0; + +foreach ($a_ppps as $id => $ppp) { +?> + <tr> + <td> + <?=htmlspecialchars($ppp['if'])?> + </td> + <td> +<?php + $portlist = explode(",", $ppp['ports']); + foreach ($portlist as $portid => $port) { + if ($port != get_real_interface($port) && $ppp['type'] != "ppp") + $portlist[$portid] = convert_friendly_interface_to_friendly_descr($port); + } + echo htmlspecialchars(implode(",", $portlist)); +?> + </td> + <td> + <?=htmlspecialchars($ppp['descr'])?> + </td> + <td> + <a href="interfaces_ppps_edit.php?id=<?=$i?>" class="btn btn-default btn-xs"><?=gettext("Edit")?></a> + <a href="interfaces_ppps.php?act=del&id=<?=$i?>" class="btn btn-danger btn-xs"><?=gettext("Delete")?></a> + </td> + </tr> +<?php + $i++; +} +?> + </tbody> + </table> + + <nav class="action-buttons"> + <a href="interfaces_ppps_edit.php" class="btn btn-success"><?=gettext("Add")?></a> + </nav> +</div> +<?php +include("foot.inc"); + diff --git a/src/usr/local/www/interfaces_ppps_edit.php b/src/usr/local/www/interfaces_ppps_edit.php new file mode 100644 index 0000000..62363a7 --- /dev/null +++ b/src/usr/local/www/interfaces_ppps_edit.php @@ -0,0 +1,1127 @@ +<?php +/* $Id$ */ +/* + interfaces_ppps_edit.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + Copyright (C) 2010 Gabriel B. <gnoahb@gmail.com>. + All rights reserved. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-ppps-edit +##|*NAME=Interfaces: PPPs: Edit page +##|*DESCR=Allow access to the 'Interfaces: PPPs: Edit' page. +##|*MATCH=interfaces_ppps_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require("functions.inc"); + +define("CRON_MONTHLY_PATTERN", "0 0 1 * *"); +define("CRON_WEEKLY_PATTERN", "0 0 * * 0"); +define("CRON_DAILY_PATTERN", "0 0 * * *"); +define("CRON_HOURLY_PATTERN", "0 * * * *"); + +if (!is_array($config['ppps']['ppp'])) { + $config['ppps']['ppp'] = array(); +} + +$a_ppps = &$config['ppps']['ppp']; + +$iflist = get_configured_interface_with_descr(); +$portlist = get_interface_list(); +$portlist = array_merge($portlist, $iflist); + +if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) { + foreach ($config['vlans']['vlan'] as $vlan) { + $portlist[$vlan['vlanif']] = $vlan; + } +} + +if($_GET && $_GET['type']) + $pconfig['type'] = $_GET['type']; + +if($_GET && $_GET['country']) + $pconfig['country'] = $_GET['country']; + +if($_GET && $_GET['provider']) + $pconfig['provider'] = $_GET['provider']; + +if (is_numericint($_GET['id'])) + $id = $_GET['id']; + +if (isset($_POST['id']) && is_numericint($_POST['id'])) + $id = $_POST['id']; + +if (isset($id) && $a_ppps[$id]) { + $pconfig['ptpid'] = $a_ppps[$id]['ptpid']; + $pconfig['type'] = $a_ppps[$id]['type']; + //$pconfig['if'] = $a_ppps[$id]['if']; + $pconfig['interfaces'] = $a_ppps[$id]['ports']; + $pconfig['username'] = $a_ppps[$id]['username']; + $pconfig['password'] = base64_decode($a_ppps[$id]['password']); + if (isset($a_ppps[$id]['ondemand'])) { + $pconfig['ondemand'] = true; + } + $pconfig['idletimeout'] = $a_ppps[$id]['idletimeout']; + $pconfig['uptime'] = $a_ppps[$id]['uptime']; + $pconfig['descr'] = $a_ppps[$id]['descr']; + $pconfig['bandwidth'] = explode(",",$a_ppps[$id]['bandwidth']); + $pconfig['mtu'] = explode(",",$a_ppps[$id]['mtu']); + $pconfig['mru'] = explode(",",$a_ppps[$id]['mru']); + $pconfig['mrru'] = explode(",",$a_ppps[$id]['mrru']); + + if (isset($a_ppps[$id]['shortseq'])) + $pconfig['shortseq'] = true; + + if (isset($a_ppps[$id]['acfcomp'])) { + $pconfig['acfcomp'] = true; + } + if (isset($a_ppps[$id]['protocomp'])) { + $pconfig['protocomp'] = true; + } + if (isset($a_ppps[$id]['vjcomp'])) { + $pconfig['vjcomp'] = true; + } + if (isset($a_ppps[$id]['tcpmssfix'])) { + $pconfig['tcpmssfix'] = true; + } + switch ($a_ppps[$id]['type']) { + case "ppp": + $pconfig['initstr'] = base64_decode($a_ppps[$id]['initstr']); + $pconfig['simpin'] = $a_ppps[$id]['simpin']; + $pconfig['pin-wait'] = $a_ppps[$id]['pin-wait']; + $pconfig['apn'] = $a_ppps[$id]['apn']; + $pconfig['apnum'] = $a_ppps[$id]['apnum']; + $pconfig['phone'] = $a_ppps[$id]['phone']; + $pconfig['connect-timeout'] = $a_ppps[$id]['connect-timeout']; + $pconfig['localip'] = explode(",", $a_ppps[$id]['localip']); + $pconfig['gateway'] = explode(",", $a_ppps[$id]['gateway']); + break; + case "l2tp": + case "pptp": + $pconfig['localip'] = explode(",", $a_ppps[$id]['localip']); + $pconfig['subnet'] = explode(",", $a_ppps[$id]['subnet']); + $pconfig['gateway'] = explode(",", $a_ppps[$id]['gateway']); + case "pppoe": + $pconfig['provider'] = $a_ppps[$id]['provider']; + if (isset($a_ppps[$id]['provider']) and empty($a_ppps[$id]['provider'])) { + $pconfig['null_service'] = true; + } + /* ================================================ */ + /* = force a connection reset at a specific time? = */ + /* ================================================ */ + + if (isset($a_ppps[$id]['pppoe-reset-type'])) { + $pconfig['pppoe-reset-type'] = $a_ppps[$id]['pppoe-reset-type']; + $itemhash = getMPDCRONSettings($a_ppps[$id]['if']); + $cronitem = $itemhash['ITEM']; + if (isset($cronitem)) { + $resetTime = "{$cronitem['minute']} {$cronitem['hour']} {$cronitem['mday']} {$cronitem['month']} {$cronitem['wday']}"; + } else { + $resetTime = NULL; + } + + if ($a_ppps[$id]['pppoe-reset-type'] == "custom") { + $resetTime_a = explode(" ", $resetTime); + $pconfig['pppoe_pr_custom'] = true; + $pconfig['pppoe_resetminute'] = $resetTime_a[0]; + $pconfig['pppoe_resethour'] = $resetTime_a[1]; + /* just initialize $pconfig['pppoe_resetdate'] if the + * corresponding item contains appropriate numeric values. + */ + if ($resetTime_a[2] <> "*" && $resetTime_a[3] <> "*") { + $pconfig['pppoe_resetdate'] = "{$resetTime_a[3]}/{$resetTime_a[2]}/" . date("Y"); + } + } else if ($a_ppps[$id]['pppoe-reset-type'] == "preset") { + $pconfig['pppoe_pr_preset'] = true; + + switch ($resetTime) { + case CRON_MONTHLY_PATTERN: + $pconfig['pppoe_monthly'] = true; + break; + case CRON_WEEKLY_PATTERN: + $pconfig['pppoe_weekly'] = true; + break; + case CRON_DAILY_PATTERN: + $pconfig['pppoe_daily'] = true; + break; + case CRON_HOURLY_PATTERN: + $pconfig['pppoe_hourly'] = true; + break; + } + } + } + break; + } + +} else { + $pconfig['ptpid'] = interfaces_ptpid_next(); +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* okay first of all, cause we are just hiding the PPPoE HTML + * fields related to PPPoE resets, we are going to unset $_POST + * vars, if the reset feature should not be used. Otherwise the + * data validation procedure below, may trigger a false error + * message. + */ + if (empty($_POST['pppoe-reset-type'])) { + unset($_POST['pppoe_resethour']); + unset($_POST['pppoe_resetminute']); + unset($_POST['pppoe_resetdate']); + unset($_POST['pppoe_pr_preset_val']); + } + + /* input validation */ + switch ($_POST['type']) { + case "ppp": + $reqdfields = explode(" ", "interfaces phone"); + $reqdfieldsn = array(gettext("Link Interface(s)"), gettext("Phone Number")); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + break; + case "pppoe": + if ($_POST['ondemand']) { + $reqdfields = explode(" ", "interfaces username passwordfld ondemand idletimeout"); + $reqdfieldsn = array(gettext("Link Interface(s)"), gettext("Username"), gettext("Password"), gettext("Dial on demand"), gettext("Idle timeout value")); + } else { + $reqdfields = explode(" ", "interfaces username passwordfld"); + $reqdfieldsn = array(gettext("Link Interface(s)"), gettext("Username"), gettext("Password")); + } + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + break; + case "l2tp": + case "pptp": + if ($_POST['ondemand']) { + $reqdfields = explode(" ", "interfaces username passwordfld localip subnet gateway ondemand idletimeout"); + $reqdfieldsn = array(gettext("Link Interface(s)"), gettext("Username"), gettext("Password"), gettext("Local IP address"), gettext("Subnet"), gettext("Remote IP address"), gettext("Dial on demand"), gettext("Idle timeout value")); + } else { + $reqdfields = explode(" ", "interfaces username passwordfld localip subnet gateway"); + $reqdfieldsn = array(gettext("Link Interface(s)"), gettext("Username"), gettext("Password"), gettext("Local IP address"), gettext("Subnet"), gettext("Remote IP address")); + } + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + break; + default: + $input_errors[] = gettext("Please choose a Link Type."); + break; + } + if ($_POST['type'] == "ppp" && count($_POST['interfaces']) > 1) { + $input_errors[] = gettext("Multilink connections (MLPPP) using the PPP link type is not currently supported. Please select only one Link Interface."); + } + if ($_POST['provider'] && !is_domain($_POST['provider'])) { + $input_errors[] = gettext("The Service name contains invalid characters."); + } + if ($_POST['provider'] && $_POST['null_service']) { + $input_errors[] = gettext("Do not specify both a Service name and a NULL Service name."); + } + if (($_POST['idletimeout'] != "") && !is_numericint($_POST['idletimeout'])) { + $input_errors[] = gettext("The idle timeout value must be an integer."); + } + if ($_POST['pppoe-reset-type'] == "custom" && $_POST['pppoe_resethour'] <> "" && !is_numericint($_POST['pppoe_resethour']) && + $_POST['pppoe_resethour'] >= 0 && $_POST['pppoe_resethour'] <=23) { + $input_errors[] = gettext("A valid PPPoE reset hour must be specified (0-23)."); + } + if ($_POST['pppoe-reset-type'] == "custom" && $_POST['pppoe_resetminute'] <> "" && !is_numericint($_POST['pppoe_resetminute']) && + $_POST['pppoe_resetminute'] >= 0 && $_POST['pppoe_resetminute'] <=59) { + $input_errors[] = gettext("A valid PPPoE reset minute must be specified (0-59)."); + } + if ($_POST['pppoe-reset-type'] == "custom" && $_POST['pppoe_resetdate'] <> "" && !is_numeric(str_replace("/", "", $_POST['pppoe_resetdate']))) { + $input_errors[] = gettext("A valid PPPoE reset date must be specified (mm/dd/yyyy)."); + } + if ($_POST['pppoe-reset-type'] == "custom" && $_POST['pppoe_resetdate'] <> "" && is_numeric(str_replace("/", "", $_POST['pppoe_resetdate']))) { + $date_nums = explode("/", $_POST['pppoe_resetdate']); + if ($date_nums[0] < 1 || $date_nums[0] > 12) { + $input_errors[] = gettext("A valid PPPoE reset month must be specified (1-12) in the Custom PPPoE Periodic reset fields."); + } + if ($date_nums[1] < 1 || $date_nums[1] > 31) { + $input_errors[] = gettext("A valid PPPoE reset day of month must be specified (1-31) in the Custom PPPoE Periodic reset fields. No checks are done on valid # of days per month"); + } + if ($date_nums[2] < date("Y")) { + $input_errors[] = gettext("A valid PPPoE reset year must be specified. Don't select a year in the past!"); + } + } + + if (is_array($_POST['interfaces'])) { + foreach ($_POST['interfaces'] as $iface) { + if ($_POST['localip'][$iface] && !is_ipaddr($_POST['localip'][$iface])) { + $input_errors[] = sprintf(gettext("A valid local IP address must be specified for %s."), $iface); + } + if ($_POST['gateway'][$iface] && !is_ipaddr($_POST['gateway'][$iface]) && !is_hostname($_POST['gateway'][$iface])) { + $input_errors[] = sprintf(gettext("A valid gateway IP address OR hostname must be specified for %s."), $iface); + } + if ($_POST['bandwidth'][$iface] && !is_numericint($_POST['bandwidth'][$iface])) { + $input_errors[] = sprintf(gettext("The bandwidth value for %s must be an integer."), $iface); + } + if ($_POST['mtu'][$iface] && ($_POST['mtu'][$iface] < 576)) { + $input_errors[] = sprintf(gettext("The MTU for %s must be greater than 576 bytes."), $iface); + } + if ($_POST['mru'][$iface] && ($_POST['mru'][$iface] < 576)) { + $input_errors[] = sprintf(gettext("The MRU for %s must be greater than 576 bytes."), $iface); + } + } + } + +/* + foreach ($a_ppps as $ppp) { + if (isset($id) && ($a_ppps[$id]) && ($a_ppps[$id] === $ppp)) { + continue; + } + + if ($ppp['serialport'] == $_POST['serialport']) { + $input_errors[] = "Serial port is in use"; + break; + } + } +*/ + + if (!$input_errors) { + $ppp = array(); + $ppp['ptpid'] = $_POST['ptpid']; + $ppp['type'] = $_POST['type']; + $ppp['if'] = $ppp['type'].$ppp['ptpid']; + $ppp['ports'] = implode(',', $_POST['interfaces']); + $ppp['username'] = $_POST['username']; + $ppp['password'] = base64_encode($_POST['passwordfld']); + $ppp['ondemand'] = $_POST['ondemand'] ? true : false; + if (!empty($_POST['idletimeout'])) { + $ppp['idletimeout'] = $_POST['idletimeout']; + } else { + unset($ppp['idletimeout']); + } + $ppp['uptime'] = $_POST['uptime'] ? true : false; + if (!empty($_POST['descr'])) { + $ppp['descr'] = $_POST['descr']; + } else { + unset($ppp['descr']); + } + + // Loop through fields associated with an individual link/port and make an array of the data + $port_fields = array("localip", "gateway", "subnet", "bandwidth", "mtu", "mru", "mrru"); + foreach ($_POST['interfaces'] as $iface) { + foreach ($port_fields as $field_label) { + if (isset($_POST[$field_label][$iface])) { + $port_data[$field_label][] = $_POST[$field_label][$iface]; + } + } + } + + switch ($_POST['type']) { + case "ppp": + if (!empty($_POST['initstr'])) { + $ppp['initstr'] = base64_encode($_POST['initstr']); + } else { + unset($ppp['initstr']); + } + if (!empty($_POST['simpin'])) { + $ppp['simpin'] = $_POST['simpin']; + $ppp['pin-wait'] = $_POST['pin-wait']; + } else { + unset($ppp['simpin']); + unset($ppp['pin-wait']); + } + + if (!empty($_POST['apn'])) { + $ppp['apn'] = $_POST['apn']; + $ppp['apnum'] = $_POST['apnum']; + } else { + unset($ppp['apn']); + unset($ppp['apnum']); + } + + $ppp['phone'] = $_POST['phone']; + $ppp['localip'] = implode(',', $port_data['localip']); + $ppp['gateway'] = implode(',', $port_data['gateway']); + if (!empty($_POST['connect-timeout'])) { + $ppp['connect-timeout'] = $_POST['connect-timeout']; + } else { + unset($ppp['connect-timeout']); + } + break; + case "pppoe": + if (!empty($_POST['provider'])) { + $ppp['provider'] = $_POST['provider']; + } else { + unset($ppp['provider']); + $ppp['provider'] = $_POST['null_service'] ? true : false; + } + if (!empty($_POST['pppoe-reset-type'])) { + $ppp['pppoe-reset-type'] = $_POST['pppoe-reset-type']; + } else { + unset($ppp['pppoe-reset-type']); + } + + break; + case "pptp": + case "l2tp": + $ppp['localip'] = implode(',', $port_data['localip']); + $ppp['subnet'] = implode(',', $port_data['subnet']); + $ppp['gateway'] = implode(',', $port_data['gateway']); + break; + default: + break; + + } + + $ppp['shortseq'] = $_POST['shortseq'] ? true : false; + $ppp['acfcomp'] = $_POST['acfcomp'] ? true : false; + $ppp['protocomp'] = $_POST['protocomp'] ? true : false; + $ppp['vjcomp'] = $_POST['vjcomp'] ? true : false; + $ppp['tcpmssfix'] = $_POST['tcpmssfix'] ? true : false; + $ppp['bandwidth'] = implode(',', $port_data['bandwidth']); + if (is_array($port_data['mtu'])) { + $ppp['mtu'] = implode(',', $port_data['mtu']); + } + if (is_array($port_data['mru'])) { + $ppp['mru'] = implode(',', $port_data['mru']); + } + if (is_array($port_data['mrru'])) { + $ppp['mrru'] = implode(',', $port_data['mrru']); + } + + /* handle_pppoe_reset is called here because if user changes Link Type from PPPoE to another type we + must be able to clear the config data in the <cron> section of config.xml if it exists + */ + handle_pppoe_reset($_POST); + + if (isset($id) && $a_ppps[$id]) { + $a_ppps[$id] = $ppp; + } else { + $a_ppps[] = $ppp; + } + + write_config(); + configure_cron(); + + foreach ($iflist as $pppif => $ifdescr) { + if ($config['interfaces'][$pppif]['if'] == $ppp['if']) { + interface_ppps_configure($pppif); + } + } + header("Location: interfaces_ppps.php"); + exit; + } +} // end if ($_POST) + +$closehead = false; +$pgtitle = array(gettext("Interfaces"), gettext("PPPs"), gettext("Edit")); +$shortcut_section = "interfaces"; +include("head.inc"); + +$types = array("select" => gettext("Select"), "ppp" => "PPP", "pppoe" => "PPPoE", "pptp" => "PPTP", "l2tp" => "L2TP"/*, "tcp" => "TCP", "udp" => "UDP"*/); + +$serviceproviders_xml = "/usr/local/share/mobile-broadband-provider-info/serviceproviders.xml"; +$serviceproviders_contents = file_get_contents($serviceproviders_xml); +$serviceproviders_attr = xml2array($serviceproviders_contents,1,"attr"); + +$serviceproviders = &$serviceproviders_attr['serviceproviders']['country']; + +function build_country_list() { + global $serviceproviders; + + $list = array(); + + // get_country_name is in pfSense-utils.inc + $country_list = get_country_name("ALL"); + + foreach($country_list as $country) { + $list[$country['code']] = $country['name']; + } + + return($list); +} + +function build_provider_list($country) { + global $serviceproviders; + + $list = array(); + + foreach($serviceproviders as $sp) { + if($sp['attr']['code'] == strtolower($country)) { + if(is_array($sp['provider'][0])) { + foreach($sp['provider'] as $provider) { + array_push($list, $provider['name']['value']); + } + } + } + } + + return array_combine($list, $list); +} + +function build_plans_list($country, $provider) { + global $serviceproviders; + + $list = array(); + + foreach($serviceproviders as $sprecord) { + if($sprecord['attr']['code'] == strtolower($country)) { + foreach($sprecord['provider'] as $sp) { + if(strtolower($sp['name']['value']) == strtolower($provider)) { + if(array_key_exists('gsm', $sp)) { + + if(array_key_exists('attr',$sp['gsm']['apn'])) { + $name = ($sp['gsm']['apn']['name'] ? $sp['gsm']['apn']['name'] : $sp['name']['value']); + echo $name . ":" . $sp['gsm']['apn']['attr']['value']; + } else { + foreach($sp['gsm']['apn'] as $apn_info) { + $name = ($apn_info['name']['value'] ? $apn_info['name']['value'] : $apn_info['gsm']['apn']['name']); + + $key = $apn_info['attr']['value']; + $list[$key] = $name . ($key ? ' ':'') . '- ' . $key; + } + } + } + + if(array_key_exists('cdma',$sp)) { + $name = $sp['cdma']['name']['value'] ? $sp['cdma']['name']['value']:$sp['name']['value']; + $key = 'CDMA'; + $list[$key] = $name . ' - ' . $key; + } + } + } + } + } + + return($list); +} + +$port_count = 0; +$serport_count = 0; + +function build_link_list() { + global $pconfig, $portlist, $port_count, $serport_count; + + $linklist = array('list' => array(), + 'selected' => array()); + + $selected_ports = explode(',',$pconfig['interfaces']); + + if (!is_dir("/var/spool/lock")) + mwexec("/bin/mkdir -p /var/spool/lock"); + + if($pconfig['type'] == 'ppp') { + $serialports = glob("/dev/cua?[0-9]{,.[0-9]}", GLOB_BRACE); + //DEBUG + $serialports = glob("/dev/tty?[0-9]{,.[0-9]}", GLOB_BRACE); + + $serport_count = 0; + + foreach ($serialports as $port) { + $serport_count++; + + $linklist['list'][$port] = trim($port); + + if (in_array($port, $selected_ports)) + array_push($linklist['selected'], $port); + } + } + else { + $port_count = 0; + foreach ($portlist as $ifn => $ifinfo){ + $port_count++; + $string = ""; + + if (is_array($ifinfo)) { + $string .= $ifn; + if ($ifinfo['mac']) + $string .= " ({$ifinfo['mac']})"; + } else + $string .= $ifinfo; + + $linklist['list'][$ifn] = $string; + + if (in_array($ifn, $selected_ports)) + array_push($linklist['selected'], $ifn); + } + + if($serport_count > $port_count) + $port_count=$serport_count; + } + + return($linklist); +} + +if ($input_errors) + print_input_errors($input_errors); + +$linkparamstr = gettext('Bandwidth is set only for MLLP conncetions and when links have diferent bandwidths' . '<br />' . + 'MTU defaults to 1492' . '<br />' . + 'MRU will be auto-negotiated by default' . '<br />' . + 'Set only for MLLP conncetions. MRRU will be auto-negotiated by default.'); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('PPPs Configuration'); + +$section->addInput(new Form_Select( + 'type', + 'Link Type', + $pconfig['type'], + $types +)); + +$linklist = build_link_list(); + +$section->addInput(new Form_Select( + 'interfaces', + 'Link Interface(s)', + $linklist['selected'], + $linklist['list'], + true // Allow multiples +))->addClass('interfaces')->setHelp('Select at least two interfaces for Multilink (MLPPP) connections.'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference. Description will appear in the "Interfaces Assign" select lists.'); + +$section->addInput(new Form_Select( + 'country', + 'Country', + $pconfig['country'], + build_country_list() +)); + +$section->addInput(new Form_Select( + 'provider', + 'Provider', + $pconfig['provider'], + build_provider_list($pconfig['country']) +)); + +$section->addInput(new Form_Select( + 'providerplan', + 'Plan', + $pconfig['providerplan'], + build_plans_list($pconfig['country'], $pconfig['provider']) +))->setHelp('Select to fill in data for your service provider.'); + +$section->addInput(new Form_Input( + 'username', + 'Username', + 'text', + $pconfig['username'] +)); + +$section->addInput(new Form_Input( + 'passwordfld', + 'Password', + 'password', + $pconfig['passwordfld'] +)); + +// These elements arehidden by default, and un-hidden in Javascript +if($pconfig['type'] == 'pptp' || $pconfig['type'] == 'l2tp') { + $j = 0; + foreach($linklist['list'] as $ifnm =>$nm) { + + $group = new Form_Group('IP/Gateway (' . $ifnm . ')'); + + $group->add(new Form_IpAddress( + 'localiplabel' . $j, + null, + $pconfig['localip'][$j] + ))->addMask('subnet' . $j, $pconfig['subnet'][$j], 31)->setHelp('IP Address'); + + $group->add(new Form_Input( + 'gateway' . $j, + null, + 'password', + $pconfig['gateway'][$j] + ))->setHelp('IP or Hostname'); + + $j++; + + $group->addClass('localip')->addClass('localip' . $ifnm); + $section->add($group); + } +} + +if($pconfig['type'] == 'ppp') { + $section->addInput(new Form_Input( + 'phone', + 'Phone number', + 'text', + $pconfig['phone'] + ))->setHelp('Typically *99# for GSM networks and #777 for CDMA networks'); +} + +$section->addInput(new Form_Input( + 'apn', + 'Access Point Name (APN)', + 'text', + $pconfig['apn'] +)); + +$section->addInput(new Form_Input( + 'apnum', + 'APN number (optional)', + 'text', + $pconfig['apnum'] +))->setHelp('Defaults to 1 if APN is set. Ignored if no APN is set.'); + +$section->addInput(new Form_Input( + 'simpin', + 'SIM PIN', + 'text', + $pconfig['simpin'] +)); + +$section->addInput(new Form_Input( + 'pin-wait', + 'SIM PIN wait', + 'text', + $pconfig['pin-wait'] +))->setHelp('Time to wait for SIM to discover network after PIN is sent to SIM (seconds).'); + +$section->addInput(new Form_Input( + 'initstr', + 'Init string', + 'text', + $pconfig['initstr'] +))->setHelp('Enter the modem initialization string here. Do NOT include the "AT" string at the beginning of the command. ' . + 'Many modern USB 3G modems don\'t need an initialization string.'); + +$section->addInput(new Form_Input( + 'connect-timeout', + 'Connection Timeout', + 'text', + $pconfig['connect-timeout'] +))->setHelp('Enter timeout in seconds for connection to be established (sec.) Default is 45 sec.'); + +$section->addInput(new Form_Checkbox( + 'uptime', + 'Uptime logging', + 'Enable persistent logging of connection uptime. ', + $pconfig['uptime'] +))->setHelp(sprintf('Causes cumulative uptime to be recorded and displayed on the %sStatus->Interfaces%s page.', '<a href="status_interfaces.php">', '</a>')); + +$group = new Form_Group('Service name'); +$group->addClass('pppoe'); + +$group->add(new Form_Input( + 'provider', + null, + 'text', + $pconfig['provider'] +)); + +$group->add(new Form_Checkbox( + 'null_service', + null, + 'Configure NULL service name', + $pconfig['null_service'] +)); + +$group->setHelp('This field can usually be left empty. Service name will not be configured if this field is empty. ' . + 'Check the "Configure NULL" box to configure a blank Service name.'); + +$section->add($group); + +$section->addInput(new Form_Select( + 'pppoe-reset-type', + 'Periodic Reset', + $pconfig['pppoe-reset-type'], + array( + '' => 'Disabled', + 'custom' => 'Custom', + 'preset' => 'Pre-set' + ) +))->addClass('pppoe')->setHelp('Select a reset timing type'); + +$group = new Form_Group('Rest Date/Time'); +$group->addClass('pppoe-reset-date'); + +$group->add(new Form_Input( + 'pppoe_resethour', + null, + 'text', + $pconfig['pppoe_resethour'] +))->setHelp('Hour'); + +$group->add(new Form_Input( + 'pppoe_resetminute', + null, + 'text', + $pconfig['pppoe_resetminute'] +))->setHelp('Minute'); + +$group->add(new Form_Input( + 'pppoe_resetdate', + null, + 'text', + $pconfig['pppoe_resetdate'], + ['placeholder' => 'mm/dd/yyyy'] +))->setHelp('Specific date'); + +$group->setHelp('Leaving the date field empty will cause the reset to be executed each day at the time you specified in the minutes and hour fields. '); + +$section->add($group); + +$group = new Form_Group('Rest frequency'); +$group->addClass('pppoe-reset-cron'); + +$group->add(new Form_Checkbox( + 'pppoe_pr_preset_val', + null, + 'Monthly (0 0 1 * *)', + $pconfig['pppoe_monthly'], + 'monthly' +))->displayAsRadio(); + +$group->add(new Form_Checkbox( + 'pppoe_pr_preset_val', + null, + 'Weekly (0 0 * * 0)', + $pconfig['pppoe_weekly'], + 'weekly' +))->displayAsRadio(); + +$group->add(new Form_Checkbox( + 'pppoe_pr_preset_val', + null, + 'Daily (0 0 * * *)', + $pconfig['pppoe_daily'], + 'daily' +))->displayAsRadio(); + +$group->add(new Form_Checkbox( + 'pppoe_pr_preset_val', + null, + 'Hourly (0 * * * *)', + $pconfig['pppoe_hourly'], + 'hourly' +))->displayAsRadio(); + +$section->add($group); + +$btnadvanced = new Form_Button( + 'btnadvanced', + 'Show' +); + +$btnadvanced->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'Advanced options', + $btnadvanced +)); + +$form->add($section); + +$section = new Form_Section('Advanced Configuration'); +$section->addClass('sec-advanced'); // This will allow the section to be hidden/shown by calling e.g.: hideClass('advanced', true); + +$section->addInput(new Form_Checkbox( + 'ondemand', + 'Dial On Demand', + 'Enable Dial-on-Demand mode. ', + $pconfig['ondemand'] +))->setHelp('Causes the interface to operate in dial-on-demand mode. Do NOT enable if you want your link to be always up. ' . + 'The interface is configured, but the actual connection of the link is delayed until qualifying outgoing traffic is detected.'); + +$section->addInput(new Form_Input( + 'idletimeout', + 'Idle Timeout', + 'text', + $pconfig['idletimeout'] +))->setHelp('If no incoming or outgoing packets are transmitted for the entered number of seconds the connection is brought down.' . + 'When the idle timeout occurs, if the dial-on-demand option is enabled, mpd goes back into dial-on-demand mode. ' . + 'Otherwise, the interface is brought down and all associated routes removed.'); + +$section->addInput(new Form_Checkbox( + 'vjcomp', + 'Compression', + 'Disable vjcomp (compression, auto-negotiated by default).', + $pconfig['vjcomp'] +))->setHelp('Disable vjcomp(compression) (auto-negotiated by default).' . '<br />' . + 'This option enables Van Jacobson TCP header compression, which saves several bytes per TCP data packet.' . + 'This option is almost always required. Compression is not effective for TCP connections with enabled modern extensions like time ' . + 'stamping or SACK, which modify TCP options between sequential packets.'); + +$section->addInput(new Form_Checkbox( + 'tcpmssfix', + 'TCPmssFix', + 'Disable tcpmssfix (enabled by default).', + $pconfig['tcpmssfix'] +))->setHelp('Causes mpd to adjust incoming and outgoing TCP SYN segments so that the requested maximum segment size is not greater than the amount ' . + 'allowed by the interface MTU. This is necessary in many setups to avoid problems caused by routers that drop ICMP Datagram Too Big messages. Without these messages, ' . + 'the originating machine sends data, it passes the rogue router then hits a machine that has an MTU that is not big enough for the data. Because the IP Don\'t Fragment option is set, ' . + 'this machine sends an ICMP Datagram Too Big message back to the originator and drops the packet. The rogue router drops the ICMP message and the originator never ' . + 'gets to discover that it must reduce the fragment size or drop the IP Don\'t Fragment option from its outgoing data.'); + +$section->addInput(new Form_Checkbox( + 'shortseq', + 'ShortSeq', + 'Disable shortseq (auto-negotiated by default).', + $pconfig['shortseq'] +))->setHelp('This option is only meaningful if multi-link PPP is negotiated. It proscribes shorter multi-link fragment headers, saving two bytes on every frame. ' . + 'It is not necessary to disable this for connections that are not multi-link.'); + +$section->addInput(new Form_Checkbox( + 'acfcomp', + 'ACFComp', + 'Disable ACF compression (auto-negotiated by default)', + $pconfig['acfcomp'] +))->setHelp('Address and control field compression. This option only applies to asynchronous link types. It saves two bytes per frame.'); + +$section->addInput(new Form_Checkbox( + 'protocomp', + 'ProtoComp', + 'Disable Protocol compression (auto-negotiated by default)', + $pconfig['protocomp'] +))->setHelp('Protocol field compression. This option saves one byte per frame for most frames.'); + +// Display the Link parameters. We will hide this by default, then un-hide the selected ones on clicking 'Advanced' +$j = 0; + +foreach($linklist['list'] as $ifnm =>$nm) { + + $group = new Form_Group('Link Parameters (' . $ifnm . ')'); + + $group->add(new Form_Input( + 'bandwidth' . $j, + null, + 'text', + $pconfig['bandwidth'][$j] + ))->setHelp('Bandwidth'); + + $group->add(new Form_Input( + 'mtu' . $j, + null, + 'password', + $pconfig['mtu'][$j] + ))->setHelp('MTU'); + + $group->add(new Form_Input( + 'mru' . $j, + null, + 'password', + $pconfig['mru'][$j] + ))->setHelp('MRU'); + + $group->add(new Form_Input( + 'mrru' . $j, + null, + 'password', + $pconfig['mrru'][$j] + ))->setHelp('MRRU'); + + $j++; + + $section->add($group); + + $group->addClass('localip sec-advanced')->addClass('linkparam' . $ifnm); +} + +$linkparamhelp = new Form_StaticText( + null, + '<span id="linkparamhelp">' . $linkparamstr . '</span>' +); + +$section->addInput($linkparamhelp); + +if (isset($id) && $a_ppps[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$section->addInput(new Form_Input( + 'ptpid', + null, + 'hidden', + $ptpid +)); + +$form->add($section); + +print($form); + +?> + +<script> +//<![CDATA[ +events.push(function(){ + var showadvanced = false; + + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified checkbox lives so that the checkbox, its label and help text are hidden + function hideCheckbox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // Simple test in which clicking the ‘clear’ button toggles certain display elements + function setAdvVisible() { + // Update the button text and toggle showadvanced + if(showadvanced) { + $("#btnadvanced").prop('value', 'Hide'); + showadvanced = false; + } + else { + $("#btnadvanced").prop('value', 'Show'); + showadvanced = true; + } + + hideClass('sec-advanced', showadvanced); + + // The options that follow are only shown if type == 'ppp' + var ppptype = ($('#type').val() == 'ppp'); + + hideInput('apnum', showadvanced && ppptype); + hideInput('simpin', showadvanced && ppptype); + hideInput('pin-wait', showadvanced && ppptype); + hideInput('initstr', showadvanced && ppptype); + hideInput('connect-timeout', showadvanced && ppptype); + hideCheckbox('uptime', showadvanced && ppptype); + + // The options that follow are only shown if type == 'pppoe' + var pppoetype = ($('#type').val() != 'pppoe'); + + hideClass('pppoe', showadvanced || pppoetype); + hideInput('pppoe-reset-type', showadvanced || pppoetype); + + hideResetDisplay(true); + + hideInterfaces(); + } + + function hideResetDisplay(hide) { + + hideClass('pppoe-reset-date', true); + hideClass('pppoe-reset-cron', true); + + if(!hide) { + switch($('#pppoe-reset-type').val()) { + case 'custom' : + hideClass('pppoe-reset-date', false); + break; + case 'preset' : + hideClass('pppoe-reset-cron', false); + break; + } + } + } + + function hideInterfaces() { + hideClass('localip', true); + hideClass('linkparam', true); + hideInput('linkparamhelp', true); + + var selected = $('.interfaces').val(); + var length = selected.length; + + for(var i=0; i<length; i++) { + hideClass('localip' + selected[i], false); + + if(!showadvanced) { + hideClass('linkparam' + selected[i], false); + hideInput('linkparamhelp', false); + } + } + } + + // Make the ‘btnadvanced’ button a plain button, not a submit button + $("#btnadvanced").prop('type','button'); + + $("#btnadvanced").click(function() { + setAdvVisible(); + }); + + $('#pppoe-reset-type').on('change', function() { + hideResetDisplay(false); + }); + + // When interfaces changed, read the selected items and unhide the corresponding IP/Gateway controls + // Multiselect boxes must be handled by class + $('.interfaces').on('change', function() { + hideInterfaces(); + }); + + // When type, country or provider are changed, reload the page and build the new selector arrays + $('#type').on('change', function() { + window.location = 'interfaces_ppps_edit.php?id=' + $('#id').val() + '&type=' + this.value; + }); + + $('#country').on('change', function() { + window.location = 'interfaces_ppps_edit.php?id=' + $('#id').val() + '&country=' + this.value + '&type=' + $('#type').val(); + }); + + $('#provider').on('change', function() { + window.location = 'interfaces_ppps_edit.php?id=' + $('#id').val() + '&provider=' + this.value + '&type=' + $('#type').val() + '&country=' + $('#country').val(); + }); + + // Set element visibility on initial page load + setAdvVisible(); + + hideClass('linkparam', true); +}); +//]]> + +</script> +<?php + +include("foot.inc"); diff --git a/src/usr/local/www/interfaces_qinq.php b/src/usr/local/www/interfaces_qinq.php new file mode 100644 index 0000000..f68ac54 --- /dev/null +++ b/src/usr/local/www/interfaces_qinq.php @@ -0,0 +1,166 @@ +<?php +/* $Id$ */ +/* + interfaces_qinq.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2009 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/sbin/ngctl + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-qinq +##|*NAME=Interfaces: QinQ page +##|*DESCR=Allow access to the 'Interfaces: QinQ' page. +##|*MATCH=interfaces_qinq.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); + +if (!is_array($config['qinqs']['qinqentry'])) { + $config['qinqs']['qinqentry'] = array(); +} + +$a_qinqs = &$config['qinqs']['qinqentry']; + +function qinq_inuse($num) { + global $config, $a_qinqs; + + $iflist = get_configured_interface_list(false, true); + foreach ($iflist as $if) { + if ($config['interfaces'][$if]['if'] == $a_qinqs[$num]['qinqif']) { + return true; + } + } + + return false; +} + +if ($_GET['act'] == "del") { + $id = $_GET['id']; + + /* check if still in use */ + if (qinq_inuse($id)) { + $input_errors[] = gettext("This QinQ cannot be deleted because it is still being used as an interface."); + } elseif (empty($a_qinqs[$id]['vlanif']) || !does_interface_exist($a_qinqs[$id]['vlanif'])) { + $input_errors[] = gettext("QinQ interface does not exist"); + } else { + $qinq =& $a_qinqs[$id]; + + $delmembers = explode(" ", $qinq['members']); + if (count($delmembers) > 0) { + foreach ($delmembers as $tag) { + mwexec("/usr/sbin/ngctl shutdown {$qinq['vlanif']}h{$tag}:"); + } + } + mwexec("/usr/sbin/ngctl shutdown {$qinq['vlanif']}qinq:"); + mwexec("/usr/sbin/ngctl shutdown {$qinq['vlanif']}:"); + mwexec("/sbin/ifconfig {$qinq['vlanif']} destroy"); + unset($a_qinqs[$id]); + + write_config(); + + header("Location: interfaces_qinq.php"); + exit; + } +} + +$pgtitle = array(gettext("Interfaces"), gettext("QinQ")); +$shortcut_section = "interfaces"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +$tab_array = array(); +$tab_array[] = array(gettext("Interface assignments"), false, "interfaces_assign.php"); +$tab_array[] = array(gettext("Interface Groups"), false, "interfaces_groups.php"); +$tab_array[] = array(gettext("Wireless"), false, "interfaces_wireless.php"); +$tab_array[] = array(gettext("VLANs"), false, "interfaces_vlan.php"); +$tab_array[] = array(gettext("QinQs"), true, "interfaces_qinq.php"); +$tab_array[] = array(gettext("PPPs"), false, "interfaces_ppps.php"); +$tab_array[] = array(gettext("GRE"), false, "interfaces_gre.php"); +$tab_array[] = array(gettext("GIF"), false, "interfaces_gif.php"); +$tab_array[] = array(gettext("Bridges"), false, "interfaces_bridge.php"); +$tab_array[] = array(gettext("LAGG"), false, "interfaces_lagg.php"); +display_top_tabs($tab_array); + +print_info_box(sprintf(gettext('Not all drivers/NICs support 802.1Q QinQ tagging properly. <br />On cards that do not explicitly support it, ' . + 'QinQ tagging will still work, but the reduced MTU may cause problems.<br />' . + 'See the %s handbook for information on supported cards.'), $g['product_name'])); + +?> +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Interface"); ?></th> + <th><?=gettext("Tag");?></td> + <th><?=gettext("QinQ members"); ?></th> + <th><?=gettext("Description"); ?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php foreach ($a_qinqs as $i => $qinq):?> + <tr> + <td> + <?=htmlspecialchars($qinq['if'])?> + </td> + <td> + <?=htmlspecialchars($qinq['tag'])?> + </td> + <td> +<?php if (strlen($qinq['members']) > 20):?> + <?=substr(htmlspecialchars($qinq['members']), 0, 20)?>… +<?php else:?> + <?=htmlspecialchars($qinq['members'])?> +<?php endif; ?> + </td> + <td> + <?=htmlspecialchars($qinq['descr'])?> + </td> + <td> + <a href="interfaces_qinq_edit.php?id=<?=$i?>" class="btn btn-default btn-xs"><?=gettext("Edit")?></a> + <a href="interfaces_qinq.php?act=del&id=<?=$i?>" class="btn btn-danger btn-xs"><?=gettext("Delete")?></a> + </td> + </tr> +<?php +endforeach; +?> + </tbody> + </table> +</div> + +<nav class="action-buttons"> + <a href="interfaces_qinq_edit.php" class="btn btn-success"> + <?=gettext("Add")?> + </a> +</nav> +<?php +include("foot.inc"); diff --git a/src/usr/local/www/interfaces_qinq_edit.php b/src/usr/local/www/interfaces_qinq_edit.php new file mode 100644 index 0000000..9460884 --- /dev/null +++ b/src/usr/local/www/interfaces_qinq_edit.php @@ -0,0 +1,320 @@ +<?php +/* + interfaces_qinq_edit.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2009 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/sbin/ngctl + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-qinq-edit +##|*NAME=Interfaces: QinQ: Edit page +##|*DESCR=Allow access to 'Interfaces: QinQ: Edit' page +##|*MATCH=interfaces_qinq_edit.php* +##|-PRIV + +$pgtitle = array(gettext("Interfaces"), gettext("QinQ"), gettext("Edit")); +$shortcut_section = "interfaces"; + +require("guiconfig.inc"); + +if (!is_array($config['qinqs']['qinqentry'])) { + $config['qinqs']['qinqentry'] = array(); +} + +$a_qinqs = &$config['qinqs']['qinqentry']; + +$portlist = get_interface_list(); + +/* add LAGG interfaces */ +if (is_array($config['laggs']['lagg']) && count($config['laggs']['lagg'])) { + foreach ($config['laggs']['lagg'] as $lagg) { + $portlist[$lagg['laggif']] = $lagg; + } +} + +if (count($portlist) < 1) { + header("Location: interfaces_qinq.php"); + exit; +} + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_qinqs[$id]) { + $pconfig['if'] = $a_qinqs[$id]['if']; + $pconfig['tag'] = $a_qinqs[$id]['tag']; + $pconfig['members'] = $a_qinqs[$id]['members']; + $pconfig['descr'] = html_entity_decode($a_qinqs[$id]['descr']); + $pconfig['autogroup'] = isset($a_qinqs[$id]['autogroup']); + $pconfig['autoadjustmtu'] = isset($a_qinqs[$id]['autoadjustmtu']); +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + if (empty($_POST['tag'])) { + $input_errors[] = gettext("First level tag cannot be empty."); + } + if (isset($id) && $a_qinqs[$id]['tag'] != $_POST['tag']) { + $input_errors[] = gettext("You are editing an existing entry and modifying the first level tag is not allowed."); + } + if (isset($id) && $a_qinqs[$id]['if'] != $_POST['if']) { + $input_errors[] = gettext("You are editing an existing entry and modifying the interface is not allowed."); + } + if (!isset($id)) { + foreach ($a_qinqs as $qinqentry) { + if ($qinqentry['tag'] == $_POST['tag'] && $qinqentry['if'] == $_POST['if']) { + $input_errors[] = gettext("QinQ level already exists for this interface, edit it!"); + } + } + if (is_array($config['vlans']['vlan'])) { + foreach ($config['vlans']['vlan'] as $vlan) { + if ($vlan['tag'] == $_POST['tag'] && $vlan['if'] == $_POST['if']) { + $input_errors[] = gettext("A normal VLAN exists with this tag please remove it to use this tag for QinQ first level."); + } + } + } + } + + $qinqentry = array(); + $qinqentry['if'] = $_POST['if']; + $qinqentry['tag'] = $_POST['tag']; + + if ($_POST['autogroup'] == "yes") { + $qinqentry['autogroup'] = true; + } + + $members = ""; + $isfirst = 0; + + // Read the POSTed member array into a space separated list translating any ranges + // into their included values + foreach ($_POST['members'] as $memb) { + // Might be a range + $member = explode("-", $memb); + + if (count($member) > 1) { + if (preg_match("/([^0-9])+/", $member[0], $match) || preg_match("/([^0-9])+/", $member[1], $match)) + $input_errors[] = gettext("Tags can contain only numbers or a range in format #-#."); + + for ($i = $member[0]; $i <= $member[1]; $i++) { + $members .= ($isfirst == 0 ? '':' ') . $i; + $isfirst++; + } + } + else { // Just a single number + if (preg_match("/([^0-9])+/", $memb, $match)) + $input_errors[] = gettext("Tags can contain only numbers or a range in format #-#."); + else { + $members .= ($isfirst == 0 ? '':' ') . $memb; + $isfirst++; + } + } + } + + if (!$input_errors) { + $qinqentry['members'] = $members; + $qinqentry['descr'] = $_POST['descr']; + $qinqentry['vlanif'] = "{$_POST['if']}_{$_POST['tag']}"; + $nmembers = explode(" ", $members); + + if (isset($id) && $a_qinqs[$id]) { + $omembers = explode(" ", $a_qinqs[$id]['members']); + $delmembers = array_diff($omembers, $nmembers); + $addmembers = array_diff($nmembers, $omembers); + + if ((count($delmembers) > 0) || (count($addmembers) > 0)) { + $fd = fopen("{$g['tmp_path']}/netgraphcmd", "w"); + foreach ($delmembers as $tag) { + fwrite($fd, "shutdown {$qinqentry['vlanif']}h{$tag}:\n"); + fwrite($fd, "msg {$qinqentry['vlanif']}qinq: delfilter \\\"{$qinqentry['vlanif']}{$tag}\\\"\n"); + } + + foreach ($addmembers as $member) { + $qinq = array(); + $qinq['if'] = $qinqentry['vlanif']; + $qinq['tag'] = $member; + $macaddr = get_interface_mac($qinqentry['vlanif']); + interface_qinq2_configure($qinq, $fd, $macaddr); + } + + fclose($fd); + mwexec("/usr/sbin/ngctl -f {$g['tmp_path']}/netgraphcmd"); + } + $a_qinqs[$id] = $qinqentry; + } else { + interface_qinq_configure($qinqentry); + $a_qinqs[] = $qinqentry; + } + if ($_POST['autogroup'] == "yes") { + if (!is_array($config['ifgroups']['ifgroupentry'])) { + $config['ifgroups']['ifgroupentry'] = array(); + } + foreach ($config['ifgroups']['ifgroupentry'] as $gid => $group) { + if ($group['ifname'] == "QinQ") { + $found = true; + break; + } + } + $additions = ""; + foreach ($nmembers as $qtag) { + $additions .= "{$qinqentry['vlanif']}_{$qtag} "; + } + $additions .= "{$qinqentry['vlanif']}"; + if ($found == true) { + $config['ifgroups']['ifgroupentry'][$gid]['members'] .= " {$additions}"; + } else { + $gentry = array(); + $gentry['ifname'] = "QinQ"; + $gentry['members'] = "{$additions}"; + $gentry['descr'] = gettext("QinQ VLANs group"); + $config['ifgroups']['ifgroupentry'][] = $gentry; + } + } + + write_config(); + + header("Location: interfaces_qinq.php"); + exit; + } else { + $pconfig['descr'] = $_POST['descr']; + $pconfig['tag'] = $_POST['tag']; + $pconfig['members'] = $members; + } +} + +function build_parent_list() { + global $portlist; + + $list = array(); + + foreach ($portlist as $ifn => $ifinfo) { + if (is_jumbo_capable($ifn)) + $list[$ifn] = $ifn . ' (' . $ifinfo['mac'] . ')'; + } + + return($list); +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(new Form_Button( + 'Submit', + gettext("Save") +)); + +$section = new Form_Section('Interface QinQ Edit'); + +$section->addInput(new Form_Select( + 'if', + 'Parent interface', + $pconfig['if'], + build_parent_list() +))->setHelp('Only QinQ capable interfaces will be shown.'); + +$section->addInput(new Form_Input( + 'tag', + 'First level tag', + 'number', + $pconfig['tag'], + ['max' => '4094', 'min' => '1'] +))->setHelp('This is the first level VLAN tag. On top of this are stacked the member VLANs defined below.'); + +$section->addInput(new Form_Checkbox( + 'autogroup', + 'Option(s)', + 'Adds interface to QinQ interface groups', + $pconfig['autogroup'] +))->setHelp('Allows rules to be written more easily'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$section->addInput(new Form_StaticText( + 'Member(s)', + 'You can specify ranges in the inputs below. Enter a range (2-3) or individual numbers.' . '<br />' . + 'Click "Duplicate" as many times as needed to add new inputs' +)); + +if (isset($id) && $a_qinqs[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$counter = 0; +$members = $pconfig['members']; + +// List each of the member tags from the space-separated list +if ($members != "") + $item = explode(" ", $members); +else + $item = array(''); + +foreach($item as $ww) { + $member = $item[$counter]; + + $group = new Form_Group($counter == 0 ? 'Tag(s)':''); + + $group->add(new Form_Input( + 'members', + null, + 'text', + $ww + ))->setWidth(6); // Width must be <= 8 to make room for the duplication buttons + +$counter++; + +$group->enableDuplication(null, true); // Buttons are in-line with the input +$section->add($group); +} + +$form->add($section); + +print($form); + +include("foot.inc"); diff --git a/src/usr/local/www/interfaces_vlan.php b/src/usr/local/www/interfaces_vlan.php new file mode 100644 index 0000000..d7a8ae2 --- /dev/null +++ b/src/usr/local/www/interfaces_vlan.php @@ -0,0 +1,141 @@ +<?php +/* $Id$ */ +/* + interfaces_vlan.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-vlan +##|*NAME=Interfaces: VLAN page +##|*DESCR=Allow access to the 'Interfaces: VLAN' page. +##|*MATCH=interfaces_vlan.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['vlans']['vlan'])) { + $config['vlans']['vlan'] = array(); +} + +$a_vlans = &$config['vlans']['vlan'] ; + +function vlan_inuse($num) { + global $config, $a_vlans; + + $iflist = get_configured_interface_list(false, true); + foreach ($iflist as $if) { + if ($config['interfaces'][$if]['if'] == $a_vlans[$num]['vlanif']) { + return true; + } + } + + return false; +} + +if ($_GET['act'] == "del") { + if (!isset($_GET['id'])) { + $input_errors[] = gettext("Wrong parameters supplied"); + } else if (empty($a_vlans[$_GET['id']])) { + $input_errors[] = gettext("Wrong index supplied"); + /* check if still in use */ + } else if (vlan_inuse($_GET['id'])) { + $input_errors[] = gettext("This VLAN cannot be deleted because it is still being used as an interface."); + } else { + if (does_interface_exist($a_vlans[$_GET['id']]['vlanif'])) { + pfSense_interface_destroy($a_vlans[$_GET['id']]['vlanif']); + } + unset($a_vlans[$_GET['id']]); + + write_config(); + + header("Location: interfaces_vlan.php"); + exit; + } +} + + +$pgtitle = array(gettext("Interfaces"), gettext("VLAN")); +$shortcut_section = "interfaces"; +include('head.inc'); + +if ($input_errors) print_input_errors($input_errors); + +$tab_array = array(); +$tab_array[] = array(gettext("Interface assignments"), false, "interfaces_assign.php"); +$tab_array[] = array(gettext("Interface Groups"), false, "interfaces_groups.php"); +$tab_array[] = array(gettext("Wireless"), false, "interfaces_wireless.php"); +$tab_array[] = array(gettext("VLANs"), true, "interfaces_vlan.php"); +$tab_array[] = array(gettext("QinQs"), false, "interfaces_qinq.php"); +$tab_array[] = array(gettext("PPPs"), false, "interfaces_ppps.php"); +$tab_array[] = array(gettext("GRE"), false, "interfaces_gre.php"); +$tab_array[] = array(gettext("GIF"), false, "interfaces_gif.php"); +$tab_array[] = array(gettext("Bridges"), false, "interfaces_bridge.php"); +$tab_array[] = array(gettext("LAGG"), false, "interfaces_lagg.php"); +display_top_tabs($tab_array); + +print_info_box(sprintf(gettext('NOTE: Not all drivers/NICs support 802.1Q '. + 'VLAN tagging properly. <br />On cards that do not explicitly support it, VLAN '. + 'tagging will still work, but the reduced MTU may cause problems.<br />See the '. + '%s handbook for information on supported cards.'),$g['product_name'])); +?> +<div class="table-responsive"> + <table class="table"> + <tr> + <th><?=gettext('Interface');?></th> + <th><?=gettext('VLAN tag');?></th> + <th><?=gettext('Description');?></th> + </tr> +<?php + $i = 0; + foreach ($a_vlans as $vlan) { +?> + <tr> + <td><?=htmlspecialchars($vlan['if']);?></td> + <td><?=htmlspecialchars($vlan['tag']);?></td> + <td><?=htmlspecialchars($vlan['descr']);?></td> + <td> + <a class="btn btn-primary btn-xs" role="button" href="interfaces_vlan_edit.php?id=<?=$i?>"><?=gettext('Edit')?></a> + <a class="btn btn-danger btn-xs" role="button" href="interfaces_vlan.php?act=del&id=<?=$i?>"><?=gettext('Delete')?></a></td> + </td> + </tr> + <?php + $i++; + } +?> + </table> + <nav class="action-buttons"> + <a class="btn btn-success" role="button" href="interfaces_vlan_edit.php"><?=gettext('Add VLAN'); ?></a> + </nav> +</div> +<?php +include("foot.inc"); diff --git a/src/usr/local/www/interfaces_vlan_edit.php b/src/usr/local/www/interfaces_vlan_edit.php new file mode 100644 index 0000000..0bf223a --- /dev/null +++ b/src/usr/local/www/interfaces_vlan_edit.php @@ -0,0 +1,223 @@ +<?php +/* $Id$ */ +/* + interfaces_vlan_edit.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-vlan-edit +##|*NAME=Interfaces: VLAN: Edit page +##|*DESCR=Allow access to the 'Interfaces: VLAN: Edit' page. +##|*MATCH=interfaces_vlan_edit.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['vlans']['vlan'])) + $config['vlans']['vlan'] = array(); + +$a_vlans = &$config['vlans']['vlan']; + +$portlist = get_interface_list(); + +/* add LAGG interfaces */ +if (is_array($config['laggs']['lagg']) && count($config['laggs']['lagg'])) { + foreach ($config['laggs']['lagg'] as $lagg) { + $portlist[$lagg['laggif']] = $lagg; + } +} + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_vlans[$id]) { + $pconfig['if'] = $a_vlans[$id]['if']; + $pconfig['vlanif'] = $a_vlans[$id]['vlanif']; + $pconfig['tag'] = $a_vlans[$id]['tag']; + $pconfig['descr'] = $a_vlans[$id]['descr']; +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "if tag"); + $reqdfieldsn = array(gettext("Parent interface"), gettext("VLAN tag")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (isset($_POST['tag']) && (!is_numericint($_POST['tag']) || ($_POST['tag'] < '1') || ($_POST['tag'] > '4094'))) { + $input_errors[] = gettext("The VLAN tag must be an integer between 1 and 4094."); + } + + if (!does_interface_exist($_POST['if'])) { + $input_errors[] = gettext("Interface supplied as parent is invalid"); + } + + if (isset($id)) { + if ($_POST['tag'] && $_POST['tag'] != $a_vlans[$id]['tag']) { + if (!empty($a_vlans[$id]['vlanif']) && convert_real_interface_to_friendly_interface_name($a_vlans[$id]['vlanif']) != NULL) { + $input_errors[] = gettext("Interface is assigned and you cannot change the VLAN tag while assigned."); + } + } + } + foreach ($a_vlans as $vlan) { + if (isset($id) && ($a_vlans[$id]) && ($a_vlans[$id] === $vlan)) { + continue; + } + + if (($vlan['if'] == $_POST['if']) && ($vlan['tag'] == $_POST['tag'])) { + $input_errors[] = sprintf(gettext("A VLAN with the tag %s is already defined on this interface."), $vlan['tag']); + break; + } + } + if (is_array($config['qinqs']['qinqentry'])) { + foreach ($config['qinqs']['qinqentry'] as $qinq) { + if ($qinq['tag'] == $_POST['tag'] && $qinq['if'] == $_POST['if']) { + $input_errors[] = gettext("A QinQ VLAN exists with this tag please remove it to use this tag with."); + } + } + } + + if (!$input_errors) { + if (isset($id) && $a_vlans[$id]) { + if (($a_vlans[$id]['if'] != $_POST['if']) || ($a_vlans[$id]['tag'] != $_POST['tag'])) { + if (!empty($a_vlans[$id]['vlanif'])) { + $confif = convert_real_interface_to_friendly_interface_name($vlan['vlanif']); + // Destroy previous vlan + pfSense_interface_destroy($a_vlans[$id]['vlanif']); + } else { + pfSense_interface_destroy("{$a_vlans[$id]['if']}_vlan{$a_vlans[$id]['tag']}"); + $confif = convert_real_interface_to_friendly_interface_name("{$a_vlans[$id]['if']}_vlan{$a_vlans[$id]['tag']}"); + } + if ($confif != "") + $config['interfaces'][$confif]['if'] = "{$_POST['if']}_vlan{$_POST['tag']}"; + } + } + $vlan = array(); + $vlan['if'] = $_POST['if']; + $vlan['tag'] = $_POST['tag']; + $vlan['descr'] = $_POST['descr']; + $vlan['vlanif'] = "{$_POST['if']}_vlan{$_POST['tag']}"; + $vlan['vlanif'] = interface_vlan_configure($vlan); + if ($vlan['vlanif'] == "" || !stristr($vlan['vlanif'], "vlan")) { + $input_errors[] = gettext("Error occurred creating interface, please retry."); + } else { + if (isset($id) && $a_vlans[$id]) { + $a_vlans[$id] = $vlan; + } else { + $a_vlans[] = $vlan; + } + + write_config(); + + if ($confif != "") + interface_configure($confif); + header("Location: interfaces_vlan.php"); + exit; + } + } +} + +$pgtitle = array(gettext("Interfaces"), gettext("VLAN"), gettext("Edit")); +$shortcut_section = "interfaces"; +include("head.inc"); + +if ($input_errors) { + print_input_errors($input_errors); +} + +require('classes/Form.class.php'); +$form = new Form; +$section = new Form_Section('Interface VLAN Edit'); + +$section->addInput(new Form_Select( + 'if', + 'Parent Interface', + $pconfig['if'], + array_combine( + array_keys($portlist), + array_map( + function($key, $value) { + return (is_jumbo_capable($key)) ? "{$key} ({$value['mac']})" : $value; + }, + array_keys($portlist), + array_values($portlist) + ) + ), + false +))->setWidth(6)->setHelp('Only VLAN capable interfaces will be shown.'); + +$section->addInput(new Form_Input( + 'tag', + 'VLAN Tag', + 'text', + $pconfig['tag'], + ['placeholder' => '1'] +))->setWidth(6)->setHelp(gettext('802.1Q VLAN tag (between 1 and 4094).')); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'], + ['placeholder' => 'Description'] +))->setWidth(6)->setHelp('You may enter a group description here '. + 'for your reference (not parsed).'); + +$form->addGlobal(new Form_Input( + 'vlanif', + 'vlanif', + 'hidden', + $pconfig['vlanif'] +)); + +if (isset($id) && $a_vlans[$id]) { + $form->addGlobal(new Form_Input( + 'id', + 'id', + 'hidden', + $id + )); +} + +$form->add($section); +print $form; + +include("foot.inc"); + diff --git a/src/usr/local/www/interfaces_wireless.php b/src/usr/local/www/interfaces_wireless.php new file mode 100644 index 0000000..cb90387 --- /dev/null +++ b/src/usr/local/www/interfaces_wireless.php @@ -0,0 +1,149 @@ +<?php +/* $Id$ */ +/* + interfaces_wireless.php + + Copyright (C) 2010 Erik Fonnesbeck + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: interfaces_assign +*/ + +##|+PRIV +##|*IDENT=page-interfaces-wireless +##|*NAME=Interfaces: Wireless page +##|*DESCR=Allow access to the 'Interfaces: Wireless' page. +##|*MATCH=interfaces_wireless.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['wireless'])) { + $config['wireless'] = array(); +} +if (!is_array($config['wireless']['clone'])) { + $config['wireless']['clone'] = array(); +} + +$a_clones = &$config['wireless']['clone']; + +function clone_inuse($num) { + global $config, $a_clones; + + $iflist = get_configured_interface_list(false, true); + + foreach ($iflist as $if) { + if ($config['interfaces'][$if]['if'] == $a_clones[$num]['cloneif']) { + return true; + } + } + + return false; +} + +if ($_GET['act'] == "del") { + /* check if still in use */ + if (clone_inuse($_GET['id'])) { + $input_errors[] = gettext("This wireless clone cannot be deleted because it is assigned as an interface."); + } else { + mwexec("/sbin/ifconfig " . $a_clones[$_GET['id']]['cloneif'] . " destroy"); + unset($a_clones[$_GET['id']]); + + write_config(); + + header("Location: interfaces_wireless.php"); + exit; + } +} + + +$pgtitle = array(gettext("Interfaces"), gettext("Wireless")); +$shortcut_section = "wireless"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); ?> + +<?php +$tab_array = array(); +$tab_array[] = array(gettext("Interface assignments"), false, "interfaces_assign.php"); +$tab_array[] = array(gettext("Interface Groups"), false, "interfaces_groups.php"); +$tab_array[] = array(gettext("Wireless"), true, "interfaces_wireless.php"); +$tab_array[] = array(gettext("VLANs"), false, "interfaces_vlan.php"); +$tab_array[] = array(gettext("QinQs"), false, "interfaces_qinq.php"); +$tab_array[] = array(gettext("PPPs"), false, "interfaces_ppps.php"); +$tab_array[] = array(gettext("GRE"), false, "interfaces_gre.php"); +$tab_array[] = array(gettext("GIF"), false, "interfaces_gif.php"); +$tab_array[] = array(gettext("Bridges"), false, "interfaces_bridge.php"); +$tab_array[] = array(gettext("LAGG"), false, "interfaces_lagg.php"); +display_top_tabs($tab_array); +?> + +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Interface"); ?></th> + <th><?=gettext("Mode"); ?></th> + <th><?=gettext("Description"); ?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php + +$i = 0; + +foreach ($a_clones as $clone) { +?> + <tr> + <td> + <?=htmlspecialchars($clone['cloneif'])?> + </td> + <td> + <?= $wlan_modes[$clone['mode']]; ?> + </td> + <td> + <?=htmlspecialchars($clone['descr'])?> + </td> + <td> + <a href="interfaces_wireless_edit.php?id=<?=$i?>" class="btn btn-default btn-xs"><?=gettext("Edit")?></a> + <a href="interfaces_wireless.php?act=del&id=<?=$i?>" class="btn btn-danger btn-xs"><?=gettext("Delete")?></a> + </td> + </tr> +<?php + $i++; +} +?> + </tbody> + </table> + + <nav class="action-buttons"> + <a href="interfaces_wireless_edit.php" class="btn btn-success"><?=gettext("Add")?></a> + </nav> +</div> +<?php +include("foot.inc"); diff --git a/src/usr/local/www/interfaces_wireless_edit.php b/src/usr/local/www/interfaces_wireless_edit.php new file mode 100644 index 0000000..52d26ff --- /dev/null +++ b/src/usr/local/www/interfaces_wireless_edit.php @@ -0,0 +1,232 @@ +<?php +/* $Id$ */ +/* + interfaces_wireless_edit.php + + Copyright (C) 2010 Erik Fonnesbeck + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-interfaces-wireless-edit +##|*NAME=Interfaces: Wireless edit page +##|*DESCR=Allow access to the 'Interfaces: Wireless : Edit' page. +##|*MATCH=interfaces_wireless_edit.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['wireless'])) + $config['wireless'] = array(); + +if (!is_array($config['wireless']['clone'])) + $config['wireless']['clone'] = array(); + +$a_clones = &$config['wireless']['clone']; + +function clone_inuse($num) { + global $config, $a_clones; + + $iflist = get_configured_interface_list(false, true); + foreach ($iflist as $if) { + if ($config['interfaces'][$if]['if'] == $a_clones[$num]['cloneif']) { + return true; + } + } + + return false; +} + +function clone_compare($a, $b) { + return strnatcmp($a['cloneif'], $b['cloneif']); +} + +$portlist = get_interface_list(); + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_clones[$id]) { + $pconfig['if'] = $a_clones[$id]['if']; + $pconfig['cloneif'] = $a_clones[$id]['cloneif']; + $pconfig['mode'] = $a_clones[$id]['mode']; + $pconfig['descr'] = $a_clones[$id]['descr']; +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "if mode"); + $reqdfieldsn = array(gettext("Parent interface"), gettext("Mode")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (!$input_errors) { + $clone = array(); + $clone['if'] = $_POST['if']; + $clone['mode'] = $_POST['mode']; + $clone['descr'] = $_POST['descr']; + + if (isset($id) && $a_clones[$id]) { + if ($clone['if'] == $a_clones[$id]['if']) { + $clone['cloneif'] = $a_clones[$id]['cloneif']; + } + } + + if (!$clone['cloneif']) { + $clone_id = 1; + do { + $clone_exists = false; + $clone['cloneif'] = "{$_POST['if']}_wlan{$clone_id}"; + foreach ($a_clones as $existing) { + if ($clone['cloneif'] == $existing['cloneif']) { + $clone_exists = true; + $clone_id++; + break; + } + } + } while ($clone_exists); + } + + if (isset($id) && $a_clones[$id]) { + if (clone_inuse($id)) { + if ($clone['if'] != $a_clones[$id]['if']) { + $input_errors[] = gettext("This wireless clone cannot be modified because it is still assigned as an interface."); + } else if ($clone['mode'] != $a_clones[$id]['mode']) { + $input_errors[] = gettext("Use the configuration page for the assigned interface to change the mode."); + } + } + } + + if (!$input_errors) { + if (!interface_wireless_clone($clone['cloneif'], $clone)) { + $input_errors[] = sprintf(gettext('Error creating interface with mode %1$s. The %2$s interface may not support creating more clones with the selected mode.'), $wlan_modes[$clone['mode']], $clone['if']); + } else { + if (isset($id) && $a_clones[$id]) { + if ($clone['if'] != $a_clones[$id]['if']) { + mwexec("/sbin/ifconfig " . $a_clones[$id]['cloneif'] . " destroy"); + } + $input_errors[] = sprintf(gettext("Created with id %s"), $id); + $a_clones[$id] = $clone; + } else { + $input_errors[] = gettext("Created without id"); + $a_clones[] = $clone; + } + + usort($a_clones, "clone_compare"); + write_config(); + + header("Location: interfaces_wireless.php"); + exit; + } + } + } +} + +function build_parent_list() { + global $g; + + $parentlist = array(); + $portlist = get_possible_listen_ips(); + $count = 0; + foreach ($portlist as $ifn => $ifinfo) { + if (preg_match($g['wireless_regex'], $ifn)) { + $parentlist[$ifn] = htmlspecialchars($ifn . '(' . $ifinfo['mac'] . ')'); + $count++; + } + } + + if($count > 0) + return($parentlist); + else + return(array('0' => gettext('None available'))); +} + +$pgtitle = array(gettext("Interfaces"),gettext("Wireless"),gettext("Edit")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Wireless Interface'); + +$section->addInput(new Form_Select( + 'parent', + 'Parent Interface', + $pconfig['if'], + build_parent_list() +)); + +$section->addInput(new Form_Select( + 'mode', + 'Mode', + $pconfig['mode'], + array( + 'bss' => 'Infrastructure (BSS)', + 'adhoc' => 'Ad-hoc (IBSS)', + 'hostap' => 'Access Point' + ) +)); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$section->addInput(new Form_Input( + 'cloneif', + null, + 'hidden', + $pconfig['cloneif'] +)); + +if (isset($id) && $a_clones[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/jquery/jquery-1.11.2.min.js b/src/usr/local/www/jquery/jquery-1.11.2.min.js new file mode 100644 index 0000000..e6a051d --- /dev/null +++ b/src/usr/local/www/jquery/jquery-1.11.2.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.11.2 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.2",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=hb(),z=hb(),A=hb(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},eb=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fb){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function gb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+rb(o[l]);w=ab.test(a)&&pb(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function hb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ib(a){return a[u]=!0,a}function jb(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function kb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function lb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function nb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function ob(a){return ib(function(b){return b=+b,ib(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pb(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=gb.support={},f=gb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=gb.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",eb,!1):e.attachEvent&&e.attachEvent("onunload",eb)),p=!f(g),c.attributes=jb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=jb(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=jb(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(jb(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\f]' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),jb(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&jb(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return lb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?lb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},gb.matches=function(a,b){return gb(a,null,null,b)},gb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return gb(b,n,null,[a]).length>0},gb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},gb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},gb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},gb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=gb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=gb.selectors={cacheLength:50,createPseudo:ib,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||gb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&gb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=gb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||gb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ib(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ib(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ib(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ib(function(a){return function(b){return gb(a,b).length>0}}),contains:ib(function(a){return a=a.replace(cb,db),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ib(function(a){return W.test(a||"")||gb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:ob(function(){return[0]}),last:ob(function(a,b){return[b-1]}),eq:ob(function(a,b,c){return[0>c?c+b:c]}),even:ob(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:ob(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:ob(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:ob(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=mb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=nb(b);function qb(){}qb.prototype=d.filters=d.pseudos,d.setFilters=new qb,g=gb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?gb.error(a):z(a,i).slice(0)};function rb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function sb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function tb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ub(a,b,c){for(var d=0,e=b.length;e>d;d++)gb(a,b[d],c);return c}function vb(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wb(a,b,c,d,e,f){return d&&!d[u]&&(d=wb(d)),e&&!e[u]&&(e=wb(e,f)),ib(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ub(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:vb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=vb(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=vb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sb(function(a){return a===b},h,!0),l=sb(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sb(tb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wb(i>1&&tb(m),i>1&&rb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xb(a.slice(i,e)),f>e&&xb(a=a.slice(e)),f>e&&rb(a))}m.push(c)}return tb(m)}function yb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=vb(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&gb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ib(f):f}return h=gb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,yb(e,d)),f.selector=a}return f},i=gb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&pb(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&rb(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&pb(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=jb(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),jb(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||kb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&jb(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||kb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),jb(function(a){return null==a.getAttribute("disabled")})||kb(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),gb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1; +return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[m.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=Z.test(e)?this.mouseHooks:Y.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new m.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||y),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||y,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==cb()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===cb()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return m.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return m.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=m.extend(new m.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?m.event.trigger(e,null,b):m.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},m.removeEvent=y.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===K&&(a[d]=null),a.detachEvent(d,c))},m.Event=function(a,b){return this instanceof m.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ab:bb):this.type=a,b&&m.extend(this,b),this.timeStamp=a&&a.timeStamp||m.now(),void(this[m.expando]=!0)):new m.Event(a,b)},m.Event.prototype={isDefaultPrevented:bb,isPropagationStopped:bb,isImmediatePropagationStopped:bb,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ab,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ab,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ab,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},m.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){m.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!m.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.submitBubbles||(m.event.special.submit={setup:function(){return m.nodeName(this,"form")?!1:void m.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=m.nodeName(b,"input")||m.nodeName(b,"button")?b.form:void 0;c&&!m._data(c,"submitBubbles")&&(m.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),m._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&m.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return m.nodeName(this,"form")?!1:void m.event.remove(this,"._submit")}}),k.changeBubbles||(m.event.special.change={setup:function(){return X.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(m.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),m.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),m.event.simulate("change",this,a,!0)})),!1):void m.event.add(this,"beforeactivate._change",function(a){var b=a.target;X.test(b.nodeName)&&!m._data(b,"changeBubbles")&&(m.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||m.event.simulate("change",this.parentNode,a,!0)}),m._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return m.event.remove(this,"._change"),!X.test(this.nodeName)}}),k.focusinBubbles||m.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){m.event.simulate(b,a.target,m.event.fix(a),!0)};m.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=m._data(d,b);e||d.addEventListener(a,c,!0),m._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=m._data(d,b)-1;e?m._data(d,b,e):(d.removeEventListener(a,c,!0),m._removeData(d,b))}}}),m.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(f in a)this.on(f,b,c,a[f],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=bb;else if(!d)return this;return 1===e&&(g=d,d=function(a){return m().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=m.guid++)),this.each(function(){m.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,m(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=bb),this.each(function(){m.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){m.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?m.event.trigger(a,b,c,!0):void 0}});function db(a){var b=eb.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var eb="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",fb=/ jQuery\d+="(?:null|\d+)"/g,gb=new RegExp("<(?:"+eb+")[\\s/>]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/<tbody/i,lb=/<|&#?\w+;/,mb=/<(?:script|style|link)/i,nb=/checked\s*(?:[^=]|=\s*.checked.)/i,ob=/^$|\/(?:java|ecma)script/i,pb=/^true\/(.*)/,qb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,rb={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:k.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1></$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?"<table>"!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(b){return b.ownerDocument.defaultView.opener?b.ownerDocument.defaultView.getComputedStyle(b,null):a.getComputedStyle(b,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight),b.removeChild(i)),b.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e) +}m.Tween=Zb,Zb.prototype={constructor:Zb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||m.fx.stop(),$b=void 0},m.fx.timer=function(a){m.timers.push(a),a()?m.fx.start():m.timers.pop()},m.fx.interval=13,m.fx.start=function(){_b||(_b=setInterval(m.fx.tick,m.fx.interval))},m.fx.stop=function(){clearInterval(_b),_b=null},m.fx.speeds={slow:600,fast:200,_default:400},m.fn.delay=function(a,b){return a=m.fx?m.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a,b,c,d,e;b=y.createElement("div"),b.setAttribute("className","t"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(uc," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=m.event&&k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.attachEvent&&a.attachEvent("onunload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("<div>").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m}); diff --git a/src/usr/local/www/jquery/jquery-ui-1.11.2.min.js b/src/usr/local/www/jquery/jquery-ui-1.11.2.min.js new file mode 100644 index 0000000..17eab79 --- /dev/null +++ b/src/usr/local/www/jquery/jquery-ui-1.11.2.min.js @@ -0,0 +1,13 @@ +/*! jQuery UI - v1.11.2 - 2014-10-16 +* http://jqueryui.com +* Includes: core.js, widget.js, mouse.js, position.js, accordion.js, autocomplete.js, button.js, datepicker.js, dialog.js, draggable.js, droppable.js, effect.js, effect-blind.js, effect-bounce.js, effect-clip.js, effect-drop.js, effect-explode.js, effect-fade.js, effect-fold.js, effect-highlight.js, effect-puff.js, effect-pulsate.js, effect-scale.js, effect-shake.js, effect-size.js, effect-slide.js, effect-transfer.js, menu.js, progressbar.js, resizable.js, selectable.js, selectmenu.js, slider.js, sortable.js, spinner.js, tabs.js, tooltip.js +* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ + +(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(t,s){var n,a,o,r=t.nodeName.toLowerCase();return"area"===r?(n=t.parentNode,a=n.name,t.href&&a&&"map"===n.nodeName.toLowerCase()?(o=e("img[usemap='#"+a+"']")[0],!!o&&i(o)):!1):(/input|select|textarea|button|object/.test(r)?!t.disabled:"a"===r?t.href||s:s)&&i(t)}function i(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}function s(e){for(var t,i;e.length&&e[0]!==document;){if(t=e.css("position"),("absolute"===t||"relative"===t||"fixed"===t)&&(i=parseInt(e.css("zIndex"),10),!isNaN(i)&&0!==i))return i;e=e.parent()}return 0}function n(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},e.extend(this._defaults,this.regional[""]),this.regional.en=e.extend(!0,{},this.regional[""]),this.regional["en-US"]=e.extend(!0,{},this.regional.en),this.dpDiv=a(e("<div id='"+this._mainDivId+"' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"))}function a(t){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return t.delegate(i,"mouseout",function(){e(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).removeClass("ui-datepicker-next-hover")}).delegate(i,"mouseover",o)}function o(){e.datepicker._isDisabledDatepicker(v.inline?v.dpDiv.parent()[0]:v.input[0])||(e(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),e(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).addClass("ui-datepicker-next-hover"))}function r(t,i){e.extend(t,i);for(var s in i)null==i[s]&&(t[s]=i[s]);return t}function h(e){return function(){var t=this.element.val();e.apply(this,arguments),this._refresh(),t!==this.element.val()&&this._trigger("change")}}e.ui=e.ui||{},e.extend(e.ui,{version:"1.11.2",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({scrollParent:function(t){var i=this.css("position"),s="absolute"===i,n=t?/(auto|scroll|hidden)/:/(auto|scroll)/,a=this.parents().filter(function(){var t=e(this);return s&&"static"===t.css("position")?!1:n.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==i&&a.length?a:e(this[0].ownerDocument||document)},uniqueId:function(){var e=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++e)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(i){return t(i,!isNaN(e.attr(i,"tabindex")))},tabbable:function(i){var s=e.attr(i,"tabindex"),n=isNaN(s);return(n||s>=0)&&t(i,!n)}}),e("<a>").outerWidth(1).jquery||e.each(["Width","Height"],function(t,i){function s(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],a=i.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+i]=function(t){return void 0===t?o["inner"+i].call(this):this.each(function(){e(this).css(a,s(this,t)+"px")})},e.fn["outer"+i]=function(t,n){return"number"!=typeof t?o["outer"+i].call(this,t):this.each(function(){e(this).css(a,s(this,t,!0,n)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("<a>").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),disableSelection:function(){var e="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(e+".ui-disableSelection",function(e){e.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(t){if(void 0!==t)return this.css("zIndex",t);if(this.length)for(var i,s,n=e(this[0]);n.length&&n[0]!==document;){if(i=n.css("position"),("absolute"===i||"relative"===i||"fixed"===i)&&(s=parseInt(n.css("zIndex"),10),!isNaN(s)&&0!==s))return s;n=n.parent()}return 0}}),e.ui.plugin={add:function(t,i,s){var n,a=e.ui[t].prototype;for(n in s)a.plugins[n]=a.plugins[n]||[],a.plugins[n].push([i,s[n]])},call:function(e,t,i,s){var n,a=e.plugins[t];if(a&&(s||e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType))for(n=0;a.length>n;n++)e.options[a[n][0]]&&a[n][1].apply(e.element,i)}};var l=0,u=Array.prototype.slice;e.cleanData=function(t){return function(i){var s,n,a;for(a=0;null!=(n=i[a]);a++)try{s=e._data(n,"events"),s&&s.remove&&e(n).triggerHandler("remove")}catch(o){}t(i)}}(e.cleanData),e.widget=function(t,i,s){var n,a,o,r,h={},l=t.split(".")[0];return t=t.split(".")[1],n=l+"-"+t,s||(s=i,i=e.Widget),e.expr[":"][n.toLowerCase()]=function(t){return!!e.data(t,n)},e[l]=e[l]||{},a=e[l][t],o=e[l][t]=function(e,t){return this._createWidget?(arguments.length&&this._createWidget(e,t),void 0):new o(e,t)},e.extend(o,a,{version:s.version,_proto:e.extend({},s),_childConstructors:[]}),r=new i,r.options=e.widget.extend({},r.options),e.each(s,function(t,s){return e.isFunction(s)?(h[t]=function(){var e=function(){return i.prototype[t].apply(this,arguments)},n=function(e){return i.prototype[t].apply(this,e)};return function(){var t,i=this._super,a=this._superApply;return this._super=e,this._superApply=n,t=s.apply(this,arguments),this._super=i,this._superApply=a,t}}(),void 0):(h[t]=s,void 0)}),o.prototype=e.widget.extend(r,{widgetEventPrefix:a?r.widgetEventPrefix||t:t},h,{constructor:o,namespace:l,widgetName:t,widgetFullName:n}),a?(e.each(a._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete a._childConstructors):i._childConstructors.push(o),e.widget.bridge(t,o),o},e.widget.extend=function(t){for(var i,s,n=u.call(arguments,1),a=0,o=n.length;o>a;a++)for(i in n[a])s=n[a][i],n[a].hasOwnProperty(i)&&void 0!==s&&(t[i]=e.isPlainObject(s)?e.isPlainObject(t[i])?e.widget.extend({},t[i],s):e.widget.extend({},s):s);return t},e.widget.bridge=function(t,i){var s=i.prototype.widgetFullName||t;e.fn[t]=function(n){var a="string"==typeof n,o=u.call(arguments,1),r=this;return n=!a&&o.length?e.widget.extend.apply(null,[n].concat(o)):n,a?this.each(function(){var i,a=e.data(this,s);return"instance"===n?(r=a,!1):a?e.isFunction(a[n])&&"_"!==n.charAt(0)?(i=a[n].apply(a,o),i!==a&&void 0!==i?(r=i&&i.jquery?r.pushStack(i.get()):i,!1):void 0):e.error("no such method '"+n+"' for "+t+" widget instance"):e.error("cannot call methods on "+t+" prior to initialization; "+"attempted to call method '"+n+"'")}):this.each(function(){var t=e.data(this,s);t?(t.option(n||{}),t._init&&t._init()):e.data(this,s,new i(n,this))}),r}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(t,i){i=e(i||this.defaultElement||this)[0],this.element=e(i),this.uuid=l++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=e(),this.hoverable=e(),this.focusable=e(),i!==this&&(e.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===i&&this.destroy()}}),this.document=e(i.style?i.ownerDocument:i.document||i),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,i){var s,n,a,o=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(o={},s=t.split("."),t=s.shift(),s.length){for(n=o[t]=e.widget.extend({},this.options[t]),a=0;s.length-1>a;a++)n[s[a]]=n[s[a]]||{},n=n[s[a]];if(t=s.pop(),1===arguments.length)return void 0===n[t]?null:n[t];n[t]=i}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=i}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,i,s){var n,a=this;"boolean"!=typeof t&&(s=i,i=t,t=!1),s?(i=n=e(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),e.each(s,function(s,o){function r(){return t||a.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(r.guid=o.guid=o.guid||r.guid||e.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+a.eventNamespace,u=h[2];u?n.delegate(u,l,r):i.bind(l,r)})},_off:function(t,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(i).undelegate(i),this.bindings=e(this.bindings.not(t).get()),this.focusable=e(this.focusable.not(t).get()),this.hoverable=e(this.hoverable.not(t).get())},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,o=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(o)&&o.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var o,r=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),o=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),o&&e.effects&&e.effects.effect[r]?s[t](n):r!==t&&s[r]?s[r](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}}),e.widget;var d=!1;e(document).mouseup(function(){d=!1}),e.widget("ui.mouse",{version:"1.11.2",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(i){return!0===e.data(i.target,t.widgetName+".preventClickEvent")?(e.removeData(i.target,t.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(t){if(!d){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(t),this._mouseDownEvent=t;var i=this,s=1===t.which,n="string"==typeof this.options.cancel&&t.target.nodeName?e(t.target).closest(this.options.cancel).length:!1;return s&&!n&&this._mouseCapture(t)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(t)!==!1,!this._mouseStarted)?(t.preventDefault(),!0):(!0===e.data(t.target,this.widgetName+".preventClickEvent")&&e.removeData(t.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return i._mouseMove(e)},this._mouseUpDelegate=function(e){return i._mouseUp(e)},this.document.bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),t.preventDefault(),d=!0,!0)):!0}},_mouseMove:function(t){if(this._mouseMoved){if(e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button)return this._mouseUp(t);if(!t.which)return this._mouseUp(t)}return(t.which||t.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted)},_mouseUp:function(t){return this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),d=!1,!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),function(){function t(e,t,i){return[parseFloat(e[0])*(p.test(e[0])?t/100:1),parseFloat(e[1])*(p.test(e[1])?i/100:1)]}function i(t,i){return parseInt(e.css(t,i),10)||0}function s(t){var i=t[0];return 9===i.nodeType?{width:t.width(),height:t.height(),offset:{top:0,left:0}}:e.isWindow(i)?{width:t.width(),height:t.height(),offset:{top:t.scrollTop(),left:t.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:t.outerWidth(),height:t.outerHeight(),offset:t.offset()}}e.ui=e.ui||{};var n,a,o=Math.max,r=Math.abs,h=Math.round,l=/left|center|right/,u=/top|center|bottom/,d=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,p=/%$/,f=e.fn.position;e.position={scrollbarWidth:function(){if(void 0!==n)return n;var t,i,s=e("<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),a=s.children()[0];return e("body").append(s),t=a.offsetWidth,s.css("overflow","scroll"),i=a.offsetWidth,t===i&&(i=s[0].clientWidth),s.remove(),n=t-i},getScrollInfo:function(t){var i=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),s=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),n="scroll"===i||"auto"===i&&t.width<t.element[0].scrollWidth,a="scroll"===s||"auto"===s&&t.height<t.element[0].scrollHeight;return{width:a?e.position.scrollbarWidth():0,height:n?e.position.scrollbarWidth():0}},getWithinInfo:function(t){var i=e(t||window),s=e.isWindow(i[0]),n=!!i[0]&&9===i[0].nodeType;return{element:i,isWindow:s,isDocument:n,offset:i.offset()||{left:0,top:0},scrollLeft:i.scrollLeft(),scrollTop:i.scrollTop(),width:s||n?i.width():i.outerWidth(),height:s||n?i.height():i.outerHeight()}}},e.fn.position=function(n){if(!n||!n.of)return f.apply(this,arguments);n=e.extend({},n);var p,m,g,v,y,b,_=e(n.of),x=e.position.getWithinInfo(n.within),w=e.position.getScrollInfo(x),k=(n.collision||"flip").split(" "),T={};return b=s(_),_[0].preventDefault&&(n.at="left top"),m=b.width,g=b.height,v=b.offset,y=e.extend({},v),e.each(["my","at"],function(){var e,t,i=(n[this]||"").split(" ");1===i.length&&(i=l.test(i[0])?i.concat(["center"]):u.test(i[0])?["center"].concat(i):["center","center"]),i[0]=l.test(i[0])?i[0]:"center",i[1]=u.test(i[1])?i[1]:"center",e=d.exec(i[0]),t=d.exec(i[1]),T[this]=[e?e[0]:0,t?t[0]:0],n[this]=[c.exec(i[0])[0],c.exec(i[1])[0]]}),1===k.length&&(k[1]=k[0]),"right"===n.at[0]?y.left+=m:"center"===n.at[0]&&(y.left+=m/2),"bottom"===n.at[1]?y.top+=g:"center"===n.at[1]&&(y.top+=g/2),p=t(T.at,m,g),y.left+=p[0],y.top+=p[1],this.each(function(){var s,l,u=e(this),d=u.outerWidth(),c=u.outerHeight(),f=i(this,"marginLeft"),b=i(this,"marginTop"),D=d+f+i(this,"marginRight")+w.width,S=c+b+i(this,"marginBottom")+w.height,M=e.extend({},y),C=t(T.my,u.outerWidth(),u.outerHeight());"right"===n.my[0]?M.left-=d:"center"===n.my[0]&&(M.left-=d/2),"bottom"===n.my[1]?M.top-=c:"center"===n.my[1]&&(M.top-=c/2),M.left+=C[0],M.top+=C[1],a||(M.left=h(M.left),M.top=h(M.top)),s={marginLeft:f,marginTop:b},e.each(["left","top"],function(t,i){e.ui.position[k[t]]&&e.ui.position[k[t]][i](M,{targetWidth:m,targetHeight:g,elemWidth:d,elemHeight:c,collisionPosition:s,collisionWidth:D,collisionHeight:S,offset:[p[0]+C[0],p[1]+C[1]],my:n.my,at:n.at,within:x,elem:u})}),n.using&&(l=function(e){var t=v.left-M.left,i=t+m-d,s=v.top-M.top,a=s+g-c,h={target:{element:_,left:v.left,top:v.top,width:m,height:g},element:{element:u,left:M.left,top:M.top,width:d,height:c},horizontal:0>i?"left":t>0?"right":"center",vertical:0>a?"top":s>0?"bottom":"middle"};d>m&&m>r(t+i)&&(h.horizontal="center"),c>g&&g>r(s+a)&&(h.vertical="middle"),h.important=o(r(t),r(i))>o(r(s),r(a))?"horizontal":"vertical",n.using.call(this,e,h)}),u.offset(e.extend(M,{using:l}))})},e.ui.position={fit:{left:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=e.left-t.collisionPosition.marginLeft,h=n-r,l=r+t.collisionWidth-a-n;t.collisionWidth>a?h>0&&0>=l?(i=e.left+h+t.collisionWidth-a-n,e.left+=h-i):e.left=l>0&&0>=h?n:h>l?n+a-t.collisionWidth:n:h>0?e.left+=h:l>0?e.left-=l:e.left=o(e.left-r,e.left)},top:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollTop:s.offset.top,a=t.within.height,r=e.top-t.collisionPosition.marginTop,h=n-r,l=r+t.collisionHeight-a-n;t.collisionHeight>a?h>0&&0>=l?(i=e.top+h+t.collisionHeight-a-n,e.top+=h-i):e.top=l>0&&0>=h?n:h>l?n+a-t.collisionHeight:n:h>0?e.top+=h:l>0?e.top-=l:e.top=o(e.top-r,e.top)}},flip:{left:function(e,t){var i,s,n=t.within,a=n.offset.left+n.scrollLeft,o=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=e.left-t.collisionPosition.marginLeft,u=l-h,d=l+t.collisionWidth-o-h,c="left"===t.my[0]?-t.elemWidth:"right"===t.my[0]?t.elemWidth:0,p="left"===t.at[0]?t.targetWidth:"right"===t.at[0]?-t.targetWidth:0,f=-2*t.offset[0];0>u?(i=e.left+c+p+f+t.collisionWidth-o-a,(0>i||r(u)>i)&&(e.left+=c+p+f)):d>0&&(s=e.left-t.collisionPosition.marginLeft+c+p+f-h,(s>0||d>r(s))&&(e.left+=c+p+f))},top:function(e,t){var i,s,n=t.within,a=n.offset.top+n.scrollTop,o=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=e.top-t.collisionPosition.marginTop,u=l-h,d=l+t.collisionHeight-o-h,c="top"===t.my[1],p=c?-t.elemHeight:"bottom"===t.my[1]?t.elemHeight:0,f="top"===t.at[1]?t.targetHeight:"bottom"===t.at[1]?-t.targetHeight:0,m=-2*t.offset[1];0>u?(s=e.top+p+f+m+t.collisionHeight-o-a,e.top+p+f+m>u&&(0>s||r(u)>s)&&(e.top+=p+f+m)):d>0&&(i=e.top-t.collisionPosition.marginTop+p+f+m-h,e.top+p+f+m>d&&(i>0||d>r(i))&&(e.top+=p+f+m))}},flipfit:{left:function(){e.ui.position.flip.left.apply(this,arguments),e.ui.position.fit.left.apply(this,arguments)},top:function(){e.ui.position.flip.top.apply(this,arguments),e.ui.position.fit.top.apply(this,arguments)}}},function(){var t,i,s,n,o,r=document.getElementsByTagName("body")[0],h=document.createElement("div");t=document.createElement(r?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},r&&e.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(o in s)t.style[o]=s[o];t.appendChild(h),i=r||document.documentElement,i.insertBefore(t,i.firstChild),h.style.cssText="position: absolute; left: 10.7432222px;",n=e(h).offset().left,a=n>10&&11>n,t.innerHTML="",i.removeChild(t)}()}(),e.ui.position,e.widget("ui.accordion",{version:"1.11.2",options:{active:0,animate:{},collapsible:!1,event:"click",header:"> li > :first-child,> :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:"hide",borderBottomWidth:"hide",paddingTop:"hide",paddingBottom:"hide",height:"hide"},showProps:{borderTopWidth:"show",borderBottomWidth:"show",paddingTop:"show",paddingBottom:"show",height:"show"},_create:function(){var t=this.options;this.prevShow=this.prevHide=e(),this.element.addClass("ui-accordion ui-widget ui-helper-reset").attr("role","tablist"),t.collapsible||t.active!==!1&&null!=t.active||(t.active=0),this._processPanels(),0>t.active&&(t.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():e()}},_createIcons:function(){var t=this.options.icons;t&&(e("<span>").addClass("ui-accordion-header-icon ui-icon "+t.header).prependTo(this.headers),this.active.children(".ui-accordion-header-icon").removeClass(t.header).addClass(t.activeHeader),this.headers.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.removeClass("ui-accordion-icons").children(".ui-accordion-header-icon").remove()},_destroy:function(){var e;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.removeClass("ui-accordion-header ui-accordion-header-active ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("aria-controls").removeAttr("tabIndex").removeUniqueId(),this._destroyIcons(),e=this.headers.next().removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled").css("display","").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeUniqueId(),"content"!==this.options.heightStyle&&e.css("height","")},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):("event"===e&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(t)),this._super(e,t),"collapsible"!==e||t||this.options.active!==!1||this._activate(0),"icons"===e&&(this._destroyIcons(),t&&this._createIcons()),"disabled"===e&&(this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this.headers.add(this.headers.next()).toggleClass("ui-state-disabled",!!t)),void 0)},_keydown:function(t){if(!t.altKey&&!t.ctrlKey){var i=e.ui.keyCode,s=this.headers.length,n=this.headers.index(t.target),a=!1;switch(t.keyCode){case i.RIGHT:case i.DOWN:a=this.headers[(n+1)%s];break;case i.LEFT:case i.UP:a=this.headers[(n-1+s)%s];break;case i.SPACE:case i.ENTER:this._eventHandler(t);break;case i.HOME:a=this.headers[0];break;case i.END:a=this.headers[s-1]}a&&(e(t.target).attr("tabIndex",-1),e(a).attr("tabIndex",0),a.focus(),t.preventDefault())}},_panelKeyDown:function(t){t.keyCode===e.ui.keyCode.UP&&t.ctrlKey&&e(t.currentTarget).prev().focus()},refresh:function(){var t=this.options;this._processPanels(),t.active===!1&&t.collapsible===!0||!this.headers.length?(t.active=!1,this.active=e()):t.active===!1?this._activate(0):this.active.length&&!e.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(t.active=!1,this.active=e()):this._activate(Math.max(0,t.active-1)):t.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){var e=this.headers,t=this.panels;this.headers=this.element.find(this.options.header).addClass("ui-accordion-header ui-state-default ui-corner-all"),this.panels=this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom").filter(":not(.ui-accordion-content-active)").hide(),t&&(this._off(e.not(this.headers)),this._off(t.not(this.panels)))},_refresh:function(){var t,i=this.options,s=i.heightStyle,n=this.element.parent();this.active=this._findActive(i.active).addClass("ui-accordion-header-active ui-state-active ui-corner-top").removeClass("ui-corner-all"),this.active.next().addClass("ui-accordion-content-active").show(),this.headers.attr("role","tab").each(function(){var t=e(this),i=t.uniqueId().attr("id"),s=t.next(),n=s.uniqueId().attr("id");t.attr("aria-controls",n),s.attr("aria-labelledby",i)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(i.event),"fill"===s?(t=n.height(),this.element.siblings(":visible").each(function(){var i=e(this),s=i.css("position");"absolute"!==s&&"fixed"!==s&&(t-=i.outerHeight(!0))}),this.headers.each(function(){t-=e(this).outerHeight(!0)}),this.headers.next().each(function(){e(this).height(Math.max(0,t-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===s&&(t=0,this.headers.next().each(function(){t=Math.max(t,e(this).css("height","").height())}).height(t))},_activate:function(t){var i=this._findActive(t)[0];i!==this.active[0]&&(i=i||this.active[0],this._eventHandler({target:i,currentTarget:i,preventDefault:e.noop}))},_findActive:function(t){return"number"==typeof t?this.headers.eq(t):e()},_setupEvents:function(t){var i={keydown:"_keydown"};t&&e.each(t.split(" "),function(e,t){i[t]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(t){var i=this.options,s=this.active,n=e(t.currentTarget),a=n[0]===s[0],o=a&&i.collapsible,r=o?e():n.next(),h=s.next(),l={oldHeader:s,oldPanel:h,newHeader:o?e():n,newPanel:r};t.preventDefault(),a&&!i.collapsible||this._trigger("beforeActivate",t,l)===!1||(i.active=o?!1:this.headers.index(n),this.active=a?e():n,this._toggle(l),s.removeClass("ui-accordion-header-active ui-state-active"),i.icons&&s.children(".ui-accordion-header-icon").removeClass(i.icons.activeHeader).addClass(i.icons.header),a||(n.removeClass("ui-corner-all").addClass("ui-accordion-header-active ui-state-active ui-corner-top"),i.icons&&n.children(".ui-accordion-header-icon").removeClass(i.icons.header).addClass(i.icons.activeHeader),n.next().addClass("ui-accordion-content-active")))},_toggle:function(t){var i=t.newPanel,s=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=i,this.prevHide=s,this.options.animate?this._animate(i,s,t):(s.hide(),i.show(),this._toggleComplete(t)),s.attr({"aria-hidden":"true"}),s.prev().attr("aria-selected","false"),i.length&&s.length?s.prev().attr({tabIndex:-1,"aria-expanded":"false"}):i.length&&this.headers.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),i.attr("aria-hidden","false").prev().attr({"aria-selected":"true",tabIndex:0,"aria-expanded":"true"})},_animate:function(e,t,i){var s,n,a,o=this,r=0,h=e.length&&(!t.length||e.index()<t.index()),l=this.options.animate||{},u=h&&l.down||l,d=function(){o._toggleComplete(i)};return"number"==typeof u&&(a=u),"string"==typeof u&&(n=u),n=n||u.easing||l.easing,a=a||u.duration||l.duration,t.length?e.length?(s=e.show().outerHeight(),t.animate(this.hideProps,{duration:a,easing:n,step:function(e,t){t.now=Math.round(e)}}),e.hide().animate(this.showProps,{duration:a,easing:n,complete:d,step:function(e,i){i.now=Math.round(e),"height"!==i.prop?r+=i.now:"content"!==o.options.heightStyle&&(i.now=Math.round(s-t.outerHeight()-r),r=0)}}),void 0):t.animate(this.hideProps,a,n,d):e.animate(this.showProps,a,n,d)},_toggleComplete:function(e){var t=e.oldPanel;t.removeClass("ui-accordion-content-active").prev().removeClass("ui-corner-top").addClass("ui-corner-all"),t.length&&(t.parent()[0].className=t.parent()[0].className),this._trigger("activate",null,e)}}),e.widget("ui.menu",{version:"1.11.2",defaultElement:"<ul>",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},items:"> *",menus:"ul",position:{my:"left-1 top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item":function(e){e.preventDefault()},"click .ui-menu-item":function(t){var i=e(t.target);!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(t),t.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(t):!this.element.is(":focus")&&e(this.document[0].activeElement).closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(t){if(!this.previousFilter){var i=e(t.currentTarget);i.siblings(".ui-state-active").removeClass("ui-state-active"),this.focus(t,i) +}},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(e,t){var i=this.active||this.element.find(this.options.items).eq(0);t||this.focus(e,i)},blur:function(t){this._delay(function(){e.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(e){this._closeOnDocumentClick(e)&&this.collapseAll(e),this.mouseHandled=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeClass("ui-menu ui-widget ui-widget-content ui-menu-icons ui-front").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").removeUniqueId().removeClass("ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var t=e(this);t.data("ui-menu-submenu-carat")&&t.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(t){var i,s,n,a,o=!0;switch(t.keyCode){case e.ui.keyCode.PAGE_UP:this.previousPage(t);break;case e.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case e.ui.keyCode.HOME:this._move("first","first",t);break;case e.ui.keyCode.END:this._move("last","last",t);break;case e.ui.keyCode.UP:this.previous(t);break;case e.ui.keyCode.DOWN:this.next(t);break;case e.ui.keyCode.LEFT:this.collapse(t);break;case e.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case e.ui.keyCode.ENTER:case e.ui.keyCode.SPACE:this._activate(t);break;case e.ui.keyCode.ESCAPE:this.collapse(t);break;default:o=!1,s=this.previousFilter||"",n=String.fromCharCode(t.keyCode),a=!1,clearTimeout(this.filterTimer),n===s?a=!0:n=s+n,i=this._filterMenuItems(n),i=a&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(n=String.fromCharCode(t.keyCode),i=this._filterMenuItems(n)),i.length?(this.focus(t,i),this.previousFilter=n,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}o&&t.preventDefault()},_activate:function(e){this.active.is(".ui-state-disabled")||(this.active.is("[aria-haspopup='true']")?this.expand(e):this.select(e))},refresh:function(){var t,i,s=this,n=this.options.icons.submenu,a=this.element.find(this.options.menus);this.element.toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length),a.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-front").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var t=e(this),i=t.parent(),s=e("<span>").addClass("ui-menu-icon ui-icon "+n).data("ui-menu-submenu-carat",!0);i.attr("aria-haspopup","true").prepend(s),t.attr("aria-labelledby",i.attr("id"))}),t=a.add(this.element),i=t.find(this.options.items),i.not(".ui-menu-item").each(function(){var t=e(this);s._isDivider(t)&&t.addClass("ui-widget-content ui-menu-divider")}),i.not(".ui-menu-item, .ui-menu-divider").addClass("ui-menu-item").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),i.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!e.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(e,t){"icons"===e&&this.element.find(".ui-menu-icon").removeClass(this.options.icons.submenu).addClass(t.submenu),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},focus:function(e,t){var i,s;this.blur(e,e&&"focus"===e.type),this._scrollIntoView(t),this.active=t.first(),s=this.active.addClass("ui-state-focus").removeClass("ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),this.active.parent().closest(".ui-menu-item").addClass("ui-state-active"),e&&"keydown"===e.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=t.children(".ui-menu"),i.length&&e&&/^mouse/.test(e.type)&&this._startOpening(i),this.activeMenu=t.parent(),this._trigger("focus",e,{item:t})},_scrollIntoView:function(t){var i,s,n,a,o,r;this._hasScroll()&&(i=parseFloat(e.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(e.css(this.activeMenu[0],"paddingTop"))||0,n=t.offset().top-this.activeMenu.offset().top-i-s,a=this.activeMenu.scrollTop(),o=this.activeMenu.height(),r=t.outerHeight(),0>n?this.activeMenu.scrollTop(a+n):n+r>o&&this.activeMenu.scrollTop(a+n-o+r))},blur:function(e,t){t||clearTimeout(this.timer),this.active&&(this.active.removeClass("ui-state-focus"),this.active=null,this._trigger("blur",e,{item:this.active}))},_startOpening:function(e){clearTimeout(this.timer),"true"===e.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(e)},this.delay))},_open:function(t){var i=e.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(t.parents(".ui-menu")).hide().attr("aria-hidden","true"),t.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(t,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:e(t&&t.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(t),this.activeMenu=s},this.delay)},_close:function(e){e||(e=this.active?this.active.parent():this.element),e.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find(".ui-state-active").not(".ui-state-focus").removeClass("ui-state-active")},_closeOnDocumentClick:function(t){return!e(t.target).closest(".ui-menu").length},_isDivider:function(e){return!/[^\-\u2014\u2013\s]/.test(e.text())},collapse:function(e){var t=this.active&&this.active.parent().closest(".ui-menu-item",this.element);t&&t.length&&(this._close(),this.focus(e,t))},expand:function(e){var t=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();t&&t.length&&(this._open(t.parent()),this._delay(function(){this.focus(e,t)}))},next:function(e){this._move("next","first",e)},previous:function(e){this._move("prev","last",e)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(e,t,i){var s;this.active&&(s="first"===e||"last"===e?this.active["first"===e?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[e+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.find(this.options.items)[t]()),this.focus(i,s)},nextPage:function(t){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=e(this),0>i.offset().top-s-n}),this.focus(t,i)):this.focus(t,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(t),void 0)},previousPage:function(t){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=e(this),i.offset().top-s+n>0}),this.focus(t,i)):this.focus(t,this.activeMenu.find(this.options.items).first())),void 0):(this.next(t),void 0)},_hasScroll:function(){return this.element.outerHeight()<this.element.prop("scrollHeight")},select:function(t){this.active=this.active||e(t.target).closest(".ui-menu-item");var i={item:this.active};this.active.has(".ui-menu").length||this.collapseAll(t,!0),this._trigger("select",t,i)},_filterMenuItems:function(t){var i=t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&"),s=RegExp("^"+i,"i");return this.activeMenu.find(this.options.items).filter(".ui-menu-item").filter(function(){return s.test(e.trim(e(this).text()))})}}),e.widget("ui.autocomplete",{version:"1.11.2",defaultElement:"<input>",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var t,i,s,n=this.element[0].nodeName.toLowerCase(),a="textarea"===n,o="input"===n;this.isMultiLine=a?!0:o?!1:this.element.prop("isContentEditable"),this.valueMethod=this.element[a||o?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return t=!0,s=!0,i=!0,void 0;t=!1,s=!1,i=!1;var a=e.ui.keyCode;switch(n.keyCode){case a.PAGE_UP:t=!0,this._move("previousPage",n);break;case a.PAGE_DOWN:t=!0,this._move("nextPage",n);break;case a.UP:t=!0,this._keyEvent("previous",n);break;case a.DOWN:t=!0,this._keyEvent("next",n);break;case a.ENTER:this.menu.active&&(t=!0,n.preventDefault(),this.menu.select(n));break;case a.TAB:this.menu.active&&this.menu.select(n);break;case a.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(t)return t=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),void 0;if(!i){var n=e.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(e){return s?(s=!1,e.preventDefault(),void 0):(this._searchTimeout(e),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(e),this._change(e),void 0)}}),this._initSource(),this.menu=e("<ul>").addClass("ui-autocomplete ui-front").appendTo(this._appendTo()).menu({role:null}).hide().menu("instance"),this._on(this.menu.element,{mousedown:function(t){t.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur});var i=this.menu.element[0];e(t.target).closest(".ui-menu-item").length||this._delay(function(){var t=this;this.document.one("mousedown",function(s){s.target===t.element[0]||s.target===i||e.contains(i,s.target)||t.close()})})},menufocus:function(t,i){var s,n;return this.isNewMenu&&(this.isNewMenu=!1,t.originalEvent&&/^mouse/.test(t.originalEvent.type))?(this.menu.blur(),this.document.one("mousemove",function(){e(t.target).trigger(t.originalEvent)}),void 0):(n=i.item.data("ui-autocomplete-item"),!1!==this._trigger("focus",t,{item:n})&&t.originalEvent&&/^key/.test(t.originalEvent.type)&&this._value(n.value),s=i.item.attr("aria-label")||n.value,s&&e.trim(s).length&&(this.liveRegion.children().hide(),e("<div>").text(s).appendTo(this.liveRegion)),void 0)},menuselect:function(e,t){var i=t.item.data("ui-autocomplete-item"),s=this.previous;this.element[0]!==this.document[0].activeElement&&(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s,this.selectedItem=i})),!1!==this._trigger("select",e,{item:i})&&this._value(i.value),this.term=this._value(),this.close(e),this.selectedItem=i}}),this.liveRegion=e("<span>",{role:"status","aria-live":"assertive","aria-relevant":"additions"}).addClass("ui-helper-hidden-accessible").appendTo(this.document[0].body),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(e,t){this._super(e,t),"source"===e&&this._initSource(),"appendTo"===e&&this.menu.element.appendTo(this._appendTo()),"disabled"===e&&t&&this.xhr&&this.xhr.abort()},_appendTo:function(){var t=this.options.appendTo;return t&&(t=t.jquery||t.nodeType?e(t):this.document.find(t).eq(0)),t&&t[0]||(t=this.element.closest(".ui-front")),t.length||(t=this.document[0].body),t},_initSource:function(){var t,i,s=this;e.isArray(this.options.source)?(t=this.options.source,this.source=function(i,s){s(e.ui.autocomplete.filter(t,i.term))}):"string"==typeof this.options.source?(i=this.options.source,this.source=function(t,n){s.xhr&&s.xhr.abort(),s.xhr=e.ajax({url:i,data:t,dataType:"json",success:function(e){n(e)},error:function(){n([])}})}):this.source=this.options.source},_searchTimeout:function(e){clearTimeout(this.searching),this.searching=this._delay(function(){var t=this.term===this._value(),i=this.menu.element.is(":visible"),s=e.altKey||e.ctrlKey||e.metaKey||e.shiftKey;(!t||t&&!i&&!s)&&(this.selectedItem=null,this.search(null,e))},this.options.delay)},search:function(e,t){return e=null!=e?e:this._value(),this.term=this._value(),e.length<this.options.minLength?this.close(t):this._trigger("search",t)!==!1?this._search(e):void 0},_search:function(e){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.cancelSearch=!1,this.source({term:e},this._response())},_response:function(){var t=++this.requestIndex;return e.proxy(function(e){t===this.requestIndex&&this.__response(e),this.pending--,this.pending||this.element.removeClass("ui-autocomplete-loading")},this)},__response:function(e){e&&(e=this._normalize(e)),this._trigger("response",null,{content:e}),!this.options.disabled&&e&&e.length&&!this.cancelSearch?(this._suggest(e),this._trigger("open")):this._close()},close:function(e){this.cancelSearch=!0,this._close(e)},_close:function(e){this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.blur(),this.isNewMenu=!0,this._trigger("close",e))},_change:function(e){this.previous!==this._value()&&this._trigger("change",e,{item:this.selectedItem})},_normalize:function(t){return t.length&&t[0].label&&t[0].value?t:e.map(t,function(t){return"string"==typeof t?{label:t,value:t}:e.extend({},t,{label:t.label||t.value,value:t.value||t.label})})},_suggest:function(t){var i=this.menu.element.empty();this._renderMenu(i,t),this.isNewMenu=!0,this.menu.refresh(),i.show(),this._resizeMenu(),i.position(e.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next()},_resizeMenu:function(){var e=this.menu.element;e.outerWidth(Math.max(e.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(t,i){var s=this;e.each(i,function(e,i){s._renderItemData(t,i)})},_renderItemData:function(e,t){return this._renderItem(e,t).data("ui-autocomplete-item",t)},_renderItem:function(t,i){return e("<li>").text(i.label).appendTo(t)},_move:function(e,t){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(e)||this.menu.isLastItem()&&/^next/.test(e)?(this.isMultiLine||this._value(this.term),this.menu.blur(),void 0):(this.menu[e](t),void 0):(this.search(null,t),void 0)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(e,t){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(e,t),t.preventDefault())}}),e.extend(e.ui.autocomplete,{escapeRegex:function(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(t,i){var s=RegExp(e.ui.autocomplete.escapeRegex(i),"i");return e.grep(t,function(e){return s.test(e.label||e.value||e)})}}),e.widget("ui.autocomplete",e.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(e){return e+(e>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(t){var i;this._superApply(arguments),this.options.disabled||this.cancelSearch||(i=t&&t.length?this.options.messages.results(t.length):this.options.messages.noResults,this.liveRegion.children().hide(),e("<div>").text(i).appendTo(this.liveRegion))}}),e.ui.autocomplete;var c,p="ui-button ui-widget ui-state-default ui-corner-all",f="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",m=function(){var t=e(this);setTimeout(function(){t.find(":ui-button").button("refresh")},1)},g=function(t){var i=t.name,s=t.form,n=e([]);return i&&(i=i.replace(/'/g,"\\'"),n=s?e(s).find("[name='"+i+"'][type=radio]"):e("[name='"+i+"'][type=radio]",t.ownerDocument).filter(function(){return!this.form})),n};e.widget("ui.button",{version:"1.11.2",defaultElement:"<button>",options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset"+this.eventNamespace).bind("reset"+this.eventNamespace,m),"boolean"!=typeof this.options.disabled?this.options.disabled=!!this.element.prop("disabled"):this.element.prop("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var t=this,i=this.options,s="checkbox"===this.type||"radio"===this.type,n=s?"":"ui-state-active";null===i.label&&(i.label="input"===this.type?this.buttonElement.val():this.buttonElement.html()),this._hoverable(this.buttonElement),this.buttonElement.addClass(p).attr("role","button").bind("mouseenter"+this.eventNamespace,function(){i.disabled||this===c&&e(this).addClass("ui-state-active")}).bind("mouseleave"+this.eventNamespace,function(){i.disabled||e(this).removeClass(n)}).bind("click"+this.eventNamespace,function(e){i.disabled&&(e.preventDefault(),e.stopImmediatePropagation())}),this._on({focus:function(){this.buttonElement.addClass("ui-state-focus")},blur:function(){this.buttonElement.removeClass("ui-state-focus")}}),s&&this.element.bind("change"+this.eventNamespace,function(){t.refresh()}),"checkbox"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){return i.disabled?!1:void 0}):"radio"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){if(i.disabled)return!1;e(this).addClass("ui-state-active"),t.buttonElement.attr("aria-pressed","true");var s=t.element[0];g(s).not(s).map(function(){return e(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown"+this.eventNamespace,function(){return i.disabled?!1:(e(this).addClass("ui-state-active"),c=this,t.document.one("mouseup",function(){c=null}),void 0)}).bind("mouseup"+this.eventNamespace,function(){return i.disabled?!1:(e(this).removeClass("ui-state-active"),void 0)}).bind("keydown"+this.eventNamespace,function(t){return i.disabled?!1:((t.keyCode===e.ui.keyCode.SPACE||t.keyCode===e.ui.keyCode.ENTER)&&e(this).addClass("ui-state-active"),void 0)}).bind("keyup"+this.eventNamespace+" blur"+this.eventNamespace,function(){e(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(t){t.keyCode===e.ui.keyCode.SPACE&&e(this).click()})),this._setOption("disabled",i.disabled),this._resetButton()},_determineButtonType:function(){var e,t,i;this.type=this.element.is("[type=checkbox]")?"checkbox":this.element.is("[type=radio]")?"radio":this.element.is("input")?"input":"button","checkbox"===this.type||"radio"===this.type?(e=this.element.parents().last(),t="label[for='"+this.element.attr("id")+"']",this.buttonElement=e.find(t),this.buttonElement.length||(e=e.length?e.siblings():this.element.siblings(),this.buttonElement=e.filter(t),this.buttonElement.length||(this.buttonElement=e.find(t))),this.element.addClass("ui-helper-hidden-accessible"),i=this.element.is(":checked"),i&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.prop("aria-pressed",i)):this.buttonElement=this.element},widget:function(){return this.buttonElement},_destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(p+" ui-state-active "+f).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title")},_setOption:function(e,t){return this._super(e,t),"disabled"===e?(this.widget().toggleClass("ui-state-disabled",!!t),this.element.prop("disabled",!!t),t&&("checkbox"===this.type||"radio"===this.type?this.buttonElement.removeClass("ui-state-focus"):this.buttonElement.removeClass("ui-state-focus ui-state-active")),void 0):(this._resetButton(),void 0)},refresh:function(){var t=this.element.is("input, button")?this.element.is(":disabled"):this.element.hasClass("ui-button-disabled");t!==this.options.disabled&&this._setOption("disabled",t),"radio"===this.type?g(this.element[0]).each(function(){e(this).is(":checked")?e(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):e(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):"checkbox"===this.type&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if("input"===this.type)return this.options.label&&this.element.val(this.options.label),void 0;var t=this.buttonElement.removeClass(f),i=e("<span></span>",this.document[0]).addClass("ui-button-text").html(this.options.label).appendTo(t.empty()).text(),s=this.options.icons,n=s.primary&&s.secondary,a=[];s.primary||s.secondary?(this.options.text&&a.push("ui-button-text-icon"+(n?"s":s.primary?"-primary":"-secondary")),s.primary&&t.prepend("<span class='ui-button-icon-primary ui-icon "+s.primary+"'></span>"),s.secondary&&t.append("<span class='ui-button-icon-secondary ui-icon "+s.secondary+"'></span>"),this.options.text||(a.push(n?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||t.attr("title",e.trim(i)))):a.push("ui-button-text-only"),t.addClass(a.join(" "))}}),e.widget("ui.buttonset",{version:"1.11.2",options:{items:"button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(e,t){"disabled"===e&&this.buttons.button("option",e,t),this._super(e,t)},refresh:function(){var t="rtl"===this.element.css("direction"),i=this.element.find(this.options.items),s=i.filter(":ui-button");i.not(":ui-button").button(),s.button("refresh"),this.buttons=i.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(t?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(t?"ui-corner-left":"ui-corner-right").end().end()},_destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy")}}),e.ui.button,e.extend(e.ui,{datepicker:{version:"1.11.2"}});var v;e.extend(n.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(e){return r(this._defaults,e||{}),this},_attachDatepicker:function(t,i){var s,n,a;s=t.nodeName.toLowerCase(),n="div"===s||"span"===s,t.id||(this.uuid+=1,t.id="dp"+this.uuid),a=this._newInst(e(t),n),a.settings=e.extend({},i||{}),"input"===s?this._connectDatepicker(t,a):n&&this._inlineDatepicker(t,a)},_newInst:function(t,i){var s=t[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1");return{id:s,input:t,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:i,dpDiv:i?a(e("<div class='"+this._inlineClass+" ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")):this.dpDiv}},_connectDatepicker:function(t,i){var s=e(t);i.append=e([]),i.trigger=e([]),s.hasClass(this.markerClassName)||(this._attachments(s,i),s.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp),this._autoSize(i),e.data(t,"datepicker",i),i.settings.disabled&&this._disableDatepicker(t))},_attachments:function(t,i){var s,n,a,o=this._get(i,"appendText"),r=this._get(i,"isRTL");i.append&&i.append.remove(),o&&(i.append=e("<span class='"+this._appendClass+"'>"+o+"</span>"),t[r?"before":"after"](i.append)),t.unbind("focus",this._showDatepicker),i.trigger&&i.trigger.remove(),s=this._get(i,"showOn"),("focus"===s||"both"===s)&&t.focus(this._showDatepicker),("button"===s||"both"===s)&&(n=this._get(i,"buttonText"),a=this._get(i,"buttonImage"),i.trigger=e(this._get(i,"buttonImageOnly")?e("<img/>").addClass(this._triggerClass).attr({src:a,alt:n,title:n}):e("<button type='button'></button>").addClass(this._triggerClass).html(a?e("<img/>").attr({src:a,alt:n,title:n}):n)),t[r?"before":"after"](i.trigger),i.trigger.click(function(){return e.datepicker._datepickerShowing&&e.datepicker._lastInput===t[0]?e.datepicker._hideDatepicker():e.datepicker._datepickerShowing&&e.datepicker._lastInput!==t[0]?(e.datepicker._hideDatepicker(),e.datepicker._showDatepicker(t[0])):e.datepicker._showDatepicker(t[0]),!1}))},_autoSize:function(e){if(this._get(e,"autoSize")&&!e.inline){var t,i,s,n,a=new Date(2009,11,20),o=this._get(e,"dateFormat");o.match(/[DM]/)&&(t=function(e){for(i=0,s=0,n=0;e.length>n;n++)e[n].length>i&&(i=e[n].length,s=n);return s},a.setMonth(t(this._get(e,o.match(/MM/)?"monthNames":"monthNamesShort"))),a.setDate(t(this._get(e,o.match(/DD/)?"dayNames":"dayNamesShort"))+20-a.getDay())),e.input.attr("size",this._formatDate(e,a).length)}},_inlineDatepicker:function(t,i){var s=e(t);s.hasClass(this.markerClassName)||(s.addClass(this.markerClassName).append(i.dpDiv),e.data(t,"datepicker",i),this._setDate(i,this._getDefaultDate(i),!0),this._updateDatepicker(i),this._updateAlternate(i),i.settings.disabled&&this._disableDatepicker(t),i.dpDiv.css("display","block"))},_dialogDatepicker:function(t,i,s,n,a){var o,h,l,u,d,c=this._dialogInst;return c||(this.uuid+=1,o="dp"+this.uuid,this._dialogInput=e("<input type='text' id='"+o+"' style='position: absolute; top: -100px; width: 0px;'/>"),this._dialogInput.keydown(this._doKeyDown),e("body").append(this._dialogInput),c=this._dialogInst=this._newInst(this._dialogInput,!1),c.settings={},e.data(this._dialogInput[0],"datepicker",c)),r(c.settings,n||{}),i=i&&i.constructor===Date?this._formatDate(c,i):i,this._dialogInput.val(i),this._pos=a?a.length?a:[a.pageX,a.pageY]:null,this._pos||(h=document.documentElement.clientWidth,l=document.documentElement.clientHeight,u=document.documentElement.scrollLeft||document.body.scrollLeft,d=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[h/2-100+u,l/2-150+d]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),c.settings.onSelect=s,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),e.blockUI&&e.blockUI(this.dpDiv),e.data(this._dialogInput[0],"datepicker",c),this},_destroyDatepicker:function(t){var i,s=e(t),n=e.data(t,"datepicker");s.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),e.removeData(t,"datepicker"),"input"===i?(n.append.remove(),n.trigger.remove(),s.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):("div"===i||"span"===i)&&s.removeClass(this.markerClassName).empty())},_enableDatepicker:function(t){var i,s,n=e(t),a=e.data(t,"datepicker");n.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),"input"===i?(t.disabled=!1,a.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().removeClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}))},_disableDatepicker:function(t){var i,s,n=e(t),a=e.data(t,"datepicker");n.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),"input"===i?(t.disabled=!0,a.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().addClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}),this._disabledInputs[this._disabledInputs.length]=t)},_isDisabledDatepicker:function(e){if(!e)return!1;for(var t=0;this._disabledInputs.length>t;t++)if(this._disabledInputs[t]===e)return!0;return!1},_getInst:function(t){try{return e.data(t,"datepicker")}catch(i){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(t,i,s){var n,a,o,h,l=this._getInst(t);return 2===arguments.length&&"string"==typeof i?"defaults"===i?e.extend({},e.datepicker._defaults):l?"all"===i?e.extend({},l.settings):this._get(l,i):null:(n=i||{},"string"==typeof i&&(n={},n[i]=s),l&&(this._curInst===l&&this._hideDatepicker(),a=this._getDateDatepicker(t,!0),o=this._getMinMaxDate(l,"min"),h=this._getMinMaxDate(l,"max"),r(l.settings,n),null!==o&&void 0!==n.dateFormat&&void 0===n.minDate&&(l.settings.minDate=this._formatDate(l,o)),null!==h&&void 0!==n.dateFormat&&void 0===n.maxDate&&(l.settings.maxDate=this._formatDate(l,h)),"disabled"in n&&(n.disabled?this._disableDatepicker(t):this._enableDatepicker(t)),this._attachments(e(t),l),this._autoSize(l),this._setDate(l,a),this._updateAlternate(l),this._updateDatepicker(l)),void 0)},_changeDatepicker:function(e,t,i){this._optionDatepicker(e,t,i)},_refreshDatepicker:function(e){var t=this._getInst(e);t&&this._updateDatepicker(t)},_setDateDatepicker:function(e,t){var i=this._getInst(e);i&&(this._setDate(i,t),this._updateDatepicker(i),this._updateAlternate(i))},_getDateDatepicker:function(e,t){var i=this._getInst(e);return i&&!i.inline&&this._setDateFromField(i,t),i?this._getDate(i):null},_doKeyDown:function(t){var i,s,n,a=e.datepicker._getInst(t.target),o=!0,r=a.dpDiv.is(".ui-datepicker-rtl");if(a._keyEvent=!0,e.datepicker._datepickerShowing)switch(t.keyCode){case 9:e.datepicker._hideDatepicker(),o=!1;break;case 13:return n=e("td."+e.datepicker._dayOverClass+":not(."+e.datepicker._currentClass+")",a.dpDiv),n[0]&&e.datepicker._selectDay(t.target,a.selectedMonth,a.selectedYear,n[0]),i=e.datepicker._get(a,"onSelect"),i?(s=e.datepicker._formatDate(a),i.apply(a.input?a.input[0]:null,[s,a])):e.datepicker._hideDatepicker(),!1;case 27:e.datepicker._hideDatepicker();break;case 33:e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(a,"stepBigMonths"):-e.datepicker._get(a,"stepMonths"),"M");break;case 34:e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(a,"stepBigMonths"):+e.datepicker._get(a,"stepMonths"),"M");break;case 35:(t.ctrlKey||t.metaKey)&&e.datepicker._clearDate(t.target),o=t.ctrlKey||t.metaKey;break;case 36:(t.ctrlKey||t.metaKey)&&e.datepicker._gotoToday(t.target),o=t.ctrlKey||t.metaKey;break;case 37:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,r?1:-1,"D"),o=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(a,"stepBigMonths"):-e.datepicker._get(a,"stepMonths"),"M");break;case 38:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,-7,"D"),o=t.ctrlKey||t.metaKey;break;case 39:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,r?-1:1,"D"),o=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(a,"stepBigMonths"):+e.datepicker._get(a,"stepMonths"),"M");break;case 40:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,7,"D"),o=t.ctrlKey||t.metaKey;break;default:o=!1}else 36===t.keyCode&&t.ctrlKey?e.datepicker._showDatepicker(this):o=!1;o&&(t.preventDefault(),t.stopPropagation())},_doKeyPress:function(t){var i,s,n=e.datepicker._getInst(t.target);return e.datepicker._get(n,"constrainInput")?(i=e.datepicker._possibleChars(e.datepicker._get(n,"dateFormat")),s=String.fromCharCode(null==t.charCode?t.keyCode:t.charCode),t.ctrlKey||t.metaKey||" ">s||!i||i.indexOf(s)>-1):void 0 +},_doKeyUp:function(t){var i,s=e.datepicker._getInst(t.target);if(s.input.val()!==s.lastVal)try{i=e.datepicker.parseDate(e.datepicker._get(s,"dateFormat"),s.input?s.input.val():null,e.datepicker._getFormatConfig(s)),i&&(e.datepicker._setDateFromField(s),e.datepicker._updateAlternate(s),e.datepicker._updateDatepicker(s))}catch(n){}return!0},_showDatepicker:function(t){if(t=t.target||t,"input"!==t.nodeName.toLowerCase()&&(t=e("input",t.parentNode)[0]),!e.datepicker._isDisabledDatepicker(t)&&e.datepicker._lastInput!==t){var i,n,a,o,h,l,u;i=e.datepicker._getInst(t),e.datepicker._curInst&&e.datepicker._curInst!==i&&(e.datepicker._curInst.dpDiv.stop(!0,!0),i&&e.datepicker._datepickerShowing&&e.datepicker._hideDatepicker(e.datepicker._curInst.input[0])),n=e.datepicker._get(i,"beforeShow"),a=n?n.apply(t,[t,i]):{},a!==!1&&(r(i.settings,a),i.lastVal=null,e.datepicker._lastInput=t,e.datepicker._setDateFromField(i),e.datepicker._inDialog&&(t.value=""),e.datepicker._pos||(e.datepicker._pos=e.datepicker._findPos(t),e.datepicker._pos[1]+=t.offsetHeight),o=!1,e(t).parents().each(function(){return o|="fixed"===e(this).css("position"),!o}),h={left:e.datepicker._pos[0],top:e.datepicker._pos[1]},e.datepicker._pos=null,i.dpDiv.empty(),i.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),e.datepicker._updateDatepicker(i),h=e.datepicker._checkOffset(i,h,o),i.dpDiv.css({position:e.datepicker._inDialog&&e.blockUI?"static":o?"fixed":"absolute",display:"none",left:h.left+"px",top:h.top+"px"}),i.inline||(l=e.datepicker._get(i,"showAnim"),u=e.datepicker._get(i,"duration"),i.dpDiv.css("z-index",s(e(t))+1),e.datepicker._datepickerShowing=!0,e.effects&&e.effects.effect[l]?i.dpDiv.show(l,e.datepicker._get(i,"showOptions"),u):i.dpDiv[l||"show"](l?u:null),e.datepicker._shouldFocusInput(i)&&i.input.focus(),e.datepicker._curInst=i))}},_updateDatepicker:function(t){this.maxRows=4,v=t,t.dpDiv.empty().append(this._generateHTML(t)),this._attachHandlers(t);var i,s=this._getNumberOfMonths(t),n=s[1],a=17,r=t.dpDiv.find("."+this._dayOverClass+" a");r.length>0&&o.apply(r.get(0)),t.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),n>1&&t.dpDiv.addClass("ui-datepicker-multi-"+n).css("width",a*n+"em"),t.dpDiv[(1!==s[0]||1!==s[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),t.dpDiv[(this._get(t,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),t===e.datepicker._curInst&&e.datepicker._datepickerShowing&&e.datepicker._shouldFocusInput(t)&&t.input.focus(),t.yearshtml&&(i=t.yearshtml,setTimeout(function(){i===t.yearshtml&&t.yearshtml&&t.dpDiv.find("select.ui-datepicker-year:first").replaceWith(t.yearshtml),i=t.yearshtml=null},0))},_shouldFocusInput:function(e){return e.input&&e.input.is(":visible")&&!e.input.is(":disabled")&&!e.input.is(":focus")},_checkOffset:function(t,i,s){var n=t.dpDiv.outerWidth(),a=t.dpDiv.outerHeight(),o=t.input?t.input.outerWidth():0,r=t.input?t.input.outerHeight():0,h=document.documentElement.clientWidth+(s?0:e(document).scrollLeft()),l=document.documentElement.clientHeight+(s?0:e(document).scrollTop());return i.left-=this._get(t,"isRTL")?n-o:0,i.left-=s&&i.left===t.input.offset().left?e(document).scrollLeft():0,i.top-=s&&i.top===t.input.offset().top+r?e(document).scrollTop():0,i.left-=Math.min(i.left,i.left+n>h&&h>n?Math.abs(i.left+n-h):0),i.top-=Math.min(i.top,i.top+a>l&&l>a?Math.abs(a+r):0),i},_findPos:function(t){for(var i,s=this._getInst(t),n=this._get(s,"isRTL");t&&("hidden"===t.type||1!==t.nodeType||e.expr.filters.hidden(t));)t=t[n?"previousSibling":"nextSibling"];return i=e(t).offset(),[i.left,i.top]},_hideDatepicker:function(t){var i,s,n,a,o=this._curInst;!o||t&&o!==e.data(t,"datepicker")||this._datepickerShowing&&(i=this._get(o,"showAnim"),s=this._get(o,"duration"),n=function(){e.datepicker._tidyDialog(o)},e.effects&&(e.effects.effect[i]||e.effects[i])?o.dpDiv.hide(i,e.datepicker._get(o,"showOptions"),s,n):o.dpDiv["slideDown"===i?"slideUp":"fadeIn"===i?"fadeOut":"hide"](i?s:null,n),i||n(),this._datepickerShowing=!1,a=this._get(o,"onClose"),a&&a.apply(o.input?o.input[0]:null,[o.input?o.input.val():"",o]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),e.blockUI&&(e.unblockUI(),e("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(e){e.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(t){if(e.datepicker._curInst){var i=e(t.target),s=e.datepicker._getInst(i[0]);(i[0].id!==e.datepicker._mainDivId&&0===i.parents("#"+e.datepicker._mainDivId).length&&!i.hasClass(e.datepicker.markerClassName)&&!i.closest("."+e.datepicker._triggerClass).length&&e.datepicker._datepickerShowing&&(!e.datepicker._inDialog||!e.blockUI)||i.hasClass(e.datepicker.markerClassName)&&e.datepicker._curInst!==s)&&e.datepicker._hideDatepicker()}},_adjustDate:function(t,i,s){var n=e(t),a=this._getInst(n[0]);this._isDisabledDatepicker(n[0])||(this._adjustInstDate(a,i+("M"===s?this._get(a,"showCurrentAtPos"):0),s),this._updateDatepicker(a))},_gotoToday:function(t){var i,s=e(t),n=this._getInst(s[0]);this._get(n,"gotoCurrent")&&n.currentDay?(n.selectedDay=n.currentDay,n.drawMonth=n.selectedMonth=n.currentMonth,n.drawYear=n.selectedYear=n.currentYear):(i=new Date,n.selectedDay=i.getDate(),n.drawMonth=n.selectedMonth=i.getMonth(),n.drawYear=n.selectedYear=i.getFullYear()),this._notifyChange(n),this._adjustDate(s)},_selectMonthYear:function(t,i,s){var n=e(t),a=this._getInst(n[0]);a["selected"+("M"===s?"Month":"Year")]=a["draw"+("M"===s?"Month":"Year")]=parseInt(i.options[i.selectedIndex].value,10),this._notifyChange(a),this._adjustDate(n)},_selectDay:function(t,i,s,n){var a,o=e(t);e(n).hasClass(this._unselectableClass)||this._isDisabledDatepicker(o[0])||(a=this._getInst(o[0]),a.selectedDay=a.currentDay=e("a",n).html(),a.selectedMonth=a.currentMonth=i,a.selectedYear=a.currentYear=s,this._selectDate(t,this._formatDate(a,a.currentDay,a.currentMonth,a.currentYear)))},_clearDate:function(t){var i=e(t);this._selectDate(i,"")},_selectDate:function(t,i){var s,n=e(t),a=this._getInst(n[0]);i=null!=i?i:this._formatDate(a),a.input&&a.input.val(i),this._updateAlternate(a),s=this._get(a,"onSelect"),s?s.apply(a.input?a.input[0]:null,[i,a]):a.input&&a.input.trigger("change"),a.inline?this._updateDatepicker(a):(this._hideDatepicker(),this._lastInput=a.input[0],"object"!=typeof a.input[0]&&a.input.focus(),this._lastInput=null)},_updateAlternate:function(t){var i,s,n,a=this._get(t,"altField");a&&(i=this._get(t,"altFormat")||this._get(t,"dateFormat"),s=this._getDate(t),n=this.formatDate(i,s,this._getFormatConfig(t)),e(a).each(function(){e(this).val(n)}))},noWeekends:function(e){var t=e.getDay();return[t>0&&6>t,""]},iso8601Week:function(e){var t,i=new Date(e.getTime());return i.setDate(i.getDate()+4-(i.getDay()||7)),t=i.getTime(),i.setMonth(0),i.setDate(1),Math.floor(Math.round((t-i)/864e5)/7)+1},parseDate:function(t,i,s){if(null==t||null==i)throw"Invalid arguments";if(i="object"==typeof i?""+i:i+"",""===i)return null;var n,a,o,r,h=0,l=(s?s.shortYearCutoff:null)||this._defaults.shortYearCutoff,u="string"!=typeof l?l:(new Date).getFullYear()%100+parseInt(l,10),d=(s?s.dayNamesShort:null)||this._defaults.dayNamesShort,c=(s?s.dayNames:null)||this._defaults.dayNames,p=(s?s.monthNamesShort:null)||this._defaults.monthNamesShort,f=(s?s.monthNames:null)||this._defaults.monthNames,m=-1,g=-1,v=-1,y=-1,b=!1,_=function(e){var i=t.length>n+1&&t.charAt(n+1)===e;return i&&n++,i},x=function(e){var t=_(e),s="@"===e?14:"!"===e?20:"y"===e&&t?4:"o"===e?3:2,n="y"===e?s:1,a=RegExp("^\\d{"+n+","+s+"}"),o=i.substring(h).match(a);if(!o)throw"Missing number at position "+h;return h+=o[0].length,parseInt(o[0],10)},w=function(t,s,n){var a=-1,o=e.map(_(t)?n:s,function(e,t){return[[t,e]]}).sort(function(e,t){return-(e[1].length-t[1].length)});if(e.each(o,function(e,t){var s=t[1];return i.substr(h,s.length).toLowerCase()===s.toLowerCase()?(a=t[0],h+=s.length,!1):void 0}),-1!==a)return a+1;throw"Unknown name at position "+h},k=function(){if(i.charAt(h)!==t.charAt(n))throw"Unexpected literal at position "+h;h++};for(n=0;t.length>n;n++)if(b)"'"!==t.charAt(n)||_("'")?k():b=!1;else switch(t.charAt(n)){case"d":v=x("d");break;case"D":w("D",d,c);break;case"o":y=x("o");break;case"m":g=x("m");break;case"M":g=w("M",p,f);break;case"y":m=x("y");break;case"@":r=new Date(x("@")),m=r.getFullYear(),g=r.getMonth()+1,v=r.getDate();break;case"!":r=new Date((x("!")-this._ticksTo1970)/1e4),m=r.getFullYear(),g=r.getMonth()+1,v=r.getDate();break;case"'":_("'")?k():b=!0;break;default:k()}if(i.length>h&&(o=i.substr(h),!/^\s+/.test(o)))throw"Extra/unparsed characters found in date: "+o;if(-1===m?m=(new Date).getFullYear():100>m&&(m+=(new Date).getFullYear()-(new Date).getFullYear()%100+(u>=m?0:-100)),y>-1)for(g=1,v=y;;){if(a=this._getDaysInMonth(m,g-1),a>=v)break;g++,v-=a}if(r=this._daylightSavingAdjust(new Date(m,g-1,v)),r.getFullYear()!==m||r.getMonth()+1!==g||r.getDate()!==v)throw"Invalid date";return r},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:1e7*60*60*24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925)),formatDate:function(e,t,i){if(!t)return"";var s,n=(i?i.dayNamesShort:null)||this._defaults.dayNamesShort,a=(i?i.dayNames:null)||this._defaults.dayNames,o=(i?i.monthNamesShort:null)||this._defaults.monthNamesShort,r=(i?i.monthNames:null)||this._defaults.monthNames,h=function(t){var i=e.length>s+1&&e.charAt(s+1)===t;return i&&s++,i},l=function(e,t,i){var s=""+t;if(h(e))for(;i>s.length;)s="0"+s;return s},u=function(e,t,i,s){return h(e)?s[t]:i[t]},d="",c=!1;if(t)for(s=0;e.length>s;s++)if(c)"'"!==e.charAt(s)||h("'")?d+=e.charAt(s):c=!1;else switch(e.charAt(s)){case"d":d+=l("d",t.getDate(),2);break;case"D":d+=u("D",t.getDay(),n,a);break;case"o":d+=l("o",Math.round((new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime()-new Date(t.getFullYear(),0,0).getTime())/864e5),3);break;case"m":d+=l("m",t.getMonth()+1,2);break;case"M":d+=u("M",t.getMonth(),o,r);break;case"y":d+=h("y")?t.getFullYear():(10>t.getYear()%100?"0":"")+t.getYear()%100;break;case"@":d+=t.getTime();break;case"!":d+=1e4*t.getTime()+this._ticksTo1970;break;case"'":h("'")?d+="'":c=!0;break;default:d+=e.charAt(s)}return d},_possibleChars:function(e){var t,i="",s=!1,n=function(i){var s=e.length>t+1&&e.charAt(t+1)===i;return s&&t++,s};for(t=0;e.length>t;t++)if(s)"'"!==e.charAt(t)||n("'")?i+=e.charAt(t):s=!1;else switch(e.charAt(t)){case"d":case"m":case"y":case"@":i+="0123456789";break;case"D":case"M":return null;case"'":n("'")?i+="'":s=!0;break;default:i+=e.charAt(t)}return i},_get:function(e,t){return void 0!==e.settings[t]?e.settings[t]:this._defaults[t]},_setDateFromField:function(e,t){if(e.input.val()!==e.lastVal){var i=this._get(e,"dateFormat"),s=e.lastVal=e.input?e.input.val():null,n=this._getDefaultDate(e),a=n,o=this._getFormatConfig(e);try{a=this.parseDate(i,s,o)||n}catch(r){s=t?"":s}e.selectedDay=a.getDate(),e.drawMonth=e.selectedMonth=a.getMonth(),e.drawYear=e.selectedYear=a.getFullYear(),e.currentDay=s?a.getDate():0,e.currentMonth=s?a.getMonth():0,e.currentYear=s?a.getFullYear():0,this._adjustInstDate(e)}},_getDefaultDate:function(e){return this._restrictMinMax(e,this._determineDate(e,this._get(e,"defaultDate"),new Date))},_determineDate:function(t,i,s){var n=function(e){var t=new Date;return t.setDate(t.getDate()+e),t},a=function(i){try{return e.datepicker.parseDate(e.datepicker._get(t,"dateFormat"),i,e.datepicker._getFormatConfig(t))}catch(s){}for(var n=(i.toLowerCase().match(/^c/)?e.datepicker._getDate(t):null)||new Date,a=n.getFullYear(),o=n.getMonth(),r=n.getDate(),h=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,l=h.exec(i);l;){switch(l[2]||"d"){case"d":case"D":r+=parseInt(l[1],10);break;case"w":case"W":r+=7*parseInt(l[1],10);break;case"m":case"M":o+=parseInt(l[1],10),r=Math.min(r,e.datepicker._getDaysInMonth(a,o));break;case"y":case"Y":a+=parseInt(l[1],10),r=Math.min(r,e.datepicker._getDaysInMonth(a,o))}l=h.exec(i)}return new Date(a,o,r)},o=null==i||""===i?s:"string"==typeof i?a(i):"number"==typeof i?isNaN(i)?s:n(i):new Date(i.getTime());return o=o&&"Invalid Date"==""+o?s:o,o&&(o.setHours(0),o.setMinutes(0),o.setSeconds(0),o.setMilliseconds(0)),this._daylightSavingAdjust(o)},_daylightSavingAdjust:function(e){return e?(e.setHours(e.getHours()>12?e.getHours()+2:0),e):null},_setDate:function(e,t,i){var s=!t,n=e.selectedMonth,a=e.selectedYear,o=this._restrictMinMax(e,this._determineDate(e,t,new Date));e.selectedDay=e.currentDay=o.getDate(),e.drawMonth=e.selectedMonth=e.currentMonth=o.getMonth(),e.drawYear=e.selectedYear=e.currentYear=o.getFullYear(),n===e.selectedMonth&&a===e.selectedYear||i||this._notifyChange(e),this._adjustInstDate(e),e.input&&e.input.val(s?"":this._formatDate(e))},_getDate:function(e){var t=!e.currentYear||e.input&&""===e.input.val()?null:this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return t},_attachHandlers:function(t){var i=this._get(t,"stepMonths"),s="#"+t.id.replace(/\\\\/g,"\\");t.dpDiv.find("[data-handler]").map(function(){var t={prev:function(){e.datepicker._adjustDate(s,-i,"M")},next:function(){e.datepicker._adjustDate(s,+i,"M")},hide:function(){e.datepicker._hideDatepicker()},today:function(){e.datepicker._gotoToday(s)},selectDay:function(){return e.datepicker._selectDay(s,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return e.datepicker._selectMonthYear(s,this,"M"),!1},selectYear:function(){return e.datepicker._selectMonthYear(s,this,"Y"),!1}};e(this).bind(this.getAttribute("data-event"),t[this.getAttribute("data-handler")])})},_generateHTML:function(e){var t,i,s,n,a,o,r,h,l,u,d,c,p,f,m,g,v,y,b,_,x,w,k,T,D,S,M,C,N,A,P,I,z,H,F,E,O,j,W,L=new Date,R=this._daylightSavingAdjust(new Date(L.getFullYear(),L.getMonth(),L.getDate())),Y=this._get(e,"isRTL"),B=this._get(e,"showButtonPanel"),J=this._get(e,"hideIfNoPrevNext"),q=this._get(e,"navigationAsDateFormat"),K=this._getNumberOfMonths(e),V=this._get(e,"showCurrentAtPos"),U=this._get(e,"stepMonths"),Q=1!==K[0]||1!==K[1],G=this._daylightSavingAdjust(e.currentDay?new Date(e.currentYear,e.currentMonth,e.currentDay):new Date(9999,9,9)),X=this._getMinMaxDate(e,"min"),$=this._getMinMaxDate(e,"max"),Z=e.drawMonth-V,et=e.drawYear;if(0>Z&&(Z+=12,et--),$)for(t=this._daylightSavingAdjust(new Date($.getFullYear(),$.getMonth()-K[0]*K[1]+1,$.getDate())),t=X&&X>t?X:t;this._daylightSavingAdjust(new Date(et,Z,1))>t;)Z--,0>Z&&(Z=11,et--);for(e.drawMonth=Z,e.drawYear=et,i=this._get(e,"prevText"),i=q?this.formatDate(i,this._daylightSavingAdjust(new Date(et,Z-U,1)),this._getFormatConfig(e)):i,s=this._canAdjustMonth(e,-1,et,Z)?"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"e":"w")+"'>"+i+"</span></a>":J?"":"<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"e":"w")+"'>"+i+"</span></a>",n=this._get(e,"nextText"),n=q?this.formatDate(n,this._daylightSavingAdjust(new Date(et,Z+U,1)),this._getFormatConfig(e)):n,a=this._canAdjustMonth(e,1,et,Z)?"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click' title='"+n+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"w":"e")+"'>"+n+"</span></a>":J?"":"<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+n+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"w":"e")+"'>"+n+"</span></a>",o=this._get(e,"currentText"),r=this._get(e,"gotoCurrent")&&e.currentDay?G:R,o=q?this.formatDate(o,r,this._getFormatConfig(e)):o,h=e.inline?"":"<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>"+this._get(e,"closeText")+"</button>",l=B?"<div class='ui-datepicker-buttonpane ui-widget-content'>"+(Y?h:"")+(this._isInRange(e,r)?"<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'>"+o+"</button>":"")+(Y?"":h)+"</div>":"",u=parseInt(this._get(e,"firstDay"),10),u=isNaN(u)?0:u,d=this._get(e,"showWeek"),c=this._get(e,"dayNames"),p=this._get(e,"dayNamesMin"),f=this._get(e,"monthNames"),m=this._get(e,"monthNamesShort"),g=this._get(e,"beforeShowDay"),v=this._get(e,"showOtherMonths"),y=this._get(e,"selectOtherMonths"),b=this._getDefaultDate(e),_="",w=0;K[0]>w;w++){for(k="",this.maxRows=4,T=0;K[1]>T;T++){if(D=this._daylightSavingAdjust(new Date(et,Z,e.selectedDay)),S=" ui-corner-all",M="",Q){if(M+="<div class='ui-datepicker-group",K[1]>1)switch(T){case 0:M+=" ui-datepicker-group-first",S=" ui-corner-"+(Y?"right":"left");break;case K[1]-1:M+=" ui-datepicker-group-last",S=" ui-corner-"+(Y?"left":"right");break;default:M+=" ui-datepicker-group-middle",S=""}M+="'>"}for(M+="<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix"+S+"'>"+(/all|left/.test(S)&&0===w?Y?a:s:"")+(/all|right/.test(S)&&0===w?Y?s:a:"")+this._generateMonthYearHeader(e,Z,et,X,$,w>0||T>0,f,m)+"</div><table class='ui-datepicker-calendar'><thead>"+"<tr>",C=d?"<th class='ui-datepicker-week-col'>"+this._get(e,"weekHeader")+"</th>":"",x=0;7>x;x++)N=(x+u)%7,C+="<th scope='col'"+((x+u+6)%7>=5?" class='ui-datepicker-week-end'":"")+">"+"<span title='"+c[N]+"'>"+p[N]+"</span></th>";for(M+=C+"</tr></thead><tbody>",A=this._getDaysInMonth(et,Z),et===e.selectedYear&&Z===e.selectedMonth&&(e.selectedDay=Math.min(e.selectedDay,A)),P=(this._getFirstDayOfMonth(et,Z)-u+7)%7,I=Math.ceil((P+A)/7),z=Q?this.maxRows>I?this.maxRows:I:I,this.maxRows=z,H=this._daylightSavingAdjust(new Date(et,Z,1-P)),F=0;z>F;F++){for(M+="<tr>",E=d?"<td class='ui-datepicker-week-col'>"+this._get(e,"calculateWeek")(H)+"</td>":"",x=0;7>x;x++)O=g?g.apply(e.input?e.input[0]:null,[H]):[!0,""],j=H.getMonth()!==Z,W=j&&!y||!O[0]||X&&X>H||$&&H>$,E+="<td class='"+((x+u+6)%7>=5?" ui-datepicker-week-end":"")+(j?" ui-datepicker-other-month":"")+(H.getTime()===D.getTime()&&Z===e.selectedMonth&&e._keyEvent||b.getTime()===H.getTime()&&b.getTime()===D.getTime()?" "+this._dayOverClass:"")+(W?" "+this._unselectableClass+" ui-state-disabled":"")+(j&&!v?"":" "+O[1]+(H.getTime()===G.getTime()?" "+this._currentClass:"")+(H.getTime()===R.getTime()?" ui-datepicker-today":""))+"'"+(j&&!v||!O[2]?"":" title='"+O[2].replace(/'/g,"'")+"'")+(W?"":" data-handler='selectDay' data-event='click' data-month='"+H.getMonth()+"' data-year='"+H.getFullYear()+"'")+">"+(j&&!v?" ":W?"<span class='ui-state-default'>"+H.getDate()+"</span>":"<a class='ui-state-default"+(H.getTime()===R.getTime()?" ui-state-highlight":"")+(H.getTime()===G.getTime()?" ui-state-active":"")+(j?" ui-priority-secondary":"")+"' href='#'>"+H.getDate()+"</a>")+"</td>",H.setDate(H.getDate()+1),H=this._daylightSavingAdjust(H);M+=E+"</tr>"}Z++,Z>11&&(Z=0,et++),M+="</tbody></table>"+(Q?"</div>"+(K[0]>0&&T===K[1]-1?"<div class='ui-datepicker-row-break'></div>":""):""),k+=M}_+=k}return _+=l,e._keyEvent=!1,_},_generateMonthYearHeader:function(e,t,i,s,n,a,o,r){var h,l,u,d,c,p,f,m,g=this._get(e,"changeMonth"),v=this._get(e,"changeYear"),y=this._get(e,"showMonthAfterYear"),b="<div class='ui-datepicker-title'>",_="";if(a||!g)_+="<span class='ui-datepicker-month'>"+o[t]+"</span>";else{for(h=s&&s.getFullYear()===i,l=n&&n.getFullYear()===i,_+="<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>",u=0;12>u;u++)(!h||u>=s.getMonth())&&(!l||n.getMonth()>=u)&&(_+="<option value='"+u+"'"+(u===t?" selected='selected'":"")+">"+r[u]+"</option>");_+="</select>"}if(y||(b+=_+(!a&&g&&v?"":" ")),!e.yearshtml)if(e.yearshtml="",a||!v)b+="<span class='ui-datepicker-year'>"+i+"</span>";else{for(d=this._get(e,"yearRange").split(":"),c=(new Date).getFullYear(),p=function(e){var t=e.match(/c[+\-].*/)?i+parseInt(e.substring(1),10):e.match(/[+\-].*/)?c+parseInt(e,10):parseInt(e,10);return isNaN(t)?c:t},f=p(d[0]),m=Math.max(f,p(d[1]||"")),f=s?Math.max(f,s.getFullYear()):f,m=n?Math.min(m,n.getFullYear()):m,e.yearshtml+="<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";m>=f;f++)e.yearshtml+="<option value='"+f+"'"+(f===i?" selected='selected'":"")+">"+f+"</option>";e.yearshtml+="</select>",b+=e.yearshtml,e.yearshtml=null}return b+=this._get(e,"yearSuffix"),y&&(b+=(!a&&g&&v?"":" ")+_),b+="</div>"},_adjustInstDate:function(e,t,i){var s=e.drawYear+("Y"===i?t:0),n=e.drawMonth+("M"===i?t:0),a=Math.min(e.selectedDay,this._getDaysInMonth(s,n))+("D"===i?t:0),o=this._restrictMinMax(e,this._daylightSavingAdjust(new Date(s,n,a)));e.selectedDay=o.getDate(),e.drawMonth=e.selectedMonth=o.getMonth(),e.drawYear=e.selectedYear=o.getFullYear(),("M"===i||"Y"===i)&&this._notifyChange(e)},_restrictMinMax:function(e,t){var i=this._getMinMaxDate(e,"min"),s=this._getMinMaxDate(e,"max"),n=i&&i>t?i:t;return s&&n>s?s:n},_notifyChange:function(e){var t=this._get(e,"onChangeMonthYear");t&&t.apply(e.input?e.input[0]:null,[e.selectedYear,e.selectedMonth+1,e])},_getNumberOfMonths:function(e){var t=this._get(e,"numberOfMonths");return null==t?[1,1]:"number"==typeof t?[1,t]:t},_getMinMaxDate:function(e,t){return this._determineDate(e,this._get(e,t+"Date"),null)},_getDaysInMonth:function(e,t){return 32-this._daylightSavingAdjust(new Date(e,t,32)).getDate()},_getFirstDayOfMonth:function(e,t){return new Date(e,t,1).getDay()},_canAdjustMonth:function(e,t,i,s){var n=this._getNumberOfMonths(e),a=this._daylightSavingAdjust(new Date(i,s+(0>t?t:n[0]*n[1]),1));return 0>t&&a.setDate(this._getDaysInMonth(a.getFullYear(),a.getMonth())),this._isInRange(e,a)},_isInRange:function(e,t){var i,s,n=this._getMinMaxDate(e,"min"),a=this._getMinMaxDate(e,"max"),o=null,r=null,h=this._get(e,"yearRange");return h&&(i=h.split(":"),s=(new Date).getFullYear(),o=parseInt(i[0],10),r=parseInt(i[1],10),i[0].match(/[+\-].*/)&&(o+=s),i[1].match(/[+\-].*/)&&(r+=s)),(!n||t.getTime()>=n.getTime())&&(!a||t.getTime()<=a.getTime())&&(!o||t.getFullYear()>=o)&&(!r||r>=t.getFullYear())},_getFormatConfig:function(e){var t=this._get(e,"shortYearCutoff");return t="string"!=typeof t?t:(new Date).getFullYear()%100+parseInt(t,10),{shortYearCutoff:t,dayNamesShort:this._get(e,"dayNamesShort"),dayNames:this._get(e,"dayNames"),monthNamesShort:this._get(e,"monthNamesShort"),monthNames:this._get(e,"monthNames")}},_formatDate:function(e,t,i,s){t||(e.currentDay=e.selectedDay,e.currentMonth=e.selectedMonth,e.currentYear=e.selectedYear);var n=t?"object"==typeof t?t:this._daylightSavingAdjust(new Date(s,i,t)):this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return this.formatDate(this._get(e,"dateFormat"),n,this._getFormatConfig(e))}}),e.fn.datepicker=function(t){if(!this.length)return this;e.datepicker.initialized||(e(document).mousedown(e.datepicker._checkExternalClick),e.datepicker.initialized=!0),0===e("#"+e.datepicker._mainDivId).length&&e("body").append(e.datepicker.dpDiv);var i=Array.prototype.slice.call(arguments,1);return"string"!=typeof t||"isDisabled"!==t&&"getDate"!==t&&"widget"!==t?"option"===t&&2===arguments.length&&"string"==typeof arguments[1]?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i)):this.each(function(){"string"==typeof t?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this].concat(i)):e.datepicker._attachDatepicker(this,t)}):e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i))},e.datepicker=new n,e.datepicker.initialized=!1,e.datepicker.uuid=(new Date).getTime(),e.datepicker.version="1.11.2",e.datepicker,e.widget("ui.draggable",e.ui.mouse,{version:"1.11.2",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._setHandleClassName(),this._mouseInit()},_setOption:function(e,t){this._super(e,t),"handle"===e&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){return(this.helper||this.element).is(".ui-draggable-dragging")?(this.destroyOnClear=!0,void 0):(this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(t){var i=this.options;return this._blurActiveElement(t),this.helper||i.disabled||e(t.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(t),this.handle?(this._blockFrames(i.iframeFix===!0?"iframe":i.iframeFix),!0):!1)},_blockFrames:function(t){this.iframeBlocks=this.document.find(t).map(function(){var t=e(this);return e("<div>").css("position","absolute").appendTo(t.parent()).outerWidth(t.outerWidth()).outerHeight(t.outerHeight()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(t){var i=this.document[0];if(this.handleElement.is(t.target))try{i.activeElement&&"body"!==i.activeElement.nodeName.toLowerCase()&&e(i.activeElement).blur()}catch(s){}},_mouseStart:function(t){var i=this.options;return this.helper=this._createHelper(t),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),e.ui.ddmanager&&(e.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=this.helper.parents().filter(function(){return"fixed"===e(this).css("position")}).length>0,this.positionAbs=this.element.offset(),this._refreshOffsets(t),this.originalPosition=this.position=this._generatePosition(t,!1),this.originalPageX=t.pageX,this.originalPageY=t.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",t)===!1?(this._clear(),!1):(this._cacheHelperProportions(),e.ui.ddmanager&&!i.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this._normalizeRightBottom(),this._mouseDrag(t,!0),e.ui.ddmanager&&e.ui.ddmanager.dragStart(this,t),!0)},_refreshOffsets:function(e){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:e.pageX-this.offset.left,top:e.pageY-this.offset.top}},_mouseDrag:function(t,i){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(t,!0),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",t,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var i=this,s=!1;return e.ui.ddmanager&&!this.options.dropBehaviour&&(s=e.ui.ddmanager.drop(this,t)),this.dropped&&(s=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||e.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?e(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",t)!==!1&&i._clear()}):this._trigger("stop",t)!==!1&&this._clear(),!1},_mouseUp:function(t){return this._unblockFrames(),e.ui.ddmanager&&e.ui.ddmanager.dragStop(this,t),this.handleElement.is(t.target)&&this.element.focus(),e.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(t){return this.options.handle?!!e(t.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this.handleElement.addClass("ui-draggable-handle")},_removeHandleClassName:function(){this.handleElement.removeClass("ui-draggable-handle")},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper),n=s?e(i.helper.apply(this.element[0],[t])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return n.parents("body").length||n.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s&&n[0]===this.element[0]&&this._setPositionRelative(),n[0]===this.element[0]||/(fixed|absolute)/.test(n.css("position"))||n.css("position","absolute"),n},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative")},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_isRootNode:function(e){return/(html|body)/i.test(e.tagName)||e===this.document[0]},_getParentOffset:function(){var t=this.offsetParent.offset(),i=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==i&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var e=this.element.position(),t=this._isRootNode(this.scrollParent[0]);return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+(t?0:this.scrollParent.scrollTop()),left:e.left-(parseInt(this.helper.css("left"),10)||0)+(t?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options,a=this.document[0];return this.relativeContainer=null,n.containment?"window"===n.containment?(this.containment=[e(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,e(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,e(window).scrollLeft()+e(window).width()-this.helperProportions.width-this.margins.left,e(window).scrollTop()+(e(window).height()||a.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):"document"===n.containment?(this.containment=[0,0,e(a).width()-this.helperProportions.width-this.margins.left,(e(a).height()||a.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):n.containment.constructor===Array?(this.containment=n.containment,void 0):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=e(n.containment),s=i[0],s&&(t=/(scroll|auto)/.test(i.css("overflow")),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(t?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(t?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=i),void 0):(this.containment=null,void 0) +},_convertPositionTo:function(e,t){t||(t=this.position);var i="absolute"===e?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:t.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:t.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}},_generatePosition:function(e,t){var i,s,n,a,o=this.options,r=this._isRootNode(this.scrollParent[0]),h=e.pageX,l=e.pageY;return r&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),t&&(this.containment&&(this.relativeContainer?(s=this.relativeContainer.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,e.pageX-this.offset.click.left<i[0]&&(h=i[0]+this.offset.click.left),e.pageY-this.offset.click.top<i[1]&&(l=i[1]+this.offset.click.top),e.pageX-this.offset.click.left>i[2]&&(h=i[2]+this.offset.click.left),e.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,l=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,h=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a),"y"===o.axis&&(h=this.originalPageX),"x"===o.axis&&(l=this.originalPageY)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:r?0:this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:r?0:this.offset.scroll.left)}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_normalizeRightBottom:function(){"y"!==this.options.axis&&"auto"!==this.helper.css("right")&&(this.helper.width(this.helper.width()),this.helper.css("right","auto")),"x"!==this.options.axis&&"auto"!==this.helper.css("bottom")&&(this.helper.height(this.helper.height()),this.helper.css("bottom","auto"))},_trigger:function(t,i,s){return s=s||this._uiHash(),e.ui.plugin.call(this,t,[i,s,this],!0),/^(drag|start|stop)/.test(t)&&(this.positionAbs=this._convertPositionTo("absolute"),s.offset=this.positionAbs),e.Widget.prototype._trigger.call(this,t,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),e.ui.plugin.add("draggable","connectToSortable",{start:function(t,i,s){var n=e.extend({},i,{item:s.element});s.sortables=[],e(s.options.connectToSortable).each(function(){var i=e(this).sortable("instance");i&&!i.options.disabled&&(s.sortables.push(i),i.refreshPositions(),i._trigger("activate",t,n))})},stop:function(t,i,s){var n=e.extend({},i,{item:s.element});s.cancelHelperRemoval=!1,e.each(s.sortables,function(){var e=this;e.isOver?(e.isOver=0,s.cancelHelperRemoval=!0,e.cancelHelperRemoval=!1,e._storedCSS={position:e.placeholder.css("position"),top:e.placeholder.css("top"),left:e.placeholder.css("left")},e._mouseStop(t),e.options.helper=e.options._helper):(e.cancelHelperRemoval=!0,e._trigger("deactivate",t,n))})},drag:function(t,i,s){e.each(s.sortables,function(){var n=!1,a=this;a.positionAbs=s.positionAbs,a.helperProportions=s.helperProportions,a.offset.click=s.offset.click,a._intersectsWith(a.containerCache)&&(n=!0,e.each(s.sortables,function(){return this.positionAbs=s.positionAbs,this.helperProportions=s.helperProportions,this.offset.click=s.offset.click,this!==a&&this._intersectsWith(this.containerCache)&&e.contains(a.element[0],this.element[0])&&(n=!1),n})),n?(a.isOver||(a.isOver=1,a.currentItem=i.helper.appendTo(a.element).data("ui-sortable-item",!0),a.options._helper=a.options.helper,a.options.helper=function(){return i.helper[0]},t.target=a.currentItem[0],a._mouseCapture(t,!0),a._mouseStart(t,!0,!0),a.offset.click.top=s.offset.click.top,a.offset.click.left=s.offset.click.left,a.offset.parent.left-=s.offset.parent.left-a.offset.parent.left,a.offset.parent.top-=s.offset.parent.top-a.offset.parent.top,s._trigger("toSortable",t),s.dropped=a.element,e.each(s.sortables,function(){this.refreshPositions()}),s.currentItem=s.element,a.fromOutside=s),a.currentItem&&(a._mouseDrag(t),i.position=a.position)):a.isOver&&(a.isOver=0,a.cancelHelperRemoval=!0,a.options._revert=a.options.revert,a.options.revert=!1,a._trigger("out",t,a._uiHash(a)),a._mouseStop(t,!0),a.options.revert=a.options._revert,a.options.helper=a.options._helper,a.placeholder&&a.placeholder.remove(),s._refreshOffsets(t),i.position=s._generatePosition(t,!0),s._trigger("fromSortable",t),s.dropped=!1,e.each(s.sortables,function(){this.refreshPositions()}))})}}),e.ui.plugin.add("draggable","cursor",{start:function(t,i,s){var n=e("body"),a=s.options;n.css("cursor")&&(a._cursor=n.css("cursor")),n.css("cursor",a.cursor)},stop:function(t,i,s){var n=s.options;n._cursor&&e("body").css("cursor",n._cursor)}}),e.ui.plugin.add("draggable","opacity",{start:function(t,i,s){var n=e(i.helper),a=s.options;n.css("opacity")&&(a._opacity=n.css("opacity")),n.css("opacity",a.opacity)},stop:function(t,i,s){var n=s.options;n._opacity&&e(i.helper).css("opacity",n._opacity)}}),e.ui.plugin.add("draggable","scroll",{start:function(e,t,i){i.scrollParentNotHidden||(i.scrollParentNotHidden=i.helper.scrollParent(!1)),i.scrollParentNotHidden[0]!==i.document[0]&&"HTML"!==i.scrollParentNotHidden[0].tagName&&(i.overflowOffset=i.scrollParentNotHidden.offset())},drag:function(t,i,s){var n=s.options,a=!1,o=s.scrollParentNotHidden[0],r=s.document[0];o!==r&&"HTML"!==o.tagName?(n.axis&&"x"===n.axis||(s.overflowOffset.top+o.offsetHeight-t.pageY<n.scrollSensitivity?o.scrollTop=a=o.scrollTop+n.scrollSpeed:t.pageY-s.overflowOffset.top<n.scrollSensitivity&&(o.scrollTop=a=o.scrollTop-n.scrollSpeed)),n.axis&&"y"===n.axis||(s.overflowOffset.left+o.offsetWidth-t.pageX<n.scrollSensitivity?o.scrollLeft=a=o.scrollLeft+n.scrollSpeed:t.pageX-s.overflowOffset.left<n.scrollSensitivity&&(o.scrollLeft=a=o.scrollLeft-n.scrollSpeed))):(n.axis&&"x"===n.axis||(t.pageY-e(r).scrollTop()<n.scrollSensitivity?a=e(r).scrollTop(e(r).scrollTop()-n.scrollSpeed):e(window).height()-(t.pageY-e(r).scrollTop())<n.scrollSensitivity&&(a=e(r).scrollTop(e(r).scrollTop()+n.scrollSpeed))),n.axis&&"y"===n.axis||(t.pageX-e(r).scrollLeft()<n.scrollSensitivity?a=e(r).scrollLeft(e(r).scrollLeft()-n.scrollSpeed):e(window).width()-(t.pageX-e(r).scrollLeft())<n.scrollSensitivity&&(a=e(r).scrollLeft(e(r).scrollLeft()+n.scrollSpeed)))),a!==!1&&e.ui.ddmanager&&!n.dropBehaviour&&e.ui.ddmanager.prepareOffsets(s,t)}}),e.ui.plugin.add("draggable","snap",{start:function(t,i,s){var n=s.options;s.snapElements=[],e(n.snap.constructor!==String?n.snap.items||":data(ui-draggable)":n.snap).each(function(){var t=e(this),i=t.offset();this!==s.element[0]&&s.snapElements.push({item:this,width:t.outerWidth(),height:t.outerHeight(),top:i.top,left:i.left})})},drag:function(t,i,s){var n,a,o,r,h,l,u,d,c,p,f=s.options,m=f.snapTolerance,g=i.offset.left,v=g+s.helperProportions.width,y=i.offset.top,b=y+s.helperProportions.height;for(c=s.snapElements.length-1;c>=0;c--)h=s.snapElements[c].left-s.margins.left,l=h+s.snapElements[c].width,u=s.snapElements[c].top-s.margins.top,d=u+s.snapElements[c].height,h-m>v||g>l+m||u-m>b||y>d+m||!e.contains(s.snapElements[c].item.ownerDocument,s.snapElements[c].item)?(s.snapElements[c].snapping&&s.options.snap.release&&s.options.snap.release.call(s.element,t,e.extend(s._uiHash(),{snapItem:s.snapElements[c].item})),s.snapElements[c].snapping=!1):("inner"!==f.snapMode&&(n=m>=Math.abs(u-b),a=m>=Math.abs(d-y),o=m>=Math.abs(h-v),r=m>=Math.abs(l-g),n&&(i.position.top=s._convertPositionTo("relative",{top:u-s.helperProportions.height,left:0}).top),a&&(i.position.top=s._convertPositionTo("relative",{top:d,left:0}).top),o&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h-s.helperProportions.width}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l}).left)),p=n||a||o||r,"outer"!==f.snapMode&&(n=m>=Math.abs(u-y),a=m>=Math.abs(d-b),o=m>=Math.abs(h-g),r=m>=Math.abs(l-v),n&&(i.position.top=s._convertPositionTo("relative",{top:u,left:0}).top),a&&(i.position.top=s._convertPositionTo("relative",{top:d-s.helperProportions.height,left:0}).top),o&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l-s.helperProportions.width}).left)),!s.snapElements[c].snapping&&(n||a||o||r||p)&&s.options.snap.snap&&s.options.snap.snap.call(s.element,t,e.extend(s._uiHash(),{snapItem:s.snapElements[c].item})),s.snapElements[c].snapping=n||a||o||r||p)}}),e.ui.plugin.add("draggable","stack",{start:function(t,i,s){var n,a=s.options,o=e.makeArray(e(a.stack)).sort(function(t,i){return(parseInt(e(t).css("zIndex"),10)||0)-(parseInt(e(i).css("zIndex"),10)||0)});o.length&&(n=parseInt(e(o[0]).css("zIndex"),10)||0,e(o).each(function(t){e(this).css("zIndex",n+t)}),this.css("zIndex",n+o.length))}}),e.ui.plugin.add("draggable","zIndex",{start:function(t,i,s){var n=e(i.helper),a=s.options;n.css("zIndex")&&(a._zIndex=n.css("zIndex")),n.css("zIndex",a.zIndex)},stop:function(t,i,s){var n=s.options;n._zIndex&&e(i.helper).css("zIndex",n._zIndex)}}),e.ui.draggable,e.widget("ui.resizable",e.ui.mouse,{version:"1.11.2",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(e){return parseInt(e,10)||0},_isNumber:function(e){return!isNaN(parseInt(e,10))},_hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",n=!1;return t[s]>0?!0:(t[s]=1,n=t[s]>0,t[s]=0,n)},_create:function(){var t,i,s,n,a,o=this,r=this.options;if(this.element.addClass("ui-resizable"),e.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(e("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=r.handles||(e(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),t=this.handles.split(","),this.handles={},i=0;t.length>i;i++)s=e.trim(t[i]),a="ui-resizable-"+s,n=e("<div class='ui-resizable-handle "+a+"'></div>"),n.css({zIndex:r.zIndex}),"se"===s&&n.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(n);this._renderAxis=function(t){var i,s,n,a;t=t||this.element;for(i in this.handles)this.handles[i].constructor===String&&(this.handles[i]=this.element.children(this.handles[i]).first().show()),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)&&(s=e(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),t.css(n,a),this._proportionallyResize()),e(this.handles[i]).length},this._renderAxis(this.element),this._handles=e(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:"se")}),r.autoHide&&(this._handles.hide(),e(this.element).addClass("ui-resizable-autohide").mouseenter(function(){r.disabled||(e(this).removeClass("ui-resizable-autohide"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(e(this).addClass("ui-resizable-autohide"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var t,i=function(t){e(t).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),t=this.element,this.originalElement.css({position:t.css("position"),width:t.outerWidth(),height:t.outerHeight(),top:t.css("top"),left:t.css("left")}).insertAfter(t),t.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(t){var i,s,n=!1;for(i in this.handles)s=e(this.handles[i])[0],(s===t.target||e.contains(s,t.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(t){var i,s,n,a=this.options,o=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css("left")),s=this._num(this.helper.css("top")),a.containment&&(i+=e(a.containment).scrollLeft()||0,s+=e(a.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:o.width(),height:o.height()},this.originalSize=this._helper?{width:o.outerWidth(),height:o.outerHeight()}:{width:o.width(),height:o.height()},this.sizeDiff={width:o.outerWidth()-o.width(),height:o.outerHeight()-o.height()},this.originalPosition={left:i,top:s},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio="number"==typeof a.aspectRatio?a.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=e(".ui-resizable-"+this.axis).css("cursor"),e("body").css("cursor","auto"===n?this.axis+"-resize":n),o.addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(t){var i,s,n=this.originalMousePosition,a=this.axis,o=t.pageX-n.left||0,r=t.pageY-n.top||0,h=this._change[a];return this._updatePrevProperties(),h?(i=h.apply(this,[t,o,r]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(i=this._updateRatio(i,t)),i=this._respectSize(i,t),this._updateCache(i),this._propagate("resize",t),s=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),e.isEmptyObject(s)||(this._updatePrevProperties(),this._trigger("resize",t,this.ui()),this._applyChanges()),!1):!1},_mouseStop:function(t){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,u=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],"left")?0:u.sizeDiff.height,a=s?0:u.sizeDiff.width,o={width:u.helper.width()-a,height:u.helper.height()-n},r=parseInt(u.element.css("left"),10)+(u.position.left-u.originalPosition.left)||null,h=parseInt(u.element.css("top"),10)+(u.position.top-u.originalPosition.top)||null,l.animate||this.element.css(e.extend(o,{top:h,left:r})),u.helper.height(u.size.height),u.helper.width(u.size.width),this._helper&&!l.animate&&this._proportionallyResize()),e("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var e={};return this.position.top!==this.prevPosition.top&&(e.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(e.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(e.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(e.height=this.size.height+"px"),this.helper.css(e),e},_updateVirtualBoundaries:function(e){var t,i,s,n,a,o=this.options;a={minWidth:this._isNumber(o.minWidth)?o.minWidth:0,maxWidth:this._isNumber(o.maxWidth)?o.maxWidth:1/0,minHeight:this._isNumber(o.minHeight)?o.minHeight:0,maxHeight:this._isNumber(o.maxHeight)?o.maxHeight:1/0},(this._aspectRatio||e)&&(t=a.minHeight*this.aspectRatio,s=a.minWidth/this.aspectRatio,i=a.maxHeight*this.aspectRatio,n=a.maxWidth/this.aspectRatio,t>a.minWidth&&(a.minWidth=t),s>a.minHeight&&(a.minHeight=s),a.maxWidth>i&&(a.maxWidth=i),a.maxHeight>n&&(a.maxHeight=n)),this._vBoundaries=a},_updateCache:function(e){this.offset=this.helper.offset(),this._isNumber(e.left)&&(this.position.left=e.left),this._isNumber(e.top)&&(this.position.top=e.top),this._isNumber(e.height)&&(this.size.height=e.height),this._isNumber(e.width)&&(this.size.width=e.width)},_updateRatio:function(e){var t=this.position,i=this.size,s=this.axis;return this._isNumber(e.height)?e.width=e.height*this.aspectRatio:this._isNumber(e.width)&&(e.height=e.width/this.aspectRatio),"sw"===s&&(e.left=t.left+(i.width-e.width),e.top=null),"nw"===s&&(e.top=t.top+(i.height-e.height),e.left=t.left+(i.width-e.width)),e},_respectSize:function(e){var t=this._vBoundaries,i=this.axis,s=this._isNumber(e.width)&&t.maxWidth&&t.maxWidth<e.width,n=this._isNumber(e.height)&&t.maxHeight&&t.maxHeight<e.height,a=this._isNumber(e.width)&&t.minWidth&&t.minWidth>e.width,o=this._isNumber(e.height)&&t.minHeight&&t.minHeight>e.height,r=this.originalPosition.left+this.originalSize.width,h=this.position.top+this.size.height,l=/sw|nw|w/.test(i),u=/nw|ne|n/.test(i);return a&&(e.width=t.minWidth),o&&(e.height=t.minHeight),s&&(e.width=t.maxWidth),n&&(e.height=t.maxHeight),a&&l&&(e.left=r-t.minWidth),s&&l&&(e.left=r-t.maxWidth),o&&u&&(e.top=h-t.minHeight),n&&u&&(e.top=h-t.maxHeight),e.width||e.height||e.left||!e.top?e.width||e.height||e.top||!e.left||(e.left=null):e.top=null,e},_getPaddingPlusBorderDimensions:function(e){for(var t=0,i=[],s=[e.css("borderTopWidth"),e.css("borderRightWidth"),e.css("borderBottomWidth"),e.css("borderLeftWidth")],n=[e.css("paddingTop"),e.css("paddingRight"),e.css("paddingBottom"),e.css("paddingLeft")];4>t;t++)i[t]=parseInt(s[t],10)||0,i[t]+=parseInt(n[t],10)||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var e,t=0,i=this.helper||this.element;this._proportionallyResizeElements.length>t;t++)e=this._proportionallyResizeElements[t],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(e)),e.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var t=this.element,i=this.options;this.elementOffset=t.offset(),this._helper?(this.helper=this.helper||e("<div style='overflow:hidden;'></div>"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(e,t){return{width:this.originalSize.width+t}},w:function(e,t){var i=this.originalSize,s=this.originalPosition;return{left:s.left+t,width:i.width-t}},n:function(e,t,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(e,t,i){return{height:this.originalSize.height+i}},se:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},sw:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,i,s]))},ne:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},nw:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,i,s]))}},_propagate:function(t,i){e.ui.plugin.call(this,t,[i,this.ui()]),"resize"!==t&&this._trigger(t,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),e.ui.plugin.add("resizable","animate",{stop:function(t){var i=e(this).resizable("instance"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css("left"),10)+(i.position.left-i.originalPosition.left)||null,u=parseInt(i.element.css("top"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(e.extend(h,u&&l?{top:u,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css("width"),10),height:parseInt(i.element.css("height"),10),top:parseInt(i.element.css("top"),10),left:parseInt(i.element.css("left"),10)};n&&n.length&&e(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",t)}})}}),e.ui.plugin.add("resizable","containment",{start:function(){var t,i,s,n,a,o,r,h=e(this).resizable("instance"),l=h.options,u=h.element,d=l.containment,c=d instanceof e?d.get(0):/parent/.test(d)?u.parent().get(0):d;c&&(h.containerElement=e(c),/document/.test(d)||d===document?(h.containerOffset={left:0,top:0},h.containerPosition={left:0,top:0},h.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}):(t=e(c),i=[],e(["Top","Right","Left","Bottom"]).each(function(e,s){i[e]=h._num(t.css("padding"+s))}),h.containerOffset=t.offset(),h.containerPosition=t.position(),h.containerSize={height:t.innerHeight()-i[3],width:t.innerWidth()-i[1]},s=h.containerOffset,n=h.containerSize.height,a=h.containerSize.width,o=h._hasScroll(c,"left")?c.scrollWidth:a,r=h._hasScroll(c)?c.scrollHeight:n,h.parentData={element:c,left:s.left,top:s.top,width:o,height:r}))},resize:function(t){var i,s,n,a,o=e(this).resizable("instance"),r=o.options,h=o.containerOffset,l=o.position,u=o._aspectRatio||t.shiftKey,d={top:0,left:0},c=o.containerElement,p=!0;c[0]!==document&&/static/.test(c.css("position"))&&(d=h),l.left<(o._helper?h.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-h.left:o.position.left-d.left),u&&(o.size.height=o.size.width/o.aspectRatio,p=!1),o.position.left=r.helper?h.left:0),l.top<(o._helper?h.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-h.top:o.position.top),u&&(o.size.width=o.size.height*o.aspectRatio,p=!1),o.position.top=o._helper?h.top:0),n=o.containerElement.get(0)===o.element.parent().get(0),a=/relative|absolute/.test(o.containerElement.css("position")),n&&a?(o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top):(o.offset.left=o.element.offset().left,o.offset.top=o.element.offset().top),i=Math.abs(o.sizeDiff.width+(o._helper?o.offset.left-d.left:o.offset.left-h.left)),s=Math.abs(o.sizeDiff.height+(o._helper?o.offset.top-d.top:o.offset.top-h.top)),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,u&&(o.size.height=o.size.width/o.aspectRatio,p=!1)),s+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-s,u&&(o.size.width=o.size.height*o.aspectRatio,p=!1)),p||(o.position.left=o.prevPosition.left,o.position.top=o.prevPosition.top,o.size.width=o.prevSize.width,o.size.height=o.prevSize.height)},stop:function(){var t=e(this).resizable("instance"),i=t.options,s=t.containerOffset,n=t.containerPosition,a=t.containerElement,o=e(t.helper),r=o.offset(),h=o.outerWidth()-t.sizeDiff.width,l=o.outerHeight()-t.sizeDiff.height;t._helper&&!i.animate&&/relative/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l}),t._helper&&!i.animate&&/static/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),e.ui.plugin.add("resizable","alsoResize",{start:function(){var t=e(this).resizable("instance"),i=t.options,s=function(t){e(t).each(function(){var t=e(this);t.data("ui-resizable-alsoresize",{width:parseInt(t.width(),10),height:parseInt(t.height(),10),left:parseInt(t.css("left"),10),top:parseInt(t.css("top"),10)})})};"object"!=typeof i.alsoResize||i.alsoResize.parentNode?s(i.alsoResize):i.alsoResize.length?(i.alsoResize=i.alsoResize[0],s(i.alsoResize)):e.each(i.alsoResize,function(e){s(e)})},resize:function(t,i){var s=e(this).resizable("instance"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0},h=function(t,s){e(t).each(function(){var t=e(this),n=e(this).data("ui-resizable-alsoresize"),a={},o=s&&s.length?s:t.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(o,function(e,t){var i=(n[t]||0)+(r[t]||0);i&&i>=0&&(a[t]=i||null)}),t.css(a)})};"object"!=typeof n.alsoResize||n.alsoResize.nodeType?h(n.alsoResize):e.each(n.alsoResize,function(e,t){h(e,t)})},stop:function(){e(this).removeData("resizable-alsoresize")}}),e.ui.plugin.add("resizable","ghost",{start:function(){var t=e(this).resizable("instance"),i=t.options,s=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof i.ghost?i.ghost:""),t.ghost.appendTo(t.helper)},resize:function(){var t=e(this).resizable("instance");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=e(this).resizable("instance");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),e.ui.plugin.add("resizable","grid",{resize:function(){var t,i=e(this).resizable("instance"),s=i.options,n=i.size,a=i.originalSize,o=i.originalPosition,r=i.axis,h="number"==typeof s.grid?[s.grid,s.grid]:s.grid,l=h[0]||1,u=h[1]||1,d=Math.round((n.width-a.width)/l)*l,c=Math.round((n.height-a.height)/u)*u,p=a.width+d,f=a.height+c,m=s.maxWidth&&p>s.maxWidth,g=s.maxHeight&&f>s.maxHeight,v=s.minWidth&&s.minWidth>p,y=s.minHeight&&s.minHeight>f;s.grid=h,v&&(p+=l),y&&(f+=u),m&&(p-=l),g&&(f-=u),/^(se|s|e)$/.test(r)?(i.size.width=p,i.size.height=f):/^(ne)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.top=o.top-c):/^(sw)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.left=o.left-d):((0>=f-u||0>=p-l)&&(t=i._getPaddingPlusBorderDimensions(this)),f-u>0?(i.size.height=f,i.position.top=o.top-c):(f=u-t.height,i.size.height=f,i.position.top=o.top+a.height-f),p-l>0?(i.size.width=p,i.position.left=o.left-d):(p=u-t.height,i.size.width=p,i.position.left=o.left+a.width-p))}}),e.ui.resizable,e.widget("ui.dialog",{version:"1.11.2",options:{appendTo:"body",autoOpen:!0,buttons:[],closeOnEscape:!0,closeText:"Close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(t){var i=e(this).css(t).offset().top;0>i&&e(this).css("top",t.top-i)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},sizeRelatedOptions:{buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},resizableRelatedOptions:{maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr("title"),this.options.title=this.options.title||this.originalTitle,this._createWrapper(),this.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(this.uiDialog),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&e.fn.draggable&&this._makeDraggable(),this.options.resizable&&e.fn.resizable&&this._makeResizable(),this._isOpen=!1,this._trackFocus()},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var t=this.options.appendTo;return t&&(t.jquery||t.nodeType)?e(t):this.document.find(t||"body").eq(0)},_destroy:function(){var e,t=this.originalPosition;this._destroyOverlay(),this.element.removeUniqueId().removeClass("ui-dialog-content ui-widget-content").css(this.originalCss).detach(),this.uiDialog.stop(!0,!0).remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),e=t.parent.children().eq(t.index),e.length&&e[0]!==this.element[0]?e.before(this.element):t.parent.append(this.element)},widget:function(){return this.uiDialog},disable:e.noop,enable:e.noop,close:function(t){var i,s=this;if(this._isOpen&&this._trigger("beforeClose",t)!==!1){if(this._isOpen=!1,this._focusedElement=null,this._destroyOverlay(),this._untrackInstance(),!this.opener.filter(":focusable").focus().length)try{i=this.document[0].activeElement,i&&"body"!==i.nodeName.toLowerCase()&&e(i).blur()}catch(n){}this._hide(this.uiDialog,this.options.hide,function(){s._trigger("close",t)})}},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(t,i){var s=!1,n=this.uiDialog.siblings(".ui-front:visible").map(function(){return+e(this).css("z-index")}).get(),a=Math.max.apply(null,n);return a>=+this.uiDialog.css("z-index")&&(this.uiDialog.css("z-index",a+1),s=!0),s&&!i&&this._trigger("focus",t),s},open:function(){var t=this;return this._isOpen?(this._moveToTop()&&this._focusTabbable(),void 0):(this._isOpen=!0,this.opener=e(this.document[0].activeElement),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this.overlay&&this.overlay.css("z-index",this.uiDialog.css("z-index")-1),this._show(this.uiDialog,this.options.show,function(){t._focusTabbable(),t._trigger("focus")}),this._makeFocusTarget(),this._trigger("open"),void 0)},_focusTabbable:function(){var e=this._focusedElement;e||(e=this.element.find("[autofocus]")),e.length||(e=this.element.find(":tabbable")),e.length||(e=this.uiDialogButtonPane.find(":tabbable")),e.length||(e=this.uiDialogTitlebarClose.filter(":tabbable")),e.length||(e=this.uiDialog),e.eq(0).focus()},_keepFocus:function(t){function i(){var t=this.document[0].activeElement,i=this.uiDialog[0]===t||e.contains(this.uiDialog[0],t);i||this._focusTabbable()}t.preventDefault(),i.call(this),this._delay(i)},_createWrapper:function(){this.uiDialog=e("<div>").addClass("ui-dialog ui-widget ui-widget-content ui-corner-all ui-front "+this.options.dialogClass).hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._on(this.uiDialog,{keydown:function(t){if(this.options.closeOnEscape&&!t.isDefaultPrevented()&&t.keyCode&&t.keyCode===e.ui.keyCode.ESCAPE)return t.preventDefault(),this.close(t),void 0; +if(t.keyCode===e.ui.keyCode.TAB&&!t.isDefaultPrevented()){var i=this.uiDialog.find(":tabbable"),s=i.filter(":first"),n=i.filter(":last");t.target!==n[0]&&t.target!==this.uiDialog[0]||t.shiftKey?t.target!==s[0]&&t.target!==this.uiDialog[0]||!t.shiftKey||(this._delay(function(){n.focus()}),t.preventDefault()):(this._delay(function(){s.focus()}),t.preventDefault())}},mousedown:function(e){this._moveToTop(e)&&this._focusTabbable()}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var t;this.uiDialogTitlebar=e("<div>").addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(this.uiDialog),this._on(this.uiDialogTitlebar,{mousedown:function(t){e(t.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.focus()}}),this.uiDialogTitlebarClose=e("<button type='button'></button>").button({label:this.options.closeText,icons:{primary:"ui-icon-closethick"},text:!1}).addClass("ui-dialog-titlebar-close").appendTo(this.uiDialogTitlebar),this._on(this.uiDialogTitlebarClose,{click:function(e){e.preventDefault(),this.close(e)}}),t=e("<span>").uniqueId().addClass("ui-dialog-title").prependTo(this.uiDialogTitlebar),this._title(t),this.uiDialog.attr({"aria-labelledby":t.attr("id")})},_title:function(e){this.options.title||e.html(" "),e.text(this.options.title)},_createButtonPane:function(){this.uiDialogButtonPane=e("<div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),this.uiButtonSet=e("<div>").addClass("ui-dialog-buttonset").appendTo(this.uiDialogButtonPane),this._createButtons()},_createButtons:function(){var t=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),e.isEmptyObject(i)||e.isArray(i)&&!i.length?(this.uiDialog.removeClass("ui-dialog-buttons"),void 0):(e.each(i,function(i,s){var n,a;s=e.isFunction(s)?{click:s,text:i}:s,s=e.extend({type:"button"},s),n=s.click,s.click=function(){n.apply(t.element[0],arguments)},a={icons:s.icons,text:s.showText},delete s.icons,delete s.showText,e("<button></button>",s).button(a).appendTo(t.uiButtonSet)}),this.uiDialog.addClass("ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),void 0)},_makeDraggable:function(){function t(e){return{position:e.position,offset:e.offset}}var i=this,s=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(s,n){e(this).addClass("ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",s,t(n))},drag:function(e,s){i._trigger("drag",e,t(s))},stop:function(n,a){var o=a.offset.left-i.document.scrollLeft(),r=a.offset.top-i.document.scrollTop();s.position={my:"left top",at:"left"+(o>=0?"+":"")+o+" "+"top"+(r>=0?"+":"")+r,of:i.window},e(this).removeClass("ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",n,t(a))}})},_makeResizable:function(){function t(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}var i=this,s=this.options,n=s.resizable,a=this.uiDialog.css("position"),o="string"==typeof n?n:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:s.maxWidth,maxHeight:s.maxHeight,minWidth:s.minWidth,minHeight:this._minHeight(),handles:o,start:function(s,n){e(this).addClass("ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",s,t(n))},resize:function(e,s){i._trigger("resize",e,t(s))},stop:function(n,a){var o=i.uiDialog.offset(),r=o.left-i.document.scrollLeft(),h=o.top-i.document.scrollTop();s.height=i.uiDialog.height(),s.width=i.uiDialog.width(),s.position={my:"left top",at:"left"+(r>=0?"+":"")+r+" "+"top"+(h>=0?"+":"")+h,of:i.window},e(this).removeClass("ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",n,t(a))}}).css("position",a)},_trackFocus:function(){this._on(this.widget(),{focusin:function(t){this._makeFocusTarget(),this._focusedElement=e(t.target)}})},_makeFocusTarget:function(){this._untrackInstance(),this._trackingInstances().unshift(this)},_untrackInstance:function(){var t=this._trackingInstances(),i=e.inArray(this,t);-1!==i&&t.splice(i,1)},_trackingInstances:function(){var e=this.document.data("ui-dialog-instances");return e||(e=[],this.document.data("ui-dialog-instances",e)),e},_minHeight:function(){var e=this.options;return"auto"===e.height?e.minHeight:Math.min(e.minHeight,e.height)},_position:function(){var e=this.uiDialog.is(":visible");e||this.uiDialog.show(),this.uiDialog.position(this.options.position),e||this.uiDialog.hide()},_setOptions:function(t){var i=this,s=!1,n={};e.each(t,function(e,t){i._setOption(e,t),e in i.sizeRelatedOptions&&(s=!0),e in i.resizableRelatedOptions&&(n[e]=t)}),s&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",n)},_setOption:function(e,t){var i,s,n=this.uiDialog;"dialogClass"===e&&n.removeClass(this.options.dialogClass).addClass(t),"disabled"!==e&&(this._super(e,t),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:""+t}),"draggable"===e&&(i=n.is(":data(ui-draggable)"),i&&!t&&n.draggable("destroy"),!i&&t&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(s=n.is(":data(ui-resizable)"),s&&!t&&n.resizable("destroy"),s&&"string"==typeof t&&n.resizable("option","handles",t),s||t===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var e,t,i,s=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),s.minWidth>s.width&&(s.width=s.minWidth),e=this.uiDialog.css({height:"auto",width:s.width}).outerHeight(),t=Math.max(0,s.minHeight-e),i="number"==typeof s.maxHeight?Math.max(0,s.maxHeight-e):"none","auto"===s.height?this.element.css({minHeight:t,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,s.height-e)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var t=e(this);return e("<div>").css({position:"absolute",width:t.outerWidth(),height:t.outerHeight()}).appendTo(t.parent()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(t){return e(t.target).closest(".ui-dialog").length?!0:!!e(t.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var t=!0;this._delay(function(){t=!1}),this.document.data("ui-dialog-overlays")||this._on(this.document,{focusin:function(e){t||this._allowInteraction(e)||(e.preventDefault(),this._trackingInstances()[0]._focusTabbable())}}),this.overlay=e("<div>").addClass("ui-widget-overlay ui-front").appendTo(this._appendTo()),this._on(this.overlay,{mousedown:"_keepFocus"}),this.document.data("ui-dialog-overlays",(this.document.data("ui-dialog-overlays")||0)+1)}},_destroyOverlay:function(){if(this.options.modal&&this.overlay){var e=this.document.data("ui-dialog-overlays")-1;e?this.document.data("ui-dialog-overlays",e):this.document.unbind("focusin").removeData("ui-dialog-overlays"),this.overlay.remove(),this.overlay=null}}}),e.widget("ui.droppable",{version:"1.11.2",widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var t,i=this.options,s=i.accept;this.isover=!1,this.isout=!0,this.accept=e.isFunction(s)?s:function(e){return e.is(s)},this.proportions=function(){return arguments.length?(t=arguments[0],void 0):t?t:t={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}},this._addToManager(i.scope),i.addClasses&&this.element.addClass("ui-droppable")},_addToManager:function(t){e.ui.ddmanager.droppables[t]=e.ui.ddmanager.droppables[t]||[],e.ui.ddmanager.droppables[t].push(this)},_splice:function(e){for(var t=0;e.length>t;t++)e[t]===this&&e.splice(t,1)},_destroy:function(){var t=e.ui.ddmanager.droppables[this.options.scope];this._splice(t),this.element.removeClass("ui-droppable ui-droppable-disabled")},_setOption:function(t,i){if("accept"===t)this.accept=e.isFunction(i)?i:function(e){return e.is(i)};else if("scope"===t){var s=e.ui.ddmanager.droppables[this.options.scope];this._splice(s),this._addToManager(i)}this._super(t,i)},_activate:function(t){var i=e.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),i&&this._trigger("activate",t,this.ui(i))},_deactivate:function(t){var i=e.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),i&&this._trigger("deactivate",t,this.ui(i))},_over:function(t){var i=e.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",t,this.ui(i)))},_out:function(t){var i=e.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",t,this.ui(i)))},_drop:function(t,i){var s=i||e.ui.ddmanager.current,n=!1;return s&&(s.currentItem||s.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var i=e(this).droppable("instance");return i.options.greedy&&!i.options.disabled&&i.options.scope===s.options.scope&&i.accept.call(i.element[0],s.currentItem||s.element)&&e.ui.intersect(s,e.extend(i,{offset:i.element.offset()}),i.options.tolerance,t)?(n=!0,!1):void 0}),n?!1:this.accept.call(this.element[0],s.currentItem||s.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",t,this.ui(s)),this.element):!1):!1},ui:function(e){return{draggable:e.currentItem||e.element,helper:e.helper,position:e.position,offset:e.positionAbs}}}),e.ui.intersect=function(){function e(e,t,i){return e>=t&&t+i>e}return function(t,i,s,n){if(!i.offset)return!1;var a=(t.positionAbs||t.position.absolute).left+t.margins.left,o=(t.positionAbs||t.position.absolute).top+t.margins.top,r=a+t.helperProportions.width,h=o+t.helperProportions.height,l=i.offset.left,u=i.offset.top,d=l+i.proportions().width,c=u+i.proportions().height;switch(s){case"fit":return a>=l&&d>=r&&o>=u&&c>=h;case"intersect":return a+t.helperProportions.width/2>l&&d>r-t.helperProportions.width/2&&o+t.helperProportions.height/2>u&&c>h-t.helperProportions.height/2;case"pointer":return e(n.pageY,u,i.proportions().height)&&e(n.pageX,l,i.proportions().width);case"touch":return(o>=u&&c>=o||h>=u&&c>=h||u>o&&h>c)&&(a>=l&&d>=a||r>=l&&d>=r||l>a&&r>d);default:return!1}}}(),e.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(t,i){var s,n,a=e.ui.ddmanager.droppables[t.options.scope]||[],o=i?i.type:null,r=(t.currentItem||t.element).find(":data(ui-droppable)").addBack();e:for(s=0;a.length>s;s++)if(!(a[s].options.disabled||t&&!a[s].accept.call(a[s].element[0],t.currentItem||t.element))){for(n=0;r.length>n;n++)if(r[n]===a[s].element[0]){a[s].proportions().height=0;continue e}a[s].visible="none"!==a[s].element.css("display"),a[s].visible&&("mousedown"===o&&a[s]._activate.call(a[s],i),a[s].offset=a[s].element.offset(),a[s].proportions({width:a[s].element[0].offsetWidth,height:a[s].element[0].offsetHeight}))}},drop:function(t,i){var s=!1;return e.each((e.ui.ddmanager.droppables[t.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&e.ui.intersect(t,this,this.options.tolerance,i)&&(s=this._drop.call(this,i)||s),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],t.currentItem||t.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,i)))}),s},dragStart:function(t,i){t.element.parentsUntil("body").bind("scroll.droppable",function(){t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,i)})},drag:function(t,i){t.options.refreshPositions&&e.ui.ddmanager.prepareOffsets(t,i),e.each(e.ui.ddmanager.droppables[t.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var s,n,a,o=e.ui.intersect(t,this,this.options.tolerance,i),r=!o&&this.isover?"isout":o&&!this.isover?"isover":null;r&&(this.options.greedy&&(n=this.options.scope,a=this.element.parents(":data(ui-droppable)").filter(function(){return e(this).droppable("instance").options.scope===n}),a.length&&(s=e(a[0]).droppable("instance"),s.greedyChild="isover"===r)),s&&"isover"===r&&(s.isover=!1,s.isout=!0,s._out.call(s,i)),this[r]=!0,this["isout"===r?"isover":"isout"]=!1,this["isover"===r?"_over":"_out"].call(this,i),s&&"isout"===r&&(s.isout=!1,s.isover=!0,s._over.call(s,i)))}})},dragStop:function(t,i){t.element.parentsUntil("body").unbind("scroll.droppable"),t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,i)}},e.ui.droppable;var y="ui-effects-",b=e;e.effects={effect:{}},function(e,t){function i(e,t,i){var s=d[t.type]||{};return null==e?i||!t.def?null:t.def:(e=s.floor?~~e:parseFloat(e),isNaN(e)?t.def:s.mod?(e+s.mod)%s.mod:0>e?0:e>s.max?s.max:e)}function s(i){var s=l(),n=s._rgba=[];return i=i.toLowerCase(),f(h,function(e,a){var o,r=a.re.exec(i),h=r&&a.parse(r),l=a.space||"rgba";return h?(o=s[l](h),s[u[l].cache]=o[u[l].cache],n=s._rgba=o._rgba,!1):t}),n.length?("0,0,0,0"===n.join()&&e.extend(n,a.transparent),s):a[i]}function n(e,t,i){return i=(i+1)%1,1>6*i?e+6*(t-e)*i:1>2*i?t:2>3*i?e+6*(t-e)*(2/3-i):e}var a,o="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,h=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[e[1],e[2],e[3],e[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[2.55*e[1],2.55*e[2],2.55*e[3],e[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(e){return[parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(e){return[parseInt(e[1]+e[1],16),parseInt(e[2]+e[2],16),parseInt(e[3]+e[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(e){return[e[1],e[2]/100,e[3]/100,e[4]]}}],l=e.Color=function(t,i,s,n){return new e.Color.fn.parse(t,i,s,n)},u={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},d={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},c=l.support={},p=e("<p>")[0],f=e.each;p.style.cssText="background-color:rgba(1,1,1,.5)",c.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(u,function(e,t){t.cache="_"+e,t.props.alpha={idx:3,type:"percent",def:1}}),l.fn=e.extend(l.prototype,{parse:function(n,o,r,h){if(n===t)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=e(n).css(o),o=t);var d=this,c=e.type(n),p=this._rgba=[];return o!==t&&(n=[n,o,r,h],c="array"),"string"===c?this.parse(s(n)||a._default):"array"===c?(f(u.rgba.props,function(e,t){p[t.idx]=i(n[t.idx],t)}),this):"object"===c?(n instanceof l?f(u,function(e,t){n[t.cache]&&(d[t.cache]=n[t.cache].slice())}):f(u,function(t,s){var a=s.cache;f(s.props,function(e,t){if(!d[a]&&s.to){if("alpha"===e||null==n[e])return;d[a]=s.to(d._rgba)}d[a][t.idx]=i(n[e],t,!0)}),d[a]&&0>e.inArray(null,d[a].slice(0,3))&&(d[a][3]=1,s.from&&(d._rgba=s.from(d[a])))}),this):t},is:function(e){var i=l(e),s=!0,n=this;return f(u,function(e,a){var o,r=i[a.cache];return r&&(o=n[a.cache]||a.to&&a.to(n._rgba)||[],f(a.props,function(e,i){return null!=r[i.idx]?s=r[i.idx]===o[i.idx]:t})),s}),s},_space:function(){var e=[],t=this;return f(u,function(i,s){t[s.cache]&&e.push(i)}),e.pop()},transition:function(e,t){var s=l(e),n=s._space(),a=u[n],o=0===this.alpha()?l("transparent"):this,r=o[a.cache]||a.to(o._rgba),h=r.slice();return s=s[a.cache],f(a.props,function(e,n){var a=n.idx,o=r[a],l=s[a],u=d[n.type]||{};null!==l&&(null===o?h[a]=l:(u.mod&&(l-o>u.mod/2?o+=u.mod:o-l>u.mod/2&&(o-=u.mod)),h[a]=i((l-o)*t+o,n)))}),this[n](h)},blend:function(t){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=l(t)._rgba;return l(e.map(i,function(e,t){return(1-s)*n[t]+s*e}))},toRgbaString:function(){var t="rgba(",i=e.map(this._rgba,function(e,t){return null==e?t>2?1:0:e});return 1===i[3]&&(i.pop(),t="rgb("),t+i.join()+")"},toHslaString:function(){var t="hsla(",i=e.map(this.hsla(),function(e,t){return null==e&&(e=t>2?1:0),t&&3>t&&(e=Math.round(100*e)+"%"),e});return 1===i[3]&&(i.pop(),t="hsl("),t+i.join()+")"},toHexString:function(t){var i=this._rgba.slice(),s=i.pop();return t&&i.push(~~(255*s)),"#"+e.map(i,function(e){return e=(e||0).toString(16),1===e.length?"0"+e:e}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),l.fn.parse.prototype=l.fn,u.hsla.to=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t,i,s=e[0]/255,n=e[1]/255,a=e[2]/255,o=e[3],r=Math.max(s,n,a),h=Math.min(s,n,a),l=r-h,u=r+h,d=.5*u;return t=h===r?0:s===r?60*(n-a)/l+360:n===r?60*(a-s)/l+120:60*(s-n)/l+240,i=0===l?0:.5>=d?l/u:l/(2-u),[Math.round(t)%360,i,d,null==o?1:o]},u.hsla.from=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t=e[0]/360,i=e[1],s=e[2],a=e[3],o=.5>=s?s*(1+i):s+i-s*i,r=2*s-o;return[Math.round(255*n(r,o,t+1/3)),Math.round(255*n(r,o,t)),Math.round(255*n(r,o,t-1/3)),a]},f(u,function(s,n){var a=n.props,o=n.cache,h=n.to,u=n.from;l.fn[s]=function(s){if(h&&!this[o]&&(this[o]=h(this._rgba)),s===t)return this[o].slice();var n,r=e.type(s),d="array"===r||"object"===r?s:arguments,c=this[o].slice();return f(a,function(e,t){var s=d["object"===r?e:t.idx];null==s&&(s=c[t.idx]),c[t.idx]=i(s,t)}),u?(n=l(u(c)),n[o]=c,n):l(c)},f(a,function(t,i){l.fn[t]||(l.fn[t]=function(n){var a,o=e.type(n),h="alpha"===t?this._hsla?"hsla":"rgba":s,l=this[h](),u=l[i.idx];return"undefined"===o?u:("function"===o&&(n=n.call(this,u),o=e.type(n)),null==n&&i.empty?this:("string"===o&&(a=r.exec(n),a&&(n=u+parseFloat(a[2])*("+"===a[1]?1:-1))),l[i.idx]=n,this[h](l)))})})}),l.hook=function(t){var i=t.split(" ");f(i,function(t,i){e.cssHooks[i]={set:function(t,n){var a,o,r="";if("transparent"!==n&&("string"!==e.type(n)||(a=s(n)))){if(n=l(a||n),!c.rgba&&1!==n._rgba[3]){for(o="backgroundColor"===i?t.parentNode:t;(""===r||"transparent"===r)&&o&&o.style;)try{r=e.css(o,"backgroundColor"),o=o.parentNode}catch(h){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{t.style[i]=n}catch(h){}}},e.fx.step[i]=function(t){t.colorInit||(t.start=l(t.elem,i),t.end=l(t.end),t.colorInit=!0),e.cssHooks[i].set(t.elem,t.start.transition(t.end,t.pos))}})},l.hook(o),e.cssHooks.borderColor={expand:function(e){var t={};return f(["Top","Right","Bottom","Left"],function(i,s){t["border"+s+"Color"]=e}),t}},a=e.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(b),function(){function t(t){var i,s,n=t.ownerDocument.defaultView?t.ownerDocument.defaultView.getComputedStyle(t,null):t.currentStyle,a={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(a[e.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(a[i]=n[i]);return a}function i(t,i){var s,a,o={};for(s in i)a=i[s],t[s]!==a&&(n[s]||(e.fx.step[s]||!isNaN(parseFloat(a)))&&(o[s]=a));return o}var s=["add","remove","toggle"],n={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};e.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(t,i){e.fx.step[i]=function(e){("none"!==e.end&&!e.setAttr||1===e.pos&&!e.setAttr)&&(b.style(e.elem,i,e.end),e.setAttr=!0)}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e.effects.animateClass=function(n,a,o,r){var h=e.speed(a,o,r);return this.queue(function(){var a,o=e(this),r=o.attr("class")||"",l=h.children?o.find("*").addBack():o;l=l.map(function(){var i=e(this);return{el:i,start:t(this)}}),a=function(){e.each(s,function(e,t){n[t]&&o[t+"Class"](n[t])})},a(),l=l.map(function(){return this.end=t(this.el[0]),this.diff=i(this.start,this.end),this}),o.attr("class",r),l=l.map(function(){var t=this,i=e.Deferred(),s=e.extend({},h,{queue:!1,complete:function(){i.resolve(t)}});return this.el.animate(this.diff,s),i.promise()}),e.when.apply(e,l.get()).done(function(){a(),e.each(arguments,function(){var t=this.el;e.each(this.diff,function(e){t.css(e,"")})}),h.complete.call(o[0])})})},e.fn.extend({addClass:function(t){return function(i,s,n,a){return s?e.effects.animateClass.call(this,{add:i},s,n,a):t.apply(this,arguments)}}(e.fn.addClass),removeClass:function(t){return function(i,s,n,a){return arguments.length>1?e.effects.animateClass.call(this,{remove:i},s,n,a):t.apply(this,arguments)}}(e.fn.removeClass),toggleClass:function(t){return function(i,s,n,a,o){return"boolean"==typeof s||void 0===s?n?e.effects.animateClass.call(this,s?{add:i}:{remove:i},n,a,o):t.apply(this,arguments):e.effects.animateClass.call(this,{toggle:i},s,n,a)}}(e.fn.toggleClass),switchClass:function(t,i,s,n,a){return e.effects.animateClass.call(this,{add:i,remove:t},s,n,a)}})}(),function(){function t(t,i,s,n){return e.isPlainObject(t)&&(i=t,t=t.effect),t={effect:t},null==i&&(i={}),e.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||e.fx.speeds[i])&&(n=s,s=i,i={}),e.isFunction(s)&&(n=s,s=null),i&&e.extend(t,i),s=s||i.duration,t.duration=e.fx.off?0:"number"==typeof s?s:s in e.fx.speeds?e.fx.speeds[s]:e.fx.speeds._default,t.complete=n||i.complete,t}function i(t){return!t||"number"==typeof t||e.fx.speeds[t]?!0:"string"!=typeof t||e.effects.effect[t]?e.isFunction(t)?!0:"object"!=typeof t||t.effect?!1:!0:!0}e.extend(e.effects,{version:"1.11.2",save:function(e,t){for(var i=0;t.length>i;i++)null!==t[i]&&e.data(y+t[i],e[0].style[t[i]])},restore:function(e,t){var i,s;for(s=0;t.length>s;s++)null!==t[s]&&(i=e.data(y+t[s]),void 0===i&&(i=""),e.css(t[s],i))},setMode:function(e,t){return"toggle"===t&&(t=e.is(":hidden")?"show":"hide"),t},getBaseline:function(e,t){var i,s;switch(e[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=e[0]/t.height}switch(e[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=e[1]/t.width}return{x:s,y:i}},createWrapper:function(t){if(t.parent().is(".ui-effects-wrapper"))return t.parent();var i={width:t.outerWidth(!0),height:t.outerHeight(!0),"float":t.css("float")},s=e("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:t.width(),height:t.height()},a=document.activeElement;try{a.id}catch(o){a=document.body}return t.wrap(s),(t[0]===a||e.contains(t[0],a))&&e(a).focus(),s=t.parent(),"static"===t.css("position")?(s.css({position:"relative"}),t.css({position:"relative"})):(e.extend(i,{position:t.css("position"),zIndex:t.css("z-index")}),e.each(["top","left","bottom","right"],function(e,s){i[s]=t.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),t.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),t.css(n),s.css(i).show()},removeWrapper:function(t){var i=document.activeElement;return t.parent().is(".ui-effects-wrapper")&&(t.parent().replaceWith(t),(t[0]===i||e.contains(t[0],i))&&e(i).focus()),t},setTransition:function(t,i,s,n){return n=n||{},e.each(i,function(e,i){var a=t.cssUnit(i);a[0]>0&&(n[i]=a[0]*s+a[1])}),n}}),e.fn.extend({effect:function(){function i(t){function i(){e.isFunction(a)&&a.call(n[0]),e.isFunction(t)&&t()}var n=e(this),a=s.complete,r=s.mode;(n.is(":hidden")?"hide"===r:"show"===r)?(n[r](),i()):o.call(n[0],s,i)}var s=t.apply(this,arguments),n=s.mode,a=s.queue,o=e.effects.effect[s.effect];return e.fx.off||!o?n?this[n](s.duration,s.complete):this.each(function(){s.complete&&s.complete.call(this)}):a===!1?this.each(i):this.queue(a||"fx",i)},show:function(e){return function(s){if(i(s))return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="show",this.effect.call(this,n)}}(e.fn.show),hide:function(e){return function(s){if(i(s))return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="hide",this.effect.call(this,n)}}(e.fn.hide),toggle:function(e){return function(s){if(i(s)||"boolean"==typeof s)return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="toggle",this.effect.call(this,n)}}(e.fn.toggle),cssUnit:function(t){var i=this.css(t),s=[];return e.each(["em","px","%","pt"],function(e,t){i.indexOf(t)>0&&(s=[parseFloat(i),t])}),s}})}(),function(){var t={};e.each(["Quad","Cubic","Quart","Quint","Expo"],function(e,i){t[i]=function(t){return Math.pow(t,e+2)}}),e.extend(t,{Sine:function(e){return 1-Math.cos(e*Math.PI/2)},Circ:function(e){return 1-Math.sqrt(1-e*e)},Elastic:function(e){return 0===e||1===e?e:-Math.pow(2,8*(e-1))*Math.sin((80*(e-1)-7.5)*Math.PI/15)},Back:function(e){return e*e*(3*e-2)},Bounce:function(e){for(var t,i=4;((t=Math.pow(2,--i))-1)/11>e;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*t-2)/22-e,2)}}),e.each(t,function(t,i){e.easing["easeIn"+t]=i,e.easing["easeOut"+t]=function(e){return 1-i(1-e)},e.easing["easeInOut"+t]=function(e){return.5>e?i(2*e)/2:1-i(-2*e+2)/2}})}(),e.effects,e.effects.effect.blind=function(t,i){var s,n,a,o=e(this),r=/up|down|vertical/,h=/up|left|vertical|horizontal/,l=["position","top","bottom","left","right","height","width"],u=e.effects.setMode(o,t.mode||"hide"),d=t.direction||"up",c=r.test(d),p=c?"height":"width",f=c?"top":"left",m=h.test(d),g={},v="show"===u;o.parent().is(".ui-effects-wrapper")?e.effects.save(o.parent(),l):e.effects.save(o,l),o.show(),s=e.effects.createWrapper(o).css({overflow:"hidden"}),n=s[p](),a=parseFloat(s.css(f))||0,g[p]=v?n:0,m||(o.css(c?"bottom":"right",0).css(c?"top":"left","auto").css({position:"absolute"}),g[f]=v?a:n+a),v&&(s.css(p,0),m||s.css(f,a+n)),s.animate(g,{duration:t.duration,easing:t.easing,queue:!1,complete:function(){"hide"===u&&o.hide(),e.effects.restore(o,l),e.effects.removeWrapper(o),i()}})},e.effects.effect.bounce=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","height","width"],h=e.effects.setMode(o,t.mode||"effect"),l="hide"===h,u="show"===h,d=t.direction||"up",c=t.distance,p=t.times||5,f=2*p+(u||l?1:0),m=t.duration/f,g=t.easing,v="up"===d||"down"===d?"top":"left",y="up"===d||"left"===d,b=o.queue(),_=b.length;for((u||l)&&r.push("opacity"),e.effects.save(o,r),o.show(),e.effects.createWrapper(o),c||(c=o["top"===v?"outerHeight":"outerWidth"]()/3),u&&(a={opacity:1},a[v]=0,o.css("opacity",0).css(v,y?2*-c:2*c).animate(a,m,g)),l&&(c/=Math.pow(2,p-1)),a={},a[v]=0,s=0;p>s;s++)n={},n[v]=(y?"-=":"+=")+c,o.animate(n,m,g).animate(a,m,g),c=l?2*c:c/2;l&&(n={opacity:0},n[v]=(y?"-=":"+=")+c,o.animate(n,m,g)),o.queue(function(){l&&o.hide(),e.effects.restore(o,r),e.effects.removeWrapper(o),i()}),_>1&&b.splice.apply(b,[1,0].concat(b.splice(_,f+1))),o.dequeue()},e.effects.effect.clip=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","height","width"],h=e.effects.setMode(o,t.mode||"hide"),l="show"===h,u=t.direction||"vertical",d="vertical"===u,c=d?"height":"width",p=d?"top":"left",f={};e.effects.save(o,r),o.show(),s=e.effects.createWrapper(o).css({overflow:"hidden"}),n="IMG"===o[0].tagName?s:o,a=n[c](),l&&(n.css(c,0),n.css(p,a/2)),f[c]=l?a:0,f[p]=l?0:a/2,n.animate(f,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){l||o.hide(),e.effects.restore(o,r),e.effects.removeWrapper(o),i()}})},e.effects.effect.drop=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","opacity","height","width"],o=e.effects.setMode(n,t.mode||"hide"),r="show"===o,h=t.direction||"left",l="up"===h||"down"===h?"top":"left",u="up"===h||"left"===h?"pos":"neg",d={opacity:r?1:0};e.effects.save(n,a),n.show(),e.effects.createWrapper(n),s=t.distance||n["top"===l?"outerHeight":"outerWidth"](!0)/2,r&&n.css("opacity",0).css(l,"pos"===u?-s:s),d[l]=(r?"pos"===u?"+=":"-=":"pos"===u?"-=":"+=")+s,n.animate(d,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}})},e.effects.effect.explode=function(t,i){function s(){b.push(this),b.length===d*c&&n()}function n(){p.css({visibility:"visible"}),e(b).remove(),m||p.hide(),i()}var a,o,r,h,l,u,d=t.pieces?Math.round(Math.sqrt(t.pieces)):3,c=d,p=e(this),f=e.effects.setMode(p,t.mode||"hide"),m="show"===f,g=p.show().css("visibility","hidden").offset(),v=Math.ceil(p.outerWidth()/c),y=Math.ceil(p.outerHeight()/d),b=[];for(a=0;d>a;a++)for(h=g.top+a*y,u=a-(d-1)/2,o=0;c>o;o++)r=g.left+o*v,l=o-(c-1)/2,p.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-o*v,top:-a*y}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:v,height:y,left:r+(m?l*v:0),top:h+(m?u*y:0),opacity:m?0:1}).animate({left:r+(m?0:l*v),top:h+(m?0:u*y),opacity:m?1:0},t.duration||500,t.easing,s)},e.effects.effect.fade=function(t,i){var s=e(this),n=e.effects.setMode(s,t.mode||"toggle");s.animate({opacity:n},{queue:!1,duration:t.duration,easing:t.easing,complete:i})},e.effects.effect.fold=function(t,i){var s,n,a=e(this),o=["position","top","bottom","left","right","height","width"],r=e.effects.setMode(a,t.mode||"hide"),h="show"===r,l="hide"===r,u=t.size||15,d=/([0-9]+)%/.exec(u),c=!!t.horizFirst,p=h!==c,f=p?["width","height"]:["height","width"],m=t.duration/2,g={},v={};e.effects.save(a,o),a.show(),s=e.effects.createWrapper(a).css({overflow:"hidden"}),n=p?[s.width(),s.height()]:[s.height(),s.width()],d&&(u=parseInt(d[1],10)/100*n[l?0:1]),h&&s.css(c?{height:0,width:u}:{height:u,width:0}),g[f[0]]=h?n[0]:u,v[f[1]]=h?n[1]:0,s.animate(g,m,t.easing).animate(v,m,t.easing,function(){l&&a.hide(),e.effects.restore(a,o),e.effects.removeWrapper(a),i()})},e.effects.effect.highlight=function(t,i){var s=e(this),n=["backgroundImage","backgroundColor","opacity"],a=e.effects.setMode(s,t.mode||"show"),o={backgroundColor:s.css("backgroundColor")};"hide"===a&&(o.opacity=0),e.effects.save(s,n),s.show().css({backgroundImage:"none",backgroundColor:t.color||"#ffff99"}).animate(o,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===a&&s.hide(),e.effects.restore(s,n),i()}})},e.effects.effect.size=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","width","height","overflow","opacity"],h=["position","top","bottom","left","right","overflow","opacity"],l=["width","height","overflow"],u=["fontSize"],d=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],c=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],p=e.effects.setMode(o,t.mode||"effect"),f=t.restore||"effect"!==p,m=t.scale||"both",g=t.origin||["middle","center"],v=o.css("position"),y=f?r:h,b={height:0,width:0,outerHeight:0,outerWidth:0};"show"===p&&o.show(),s={height:o.height(),width:o.width(),outerHeight:o.outerHeight(),outerWidth:o.outerWidth()},"toggle"===t.mode&&"show"===p?(o.from=t.to||b,o.to=t.from||s):(o.from=t.from||("show"===p?b:s),o.to=t.to||("hide"===p?b:s)),a={from:{y:o.from.height/s.height,x:o.from.width/s.width},to:{y:o.to.height/s.height,x:o.to.width/s.width}},("box"===m||"both"===m)&&(a.from.y!==a.to.y&&(y=y.concat(d),o.from=e.effects.setTransition(o,d,a.from.y,o.from),o.to=e.effects.setTransition(o,d,a.to.y,o.to)),a.from.x!==a.to.x&&(y=y.concat(c),o.from=e.effects.setTransition(o,c,a.from.x,o.from),o.to=e.effects.setTransition(o,c,a.to.x,o.to))),("content"===m||"both"===m)&&a.from.y!==a.to.y&&(y=y.concat(u).concat(l),o.from=e.effects.setTransition(o,u,a.from.y,o.from),o.to=e.effects.setTransition(o,u,a.to.y,o.to)),e.effects.save(o,y),o.show(),e.effects.createWrapper(o),o.css("overflow","hidden").css(o.from),g&&(n=e.effects.getBaseline(g,s),o.from.top=(s.outerHeight-o.outerHeight())*n.y,o.from.left=(s.outerWidth-o.outerWidth())*n.x,o.to.top=(s.outerHeight-o.to.outerHeight)*n.y,o.to.left=(s.outerWidth-o.to.outerWidth)*n.x),o.css(o.from),("content"===m||"both"===m)&&(d=d.concat(["marginTop","marginBottom"]).concat(u),c=c.concat(["marginLeft","marginRight"]),l=r.concat(d).concat(c),o.find("*[width]").each(function(){var i=e(this),s={height:i.height(),width:i.width(),outerHeight:i.outerHeight(),outerWidth:i.outerWidth()}; +f&&e.effects.save(i,l),i.from={height:s.height*a.from.y,width:s.width*a.from.x,outerHeight:s.outerHeight*a.from.y,outerWidth:s.outerWidth*a.from.x},i.to={height:s.height*a.to.y,width:s.width*a.to.x,outerHeight:s.height*a.to.y,outerWidth:s.width*a.to.x},a.from.y!==a.to.y&&(i.from=e.effects.setTransition(i,d,a.from.y,i.from),i.to=e.effects.setTransition(i,d,a.to.y,i.to)),a.from.x!==a.to.x&&(i.from=e.effects.setTransition(i,c,a.from.x,i.from),i.to=e.effects.setTransition(i,c,a.to.x,i.to)),i.css(i.from),i.animate(i.to,t.duration,t.easing,function(){f&&e.effects.restore(i,l)})})),o.animate(o.to,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){0===o.to.opacity&&o.css("opacity",o.from.opacity),"hide"===p&&o.hide(),e.effects.restore(o,y),f||("static"===v?o.css({position:"relative",top:o.to.top,left:o.to.left}):e.each(["top","left"],function(e,t){o.css(t,function(t,i){var s=parseInt(i,10),n=e?o.to.left:o.to.top;return"auto"===i?n+"px":s+n+"px"})})),e.effects.removeWrapper(o),i()}})},e.effects.effect.scale=function(t,i){var s=e(this),n=e.extend(!0,{},t),a=e.effects.setMode(s,t.mode||"effect"),o=parseInt(t.percent,10)||(0===parseInt(t.percent,10)?0:"hide"===a?0:100),r=t.direction||"both",h=t.origin,l={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()},u={y:"horizontal"!==r?o/100:1,x:"vertical"!==r?o/100:1};n.effect="size",n.queue=!1,n.complete=i,"effect"!==a&&(n.origin=h||["middle","center"],n.restore=!0),n.from=t.from||("show"===a?{height:0,width:0,outerHeight:0,outerWidth:0}:l),n.to={height:l.height*u.y,width:l.width*u.x,outerHeight:l.outerHeight*u.y,outerWidth:l.outerWidth*u.x},n.fade&&("show"===a&&(n.from.opacity=0,n.to.opacity=1),"hide"===a&&(n.from.opacity=1,n.to.opacity=0)),s.effect(n)},e.effects.effect.puff=function(t,i){var s=e(this),n=e.effects.setMode(s,t.mode||"hide"),a="hide"===n,o=parseInt(t.percent,10)||150,r=o/100,h={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()};e.extend(t,{effect:"scale",queue:!1,fade:!0,mode:n,complete:i,percent:a?o:100,from:a?h:{height:h.height*r,width:h.width*r,outerHeight:h.outerHeight*r,outerWidth:h.outerWidth*r}}),s.effect(t)},e.effects.effect.pulsate=function(t,i){var s,n=e(this),a=e.effects.setMode(n,t.mode||"show"),o="show"===a,r="hide"===a,h=o||"hide"===a,l=2*(t.times||5)+(h?1:0),u=t.duration/l,d=0,c=n.queue(),p=c.length;for((o||!n.is(":visible"))&&(n.css("opacity",0).show(),d=1),s=1;l>s;s++)n.animate({opacity:d},u,t.easing),d=1-d;n.animate({opacity:d},u,t.easing),n.queue(function(){r&&n.hide(),i()}),p>1&&c.splice.apply(c,[1,0].concat(c.splice(p,l+1))),n.dequeue()},e.effects.effect.shake=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","height","width"],o=e.effects.setMode(n,t.mode||"effect"),r=t.direction||"left",h=t.distance||20,l=t.times||3,u=2*l+1,d=Math.round(t.duration/u),c="up"===r||"down"===r?"top":"left",p="up"===r||"left"===r,f={},m={},g={},v=n.queue(),y=v.length;for(e.effects.save(n,a),n.show(),e.effects.createWrapper(n),f[c]=(p?"-=":"+=")+h,m[c]=(p?"+=":"-=")+2*h,g[c]=(p?"-=":"+=")+2*h,n.animate(f,d,t.easing),s=1;l>s;s++)n.animate(m,d,t.easing).animate(g,d,t.easing);n.animate(m,d,t.easing).animate(f,d/2,t.easing).queue(function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}),y>1&&v.splice.apply(v,[1,0].concat(v.splice(y,u+1))),n.dequeue()},e.effects.effect.slide=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","width","height"],o=e.effects.setMode(n,t.mode||"show"),r="show"===o,h=t.direction||"left",l="up"===h||"down"===h?"top":"left",u="up"===h||"left"===h,d={};e.effects.save(n,a),n.show(),s=t.distance||n["top"===l?"outerHeight":"outerWidth"](!0),e.effects.createWrapper(n).css({overflow:"hidden"}),r&&n.css(l,u?isNaN(s)?"-"+s:-s:s),d[l]=(r?u?"+=":"-=":u?"-=":"+=")+s,n.animate(d,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}})},e.effects.effect.transfer=function(t,i){var s=e(this),n=e(t.to),a="fixed"===n.css("position"),o=e("body"),r=a?o.scrollTop():0,h=a?o.scrollLeft():0,l=n.offset(),u={top:l.top-r,left:l.left-h,height:n.innerHeight(),width:n.innerWidth()},d=s.offset(),c=e("<div class='ui-effects-transfer'></div>").appendTo(document.body).addClass(t.className).css({top:d.top-r,left:d.left-h,height:s.innerHeight(),width:s.innerWidth(),position:a?"fixed":"absolute"}).animate(u,t.duration,t.easing,function(){c.remove(),i()})},e.widget("ui.progressbar",{version:"1.11.2",options:{max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min}),this.valueDiv=e("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this._refreshValue()},_destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove()},value:function(e){return void 0===e?this.options.value:(this.options.value=this._constrainedValue(e),this._refreshValue(),void 0)},_constrainedValue:function(e){return void 0===e&&(e=this.options.value),this.indeterminate=e===!1,"number"!=typeof e&&(e=0),this.indeterminate?!1:Math.min(this.options.max,Math.max(this.min,e))},_setOptions:function(e){var t=e.value;delete e.value,this._super(e),this.options.value=this._constrainedValue(t),this._refreshValue()},_setOption:function(e,t){"max"===e&&(t=Math.max(this.min,t)),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var t=this.options.value,i=this._percentage();this.valueDiv.toggle(this.indeterminate||t>this.min).toggleClass("ui-corner-right",t===this.options.max).width(i.toFixed(0)+"%"),this.element.toggleClass("ui-progressbar-indeterminate",this.indeterminate),this.indeterminate?(this.element.removeAttr("aria-valuenow"),this.overlayDiv||(this.overlayDiv=e("<div class='ui-progressbar-overlay'></div>").appendTo(this.valueDiv))):(this.element.attr({"aria-valuemax":this.options.max,"aria-valuenow":t}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==t&&(this.oldValue=t,this._trigger("change")),t===this.options.max&&this._trigger("complete")}}),e.widget("ui.selectable",e.ui.mouse,{version:"1.11.2",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var t,i=this;this.element.addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){t=e(i.options.filter,i.element[0]),t.addClass("ui-selectee"),t.each(function(){var t=e(this),i=t.offset();e.data(this,"selectable-item",{element:this,$element:t,left:i.left,top:i.top,right:i.left+t.outerWidth(),bottom:i.top+t.outerHeight(),startselected:!1,selected:t.hasClass("ui-selected"),selecting:t.hasClass("ui-selecting"),unselecting:t.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=t.addClass("ui-selectee"),this._mouseInit(),this.helper=e("<div class='ui-selectable-helper'></div>")},_destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled"),this._mouseDestroy()},_mouseStart:function(t){var i=this,s=this.options;this.opos=[t.pageX,t.pageY],this.options.disabled||(this.selectees=e(s.filter,this.element[0]),this._trigger("start",t),e(s.appendTo).append(this.helper),this.helper.css({left:t.pageX,top:t.pageY,width:0,height:0}),s.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var s=e.data(this,"selectable-item");s.startselected=!0,t.metaKey||t.ctrlKey||(s.$element.removeClass("ui-selected"),s.selected=!1,s.$element.addClass("ui-unselecting"),s.unselecting=!0,i._trigger("unselecting",t,{unselecting:s.element}))}),e(t.target).parents().addBack().each(function(){var s,n=e.data(this,"selectable-item");return n?(s=!t.metaKey&&!t.ctrlKey||!n.$element.hasClass("ui-selected"),n.$element.removeClass(s?"ui-unselecting":"ui-selected").addClass(s?"ui-selecting":"ui-unselecting"),n.unselecting=!s,n.selecting=s,n.selected=s,s?i._trigger("selecting",t,{selecting:n.element}):i._trigger("unselecting",t,{unselecting:n.element}),!1):void 0}))},_mouseDrag:function(t){if(this.dragged=!0,!this.options.disabled){var i,s=this,n=this.options,a=this.opos[0],o=this.opos[1],r=t.pageX,h=t.pageY;return a>r&&(i=r,r=a,a=i),o>h&&(i=h,h=o,o=i),this.helper.css({left:a,top:o,width:r-a,height:h-o}),this.selectees.each(function(){var i=e.data(this,"selectable-item"),l=!1;i&&i.element!==s.element[0]&&("touch"===n.tolerance?l=!(i.left>r||a>i.right||i.top>h||o>i.bottom):"fit"===n.tolerance&&(l=i.left>a&&r>i.right&&i.top>o&&h>i.bottom),l?(i.selected&&(i.$element.removeClass("ui-selected"),i.selected=!1),i.unselecting&&(i.$element.removeClass("ui-unselecting"),i.unselecting=!1),i.selecting||(i.$element.addClass("ui-selecting"),i.selecting=!0,s._trigger("selecting",t,{selecting:i.element}))):(i.selecting&&((t.metaKey||t.ctrlKey)&&i.startselected?(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.$element.addClass("ui-selected"),i.selected=!0):(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.startselected&&(i.$element.addClass("ui-unselecting"),i.unselecting=!0),s._trigger("unselecting",t,{unselecting:i.element}))),i.selected&&(t.metaKey||t.ctrlKey||i.startselected||(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,s._trigger("unselecting",t,{unselecting:i.element})))))}),!1}},_mouseStop:function(t){var i=this;return this.dragged=!1,e(".ui-unselecting",this.element[0]).each(function(){var s=e.data(this,"selectable-item");s.$element.removeClass("ui-unselecting"),s.unselecting=!1,s.startselected=!1,i._trigger("unselected",t,{unselected:s.element})}),e(".ui-selecting",this.element[0]).each(function(){var s=e.data(this,"selectable-item");s.$element.removeClass("ui-selecting").addClass("ui-selected"),s.selecting=!1,s.selected=!0,s.startselected=!0,i._trigger("selected",t,{selected:s.element})}),this._trigger("stop",t),this.helper.remove(),!1}}),e.widget("ui.selectmenu",{version:"1.11.2",defaultElement:"<select>",options:{appendTo:null,disabled:null,icons:{button:"ui-icon-triangle-1-s"},position:{my:"left top",at:"left bottom",collision:"none"},width:null,change:null,close:null,focus:null,open:null,select:null},_create:function(){var e=this.element.uniqueId().attr("id");this.ids={element:e,button:e+"-button",menu:e+"-menu"},this._drawButton(),this._drawMenu(),this.options.disabled&&this.disable()},_drawButton:function(){var t=this,i=this.element.attr("tabindex");this.label=e("label[for='"+this.ids.element+"']").attr("for",this.ids.button),this._on(this.label,{click:function(e){this.button.focus(),e.preventDefault()}}),this.element.hide(),this.button=e("<span>",{"class":"ui-selectmenu-button ui-widget ui-state-default ui-corner-all",tabindex:i||this.options.disabled?-1:0,id:this.ids.button,role:"combobox","aria-expanded":"false","aria-autocomplete":"list","aria-owns":this.ids.menu,"aria-haspopup":"true"}).insertAfter(this.element),e("<span>",{"class":"ui-icon "+this.options.icons.button}).prependTo(this.button),this.buttonText=e("<span>",{"class":"ui-selectmenu-text"}).appendTo(this.button),this._setText(this.buttonText,this.element.find("option:selected").text()),this._resizeButton(),this._on(this.button,this._buttonEvents),this.button.one("focusin",function(){t.menuItems||t._refreshMenu()}),this._hoverable(this.button),this._focusable(this.button)},_drawMenu:function(){var t=this;this.menu=e("<ul>",{"aria-hidden":"true","aria-labelledby":this.ids.button,id:this.ids.menu}),this.menuWrap=e("<div>",{"class":"ui-selectmenu-menu ui-front"}).append(this.menu).appendTo(this._appendTo()),this.menuInstance=this.menu.menu({role:"listbox",select:function(e,i){e.preventDefault(),t._setSelection(),t._select(i.item.data("ui-selectmenu-item"),e)},focus:function(e,i){var s=i.item.data("ui-selectmenu-item");null!=t.focusIndex&&s.index!==t.focusIndex&&(t._trigger("focus",e,{item:s}),t.isOpen||t._select(s,e)),t.focusIndex=s.index,t.button.attr("aria-activedescendant",t.menuItems.eq(s.index).attr("id"))}}).menu("instance"),this.menu.addClass("ui-corner-bottom").removeClass("ui-corner-all"),this.menuInstance._off(this.menu,"mouseleave"),this.menuInstance._closeOnDocumentClick=function(){return!1},this.menuInstance._isDivider=function(){return!1}},refresh:function(){this._refreshMenu(),this._setText(this.buttonText,this._getSelectedItem().text()),this.options.width||this._resizeButton()},_refreshMenu:function(){this.menu.empty();var e,t=this.element.find("option");t.length&&(this._parseOptions(t),this._renderMenu(this.menu,this.items),this.menuInstance.refresh(),this.menuItems=this.menu.find("li").not(".ui-selectmenu-optgroup"),e=this._getSelectedItem(),this.menuInstance.focus(null,e),this._setAria(e.data("ui-selectmenu-item")),this._setOption("disabled",this.element.prop("disabled")))},open:function(e){this.options.disabled||(this.menuItems?(this.menu.find(".ui-state-focus").removeClass("ui-state-focus"),this.menuInstance.focus(null,this._getSelectedItem())):this._refreshMenu(),this.isOpen=!0,this._toggleAttr(),this._resizeMenu(),this._position(),this._on(this.document,this._documentClick),this._trigger("open",e))},_position:function(){this.menuWrap.position(e.extend({of:this.button},this.options.position))},close:function(e){this.isOpen&&(this.isOpen=!1,this._toggleAttr(),this.range=null,this._off(this.document),this._trigger("close",e))},widget:function(){return this.button},menuWidget:function(){return this.menu},_renderMenu:function(t,i){var s=this,n="";e.each(i,function(i,a){a.optgroup!==n&&(e("<li>",{"class":"ui-selectmenu-optgroup ui-menu-divider"+(a.element.parent("optgroup").prop("disabled")?" ui-state-disabled":""),text:a.optgroup}).appendTo(t),n=a.optgroup),s._renderItemData(t,a)})},_renderItemData:function(e,t){return this._renderItem(e,t).data("ui-selectmenu-item",t)},_renderItem:function(t,i){var s=e("<li>");return i.disabled&&s.addClass("ui-state-disabled"),this._setText(s,i.label),s.appendTo(t)},_setText:function(e,t){t?e.text(t):e.html(" ")},_move:function(e,t){var i,s,n=".ui-menu-item";this.isOpen?i=this.menuItems.eq(this.focusIndex):(i=this.menuItems.eq(this.element[0].selectedIndex),n+=":not(.ui-state-disabled)"),s="first"===e||"last"===e?i["first"===e?"prevAll":"nextAll"](n).eq(-1):i[e+"All"](n).eq(0),s.length&&this.menuInstance.focus(t,s)},_getSelectedItem:function(){return this.menuItems.eq(this.element[0].selectedIndex)},_toggle:function(e){this[this.isOpen?"close":"open"](e)},_setSelection:function(){var e;this.range&&(window.getSelection?(e=window.getSelection(),e.removeAllRanges(),e.addRange(this.range)):this.range.select(),this.button.focus())},_documentClick:{mousedown:function(t){this.isOpen&&(e(t.target).closest(".ui-selectmenu-menu, #"+this.ids.button).length||this.close(t))}},_buttonEvents:{mousedown:function(){var e;window.getSelection?(e=window.getSelection(),e.rangeCount&&(this.range=e.getRangeAt(0))):this.range=document.selection.createRange()},click:function(e){this._setSelection(),this._toggle(e)},keydown:function(t){var i=!0;switch(t.keyCode){case e.ui.keyCode.TAB:case e.ui.keyCode.ESCAPE:this.close(t),i=!1;break;case e.ui.keyCode.ENTER:this.isOpen&&this._selectFocusedItem(t);break;case e.ui.keyCode.UP:t.altKey?this._toggle(t):this._move("prev",t);break;case e.ui.keyCode.DOWN:t.altKey?this._toggle(t):this._move("next",t);break;case e.ui.keyCode.SPACE:this.isOpen?this._selectFocusedItem(t):this._toggle(t);break;case e.ui.keyCode.LEFT:this._move("prev",t);break;case e.ui.keyCode.RIGHT:this._move("next",t);break;case e.ui.keyCode.HOME:case e.ui.keyCode.PAGE_UP:this._move("first",t);break;case e.ui.keyCode.END:case e.ui.keyCode.PAGE_DOWN:this._move("last",t);break;default:this.menu.trigger(t),i=!1}i&&t.preventDefault()}},_selectFocusedItem:function(e){var t=this.menuItems.eq(this.focusIndex);t.hasClass("ui-state-disabled")||this._select(t.data("ui-selectmenu-item"),e)},_select:function(e,t){var i=this.element[0].selectedIndex;this.element[0].selectedIndex=e.index,this._setText(this.buttonText,e.label),this._setAria(e),this._trigger("select",t,{item:e}),e.index!==i&&this._trigger("change",t,{item:e}),this.close(t)},_setAria:function(e){var t=this.menuItems.eq(e.index).attr("id");this.button.attr({"aria-labelledby":t,"aria-activedescendant":t}),this.menu.attr("aria-activedescendant",t)},_setOption:function(e,t){"icons"===e&&this.button.find("span.ui-icon").removeClass(this.options.icons.button).addClass(t.button),this._super(e,t),"appendTo"===e&&this.menuWrap.appendTo(this._appendTo()),"disabled"===e&&(this.menuInstance.option("disabled",t),this.button.toggleClass("ui-state-disabled",t).attr("aria-disabled",t),this.element.prop("disabled",t),t?(this.button.attr("tabindex",-1),this.close()):this.button.attr("tabindex",0)),"width"===e&&this._resizeButton()},_appendTo:function(){var t=this.options.appendTo;return t&&(t=t.jquery||t.nodeType?e(t):this.document.find(t).eq(0)),t&&t[0]||(t=this.element.closest(".ui-front")),t.length||(t=this.document[0].body),t},_toggleAttr:function(){this.button.toggleClass("ui-corner-top",this.isOpen).toggleClass("ui-corner-all",!this.isOpen).attr("aria-expanded",this.isOpen),this.menuWrap.toggleClass("ui-selectmenu-open",this.isOpen),this.menu.attr("aria-hidden",!this.isOpen)},_resizeButton:function(){var e=this.options.width;e||(e=this.element.show().outerWidth(),this.element.hide()),this.button.outerWidth(e)},_resizeMenu:function(){this.menu.outerWidth(Math.max(this.button.outerWidth(),this.menu.width("").outerWidth()+1))},_getCreateOptions:function(){return{disabled:this.element.prop("disabled")}},_parseOptions:function(t){var i=[];t.each(function(t,s){var n=e(s),a=n.parent("optgroup");i.push({element:n,index:t,value:n.attr("value"),label:n.text(),optgroup:a.attr("label")||"",disabled:a.prop("disabled")||n.prop("disabled")})}),this.items=i},_destroy:function(){this.menuWrap.remove(),this.button.remove(),this.element.show(),this.element.removeUniqueId(),this.label.attr("for",this.ids.element)}}),e.widget("ui.slider",e.ui.mouse,{version:"1.11.2",widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},numPages:5,_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this._calculateNewMax(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"),this._refresh(),this._setOption("disabled",this.options.disabled),this._animateOff=!1},_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var t,i,s=this.options,n=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),a="<span class='ui-slider-handle ui-state-default ui-corner-all' tabindex='0'></span>",o=[];for(i=s.values&&s.values.length||1,n.length>i&&(n.slice(i).remove(),n=n.slice(0,i)),t=n.length;i>t;t++)o.push(a);this.handles=n.add(e(o.join("")).appendTo(this.element)),this.handle=this.handles.eq(0),this.handles.each(function(t){e(this).data("ui-slider-handle-index",t)})},_createRange:function(){var t=this.options,i="";t.range?(t.range===!0&&(t.values?t.values.length&&2!==t.values.length?t.values=[t.values[0],t.values[0]]:e.isArray(t.values)&&(t.values=t.values.slice(0)):t.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?this.range.removeClass("ui-slider-range-min ui-slider-range-max").css({left:"",bottom:""}):(this.range=e("<div></div>").appendTo(this.element),i="ui-slider-range ui-widget-header ui-corner-all"),this.range.addClass(i+("min"===t.range||"max"===t.range?" ui-slider-range-"+t.range:""))):(this.range&&this.range.remove(),this.range=null)},_setupEvents:function(){this._off(this.handles),this._on(this.handles,this._handleEvents),this._hoverable(this.handles),this._focusable(this.handles)},_destroy:function(){this.handles.remove(),this.range&&this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-widget ui-widget-content ui-corner-all"),this._mouseDestroy()},_mouseCapture:function(t){var i,s,n,a,o,r,h,l,u=this,d=this.options;return d.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),i={x:t.pageX,y:t.pageY},s=this._normValueFromMouse(i),n=this._valueMax()-this._valueMin()+1,this.handles.each(function(t){var i=Math.abs(s-u.values(t));(n>i||n===i&&(t===u._lastChangedValue||u.values(t)===d.min))&&(n=i,a=e(this),o=t)}),r=this._start(t,o),r===!1?!1:(this._mouseSliding=!0,this._handleIndex=o,a.addClass("ui-state-active").focus(),h=a.offset(),l=!e(t.target).parents().addBack().is(".ui-slider-handle"),this._clickOffset=l?{left:0,top:0}:{left:t.pageX-h.left-a.width()/2,top:t.pageY-h.top-a.height()/2-(parseInt(a.css("borderTopWidth"),10)||0)-(parseInt(a.css("borderBottomWidth"),10)||0)+(parseInt(a.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(t,o,s),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(e){var t={x:e.pageX,y:e.pageY},i=this._normValueFromMouse(t);return this._slide(e,this._handleIndex,i),!1},_mouseStop:function(e){return this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(e,this._handleIndex),this._change(e,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation="vertical"===this.options.orientation?"vertical":"horizontal"},_normValueFromMouse:function(e){var t,i,s,n,a;return"horizontal"===this.orientation?(t=this.elementSize.width,i=e.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(t=this.elementSize.height,i=e.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),s=i/t,s>1&&(s=1),0>s&&(s=0),"vertical"===this.orientation&&(s=1-s),n=this._valueMax()-this._valueMin(),a=this._valueMin()+s*n,this._trimAlignValue(a)},_start:function(e,t){var i={handle:this.handles[t],value:this.value()};return this.options.values&&this.options.values.length&&(i.value=this.values(t),i.values=this.values()),this._trigger("start",e,i)},_slide:function(e,t,i){var s,n,a;this.options.values&&this.options.values.length?(s=this.values(t?0:1),2===this.options.values.length&&this.options.range===!0&&(0===t&&i>s||1===t&&s>i)&&(i=s),i!==this.values(t)&&(n=this.values(),n[t]=i,a=this._trigger("slide",e,{handle:this.handles[t],value:i,values:n}),s=this.values(t?0:1),a!==!1&&this.values(t,i))):i!==this.value()&&(a=this._trigger("slide",e,{handle:this.handles[t],value:i}),a!==!1&&this.value(i))},_stop:function(e,t){var i={handle:this.handles[t],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(t),i.values=this.values()),this._trigger("stop",e,i)},_change:function(e,t){if(!this._keySliding&&!this._mouseSliding){var i={handle:this.handles[t],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(t),i.values=this.values()),this._lastChangedValue=t,this._trigger("change",e,i)}},value:function(e){return arguments.length?(this.options.value=this._trimAlignValue(e),this._refreshValue(),this._change(null,0),void 0):this._value()},values:function(t,i){var s,n,a;if(arguments.length>1)return this.options.values[t]=this._trimAlignValue(i),this._refreshValue(),this._change(null,t),void 0;if(!arguments.length)return this._values();if(!e.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(t):this.value();for(s=this.options.values,n=arguments[0],a=0;s.length>a;a+=1)s[a]=this._trimAlignValue(n[a]),this._change(null,a);this._refreshValue()},_setOption:function(t,i){var s,n=0;switch("range"===t&&this.options.range===!0&&("min"===i?(this.options.value=this._values(0),this.options.values=null):"max"===i&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),e.isArray(this.options.values)&&(n=this.options.values.length),"disabled"===t&&this.element.toggleClass("ui-state-disabled",!!i),this._super(t,i),t){case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue(),this.handles.css("horizontal"===i?"bottom":"left","");break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":for(this._animateOff=!0,this._refreshValue(),s=0;n>s;s+=1)this._change(null,s);this._animateOff=!1;break;case"step":case"min":case"max":this._animateOff=!0,this._calculateNewMax(),this._refreshValue(),this._animateOff=!1;break;case"range":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_value:function(){var e=this.options.value;return e=this._trimAlignValue(e)},_values:function(e){var t,i,s;if(arguments.length)return t=this.options.values[e],t=this._trimAlignValue(t);if(this.options.values&&this.options.values.length){for(i=this.options.values.slice(),s=0;i.length>s;s+=1)i[s]=this._trimAlignValue(i[s]);return i}return[]},_trimAlignValue:function(e){if(this._valueMin()>=e)return this._valueMin();if(e>=this._valueMax())return this._valueMax();var t=this.options.step>0?this.options.step:1,i=(e-this._valueMin())%t,s=e-i;return 2*Math.abs(i)>=t&&(s+=i>0?t:-t),parseFloat(s.toFixed(5))},_calculateNewMax:function(){var e=(this.options.max-this._valueMin())%this.options.step;this.max=this.options.max-e},_valueMin:function(){return this.options.min},_valueMax:function(){return this.max},_refreshValue:function(){var t,i,s,n,a,o=this.options.range,r=this.options,h=this,l=this._animateOff?!1:r.animate,u={};this.options.values&&this.options.values.length?this.handles.each(function(s){i=100*((h.values(s)-h._valueMin())/(h._valueMax()-h._valueMin())),u["horizontal"===h.orientation?"left":"bottom"]=i+"%",e(this).stop(1,1)[l?"animate":"css"](u,r.animate),h.options.range===!0&&("horizontal"===h.orientation?(0===s&&h.range.stop(1,1)[l?"animate":"css"]({left:i+"%"},r.animate),1===s&&h.range[l?"animate":"css"]({width:i-t+"%"},{queue:!1,duration:r.animate})):(0===s&&h.range.stop(1,1)[l?"animate":"css"]({bottom:i+"%"},r.animate),1===s&&h.range[l?"animate":"css"]({height:i-t+"%"},{queue:!1,duration:r.animate}))),t=i}):(s=this.value(),n=this._valueMin(),a=this._valueMax(),i=a!==n?100*((s-n)/(a-n)):0,u["horizontal"===this.orientation?"left":"bottom"]=i+"%",this.handle.stop(1,1)[l?"animate":"css"](u,r.animate),"min"===o&&"horizontal"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({width:i+"%"},r.animate),"max"===o&&"horizontal"===this.orientation&&this.range[l?"animate":"css"]({width:100-i+"%"},{queue:!1,duration:r.animate}),"min"===o&&"vertical"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({height:i+"%"},r.animate),"max"===o&&"vertical"===this.orientation&&this.range[l?"animate":"css"]({height:100-i+"%"},{queue:!1,duration:r.animate}))},_handleEvents:{keydown:function(t){var i,s,n,a,o=e(t.target).data("ui-slider-handle-index");switch(t.keyCode){case e.ui.keyCode.HOME:case e.ui.keyCode.END:case e.ui.keyCode.PAGE_UP:case e.ui.keyCode.PAGE_DOWN:case e.ui.keyCode.UP:case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:case e.ui.keyCode.LEFT:if(t.preventDefault(),!this._keySliding&&(this._keySliding=!0,e(t.target).addClass("ui-state-active"),i=this._start(t,o),i===!1))return}switch(a=this.options.step,s=n=this.options.values&&this.options.values.length?this.values(o):this.value(),t.keyCode){case e.ui.keyCode.HOME:n=this._valueMin();break;case e.ui.keyCode.END:n=this._valueMax();break;case e.ui.keyCode.PAGE_UP:n=this._trimAlignValue(s+(this._valueMax()-this._valueMin())/this.numPages);break;case e.ui.keyCode.PAGE_DOWN:n=this._trimAlignValue(s-(this._valueMax()-this._valueMin())/this.numPages);break;case e.ui.keyCode.UP:case e.ui.keyCode.RIGHT:if(s===this._valueMax())return;n=this._trimAlignValue(s+a);break;case e.ui.keyCode.DOWN:case e.ui.keyCode.LEFT:if(s===this._valueMin())return;n=this._trimAlignValue(s-a)}this._slide(t,o,n)},keyup:function(t){var i=e(t.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(t,i),this._change(t,i),e(t.target).removeClass("ui-state-active"))}}}),e.widget("ui.sortable",e.ui.mouse,{version:"1.11.2",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(e,t,i){return e>=t&&t+i>e},_isFloating:function(e){return/left|right/.test(e.css("float"))||/inline|table-cell/.test(e.css("display"))},_create:function(){var e=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?"x"===e.axis||this._isFloating(this.items[0].item):!1,this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(e,t){this._super(e,t),"handle"===e&&this._setHandleClassName()},_setHandleClassName:function(){this.element.find(".ui-sortable-handle").removeClass("ui-sortable-handle"),e.each(this.items,function(){(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item).addClass("ui-sortable-handle")})},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").find(".ui-sortable-handle").removeClass("ui-sortable-handle"),this._mouseDestroy();for(var e=this.items.length-1;e>=0;e--)this.items[e].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(t,i){var s=null,n=!1,a=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(t),e(t.target).parents().each(function(){return e.data(this,a.widgetName+"-item")===a?(s=e(this),!1):void 0}),e.data(t.target,a.widgetName+"-item")===a&&(s=e(t.target)),s?!this.options.handle||i||(e(this.options.handle,s).find("*").addBack().each(function(){this===t.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(t,i,s){var n,a,o=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(t),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,o.cursorAt&&this._adjustOffsetFromHelper(o.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),o.containment&&this._setContainment(),o.cursor&&"auto"!==o.cursor&&(a=this.document.find("body"),this.storedCursor=a.css("cursor"),a.css("cursor",o.cursor),this.storedStylesheet=e("<style>*{ cursor: "+o.cursor+" !important; }</style>").appendTo(a)),o.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",o.opacity)),o.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",o.zIndex)),this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",t,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",t,this._uiHash(this)); +return e.ui.ddmanager&&(e.ui.ddmanager.current=this),e.ui.ddmanager&&!o.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(t),!0},_mouseDrag:function(t){var i,s,n,a,o=this.options,r=!1;for(this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-t.pageY<o.scrollSensitivity?this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop+o.scrollSpeed:t.pageY-this.overflowOffset.top<o.scrollSensitivity&&(this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop-o.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-t.pageX<o.scrollSensitivity?this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft+o.scrollSpeed:t.pageX-this.overflowOffset.left<o.scrollSensitivity&&(this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft-o.scrollSpeed)):(t.pageY-e(document).scrollTop()<o.scrollSensitivity?r=e(document).scrollTop(e(document).scrollTop()-o.scrollSpeed):e(window).height()-(t.pageY-e(document).scrollTop())<o.scrollSensitivity&&(r=e(document).scrollTop(e(document).scrollTop()+o.scrollSpeed)),t.pageX-e(document).scrollLeft()<o.scrollSensitivity?r=e(document).scrollLeft(e(document).scrollLeft()-o.scrollSpeed):e(window).width()-(t.pageX-e(document).scrollLeft())<o.scrollSensitivity&&(r=e(document).scrollLeft(e(document).scrollLeft()+o.scrollSpeed))),r!==!1&&e.ui.ddmanager&&!o.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t)),this.positionAbs=this._convertPositionTo("absolute"),this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),i=this.items.length-1;i>=0;i--)if(s=this.items[i],n=s.item[0],a=this._intersectsWithPointer(s),a&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===a?"next":"prev"]()[0]!==n&&!e.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!e.contains(this.element[0],n):!0)){if(this.direction=1===a?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(t,s),this._trigger("change",t,this._uiHash());break}return this._contactContainers(t),e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),this._trigger("sort",t,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(t,i){if(t){if(e.ui.ddmanager&&!this.options.dropBehaviour&&e.ui.ddmanager.drop(this,t),this.options.revert){var s=this,n=this.placeholder.offset(),a=this.options.axis,o={};a&&"x"!==a||(o.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollLeft)),a&&"y"!==a||(o.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,e(this.helper).animate(o,parseInt(this.options.revert,10)||500,function(){s._clear(t)})}else this._clear(t,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),"original"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var t=this.containers.length-1;t>=0;t--)this.containers[t]._trigger("deactivate",null,this._uiHash(this)),this.containers[t].containerCache.over&&(this.containers[t]._trigger("out",null,this._uiHash(this)),this.containers[t].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),e.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?e(this.domPosition.prev).after(this.currentItem):e(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(t){var i=this._getItemsAsjQuery(t&&t.connected),s=[];return t=t||{},e(i).each(function(){var i=(e(t.item||this).attr(t.attribute||"id")||"").match(t.expression||/(.+)[\-=_](.+)/);i&&s.push((t.key||i[1]+"[]")+"="+(t.key&&t.expression?i[1]:i[2]))}),!s.length&&t.key&&s.push(t.key+"="),s.join("&")},toArray:function(t){var i=this._getItemsAsjQuery(t&&t.connected),s=[];return t=t||{},i.each(function(){s.push(e(t.item||this).attr(t.attribute||"id")||"")}),s},_intersectsWith:function(e){var t=this.positionAbs.left,i=t+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,a=e.left,o=a+e.width,r=e.top,h=r+e.height,l=this.offset.click.top,u=this.offset.click.left,d="x"===this.options.axis||s+l>r&&h>s+l,c="y"===this.options.axis||t+u>a&&o>t+u,p=d&&c;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>e[this.floating?"width":"height"]?p:t+this.helperProportions.width/2>a&&o>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(e){var t="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,e.top,e.height),i="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,e.left,e.width),s=t&&i,n=this._getDragVerticalDirection(),a=this._getDragHorizontalDirection();return s?this.floating?a&&"right"===a||"down"===n?2:1:n&&("down"===n?2:1):!1},_intersectsWithSides:function(e){var t=this._isOverAxis(this.positionAbs.top+this.offset.click.top,e.top+e.height/2,e.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,e.left+e.width/2,e.width),s=this._getDragVerticalDirection(),n=this._getDragHorizontalDirection();return this.floating&&n?"right"===n&&i||"left"===n&&!i:s&&("down"===s&&t||"up"===s&&!t)},_getDragVerticalDirection:function(){var e=this.positionAbs.top-this.lastPositionAbs.top;return 0!==e&&(e>0?"down":"up")},_getDragHorizontalDirection:function(){var e=this.positionAbs.left-this.lastPositionAbs.left;return 0!==e&&(e>0?"right":"left")},refresh:function(e){return this._refreshItems(e),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var e=this.options;return e.connectWith.constructor===String?[e.connectWith]:e.connectWith},_getItemsAsjQuery:function(t){function i(){r.push(this)}var s,n,a,o,r=[],h=[],l=this._connectWith();if(l&&t)for(s=l.length-1;s>=0;s--)for(a=e(l[s]),n=a.length-1;n>=0;n--)o=e.data(a[n],this.widgetFullName),o&&o!==this&&!o.options.disabled&&h.push([e.isFunction(o.options.items)?o.options.items.call(o.element):e(o.options.items,o.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),o]);for(h.push([e.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):e(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=h.length-1;s>=0;s--)h[s][0].each(i);return e(r)},_removeCurrentsFromItems:function(){var t=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=e.grep(this.items,function(e){for(var i=0;t.length>i;i++)if(t[i]===e.item[0])return!1;return!0})},_refreshItems:function(t){this.items=[],this.containers=[this];var i,s,n,a,o,r,h,l,u=this.items,d=[[e.isFunction(this.options.items)?this.options.items.call(this.element[0],t,{item:this.currentItem}):e(this.options.items,this.element),this]],c=this._connectWith();if(c&&this.ready)for(i=c.length-1;i>=0;i--)for(n=e(c[i]),s=n.length-1;s>=0;s--)a=e.data(n[s],this.widgetFullName),a&&a!==this&&!a.options.disabled&&(d.push([e.isFunction(a.options.items)?a.options.items.call(a.element[0],t,{item:this.currentItem}):e(a.options.items,a.element),a]),this.containers.push(a));for(i=d.length-1;i>=0;i--)for(o=d[i][1],r=d[i][0],s=0,l=r.length;l>s;s++)h=e(r[s]),h.data(this.widgetName+"-item",o),u.push({item:h,instance:o,width:0,height:0,left:0,top:0})},refreshPositions:function(t){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,a;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?e(this.options.toleranceElement,s.item):s.item,t||(s.width=n.outerWidth(),s.height=n.outerHeight()),a=n.offset(),s.left=a.left,s.top=a.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)a=this.containers[i].element.offset(),this.containers[i].containerCache.left=a.left,this.containers[i].containerCache.top=a.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(t){t=t||this;var i,s=t.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=t.currentItem[0].nodeName.toLowerCase(),n=e("<"+s+">",t.document[0]).addClass(i||t.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper");return"tr"===s?t.currentItem.children().each(function(){e("<td> </td>",t.document[0]).attr("colspan",e(this).attr("colspan")||1).appendTo(n)}):"img"===s&&n.attr("src",t.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(e,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(t.currentItem.innerHeight()-parseInt(t.currentItem.css("paddingTop")||0,10)-parseInt(t.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(t.currentItem.innerWidth()-parseInt(t.currentItem.css("paddingLeft")||0,10)-parseInt(t.currentItem.css("paddingRight")||0,10)))}}),t.placeholder=e(s.placeholder.element.call(t.element,t.currentItem)),t.currentItem.after(t.placeholder),s.placeholder.update(t,t.placeholder)},_contactContainers:function(t){var i,s,n,a,o,r,h,l,u,d,c=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!e.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(c&&e.contains(this.containers[i].element[0],c.element[0]))continue;c=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",t,this._uiHash(this)),this.containers[i].containerCache.over=0);if(c)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",t,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,a=null,u=c.floating||this._isFloating(this.currentItem),o=u?"left":"top",r=u?"width":"height",d=u?"clientX":"clientY",s=this.items.length-1;s>=0;s--)e.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(h=this.items[s].item.offset()[o],l=!1,t[d]-h>this.items[s][r]/2&&(l=!0),n>Math.abs(t[d]-h)&&(n=Math.abs(t[d]-h),a=this.items[s],this.direction=l?"up":"down"));if(!a&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger("over",t,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;a?this._rearrange(t,a,null,!0):this._rearrange(t,null,this.containers[p].element,!0),this._trigger("change",t,this._uiHash()),this.containers[p]._trigger("change",t,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",t,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper)?e(i.helper.apply(this.element[0],[t,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||e("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var t=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&e.ui.ie)&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var e=this.currentItem.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,e("document"===n.containment?document:window).width()-this.helperProportions.width-this.margins.left,(e("document"===n.containment?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(t=e(n.containment)[0],i=e(n.containment).offset(),s="hidden"!==e(t).css("overflow"),this.containment=[i.left+(parseInt(e(t).css("borderLeftWidth"),10)||0)+(parseInt(e(t).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(e(t).css("borderTopWidth"),10)||0)+(parseInt(e(t).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(t.scrollWidth,t.offsetWidth):t.offsetWidth)-(parseInt(e(t).css("borderLeftWidth"),10)||0)-(parseInt(e(t).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(t.scrollHeight,t.offsetHeight):t.offsetHeight)-(parseInt(e(t).css("borderTopWidth"),10)||0)-(parseInt(e(t).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(t,i){i||(i=this.position);var s="absolute"===t?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,a=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():a?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():a?0:n.scrollLeft())*s}},_generatePosition:function(t){var i,s,n=this.options,a=t.pageX,o=t.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==document&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(t.pageX-this.offset.click.left<this.containment[0]&&(a=this.containment[0]+this.offset.click.left),t.pageY-this.offset.click.top<this.containment[1]&&(o=this.containment[1]+this.offset.click.top),t.pageX-this.offset.click.left>this.containment[2]&&(a=this.containment[2]+this.offset.click.left),t.pageY-this.offset.click.top>this.containment[3]&&(o=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((o-this.originalPageY)/n.grid[1])*n.grid[1],o=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((a-this.originalPageX)/n.grid[0])*n.grid[0],a=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:o-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:a-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(e,t,i,s){i?i[0].appendChild(this.placeholder[0]):t.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?t.item[0]:t.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(e,t){function i(e,t,i){return function(s){i._trigger(e,s,t._uiHash(t))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!t&&n.push(function(e){this._trigger("receive",e,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||t||n.push(function(e){this._trigger("update",e,this._uiHash())}),this!==this.currentContainer&&(t||(n.push(function(e){this._trigger("remove",e,this._uiHash())}),n.push(function(e){return function(t){e._trigger("receive",t,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(e){return function(t){e._trigger("update",t,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)t||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,t||this._trigger("beforeStop",e,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!t){for(s=0;n.length>s;s++)n[s].call(this,e);this._trigger("stop",e,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){e.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(t){var i=t||this;return{helper:i.helper,placeholder:i.placeholder||e([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:t?t.element:null}}}),e.widget("ui.spinner",{version:"1.11.2",defaultElement:"<input>",widgetEventPrefix:"spin",options:{culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),""!==this.value()&&this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var t={},i=this.element;return e.each(["min","max","step"],function(e,s){var n=i.attr(s);void 0!==n&&n.length&&(t[s]=n)}),t},_events:{keydown:function(e){this._start(e)&&this._keydown(e)&&e.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,void 0):(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",e),void 0)},mousewheel:function(e,t){if(t){if(!this.spinning&&!this._start(e))return!1;this._spin((t>0?1:-1)*this.options.step,e),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(e)},100),e.preventDefault()}},"mousedown .ui-spinner-button":function(t){function i(){var e=this.element[0]===this.document[0].activeElement;e||(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s}))}var s;s=this.element[0]===this.document[0].activeElement?this.previous:this.element.val(),t.preventDefault(),i.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,i.call(this)}),this._start(t)!==!1&&this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(t){return e(t.currentTarget).hasClass("ui-state-active")?this._start(t)===!1?!1:(this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t),void 0):void 0},"mouseleave .ui-spinner-button":"_stop"},_draw:function(){var e=this.uiSpinner=this.element.addClass("ui-spinner-input").attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml());this.element.attr("role","spinbutton"),this.buttons=e.find(".ui-spinner-button").attr("tabIndex",-1).button().removeClass("ui-corner-all"),this.buttons.height()>Math.ceil(.5*e.height())&&e.height()>0&&e.height(e.height()),this.options.disabled&&this.disable()},_keydown:function(t){var i=this.options,s=e.ui.keyCode;switch(t.keyCode){case s.UP:return this._repeat(null,1,t),!0;case s.DOWN:return this._repeat(null,-1,t),!0;case s.PAGE_UP:return this._repeat(null,i.page,t),!0;case s.PAGE_DOWN:return this._repeat(null,-i.page,t),!0}return!1},_uiSpinnerHtml:function(){return"<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>"},_buttonHtml:function(){return"<a class='ui-spinner-button ui-spinner-up ui-corner-tr'><span class='ui-icon "+this.options.icons.up+"'>▲</span>"+"</a>"+"<a class='ui-spinner-button ui-spinner-down ui-corner-br'>"+"<span class='ui-icon "+this.options.icons.down+"'>▼</span>"+"</a>"},_start:function(e){return this.spinning||this._trigger("start",e)!==!1?(this.counter||(this.counter=1),this.spinning=!0,!0):!1},_repeat:function(e,t,i){e=e||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,t,i)},e),this._spin(t*this.options.step,i)},_spin:function(e,t){var i=this.value()||0;this.counter||(this.counter=1),i=this._adjustValue(i+e*this._increment(this.counter)),this.spinning&&this._trigger("spin",t,{value:i})===!1||(this._value(i),this.counter++)},_increment:function(t){var i=this.options.incremental;return i?e.isFunction(i)?i(t):Math.floor(t*t*t/5e4-t*t/500+17*t/200+1):1},_precision:function(){var e=this._precisionOf(this.options.step);return null!==this.options.min&&(e=Math.max(e,this._precisionOf(this.options.min))),e},_precisionOf:function(e){var t=""+e,i=t.indexOf(".");return-1===i?0:t.length-i-1},_adjustValue:function(e){var t,i,s=this.options;return t=null!==s.min?s.min:0,i=e-t,i=Math.round(i/s.step)*s.step,e=t+i,e=parseFloat(e.toFixed(this._precision())),null!==s.max&&e>s.max?s.max:null!==s.min&&s.min>e?s.min:e},_stop:function(e){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",e))},_setOption:function(e,t){if("culture"===e||"numberFormat"===e){var i=this._parse(this.element.val());return this.options[e]=t,this.element.val(this._format(i)),void 0}("max"===e||"min"===e||"step"===e)&&"string"==typeof t&&(t=this._parse(t)),"icons"===e&&(this.buttons.first().find(".ui-icon").removeClass(this.options.icons.up).addClass(t.up),this.buttons.last().find(".ui-icon").removeClass(this.options.icons.down).addClass(t.down)),this._super(e,t),"disabled"===e&&(this.widget().toggleClass("ui-state-disabled",!!t),this.element.prop("disabled",!!t),this.buttons.button(t?"disable":"enable"))},_setOptions:h(function(e){this._super(e)}),_parse:function(e){return"string"==typeof e&&""!==e&&(e=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(e,10,this.options.culture):+e),""===e||isNaN(e)?null:e},_format:function(e){return""===e?"":window.Globalize&&this.options.numberFormat?Globalize.format(e,this.options.numberFormat,this.options.culture):e},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},isValid:function(){var e=this.value();return null===e?!1:e===this._adjustValue(e)},_value:function(e,t){var i;""!==e&&(i=this._parse(e),null!==i&&(t||(i=this._adjustValue(i)),e=this._format(i))),this.element.val(e),this._refresh()},_destroy:function(){this.element.removeClass("ui-spinner-input").prop("disabled",!1).removeAttr("autocomplete").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:h(function(e){this._stepUp(e)}),_stepUp:function(e){this._start()&&(this._spin((e||1)*this.options.step),this._stop())},stepDown:h(function(e){this._stepDown(e)}),_stepDown:function(e){this._start()&&(this._spin((e||1)*-this.options.step),this._stop())},pageUp:h(function(e){this._stepUp((e||1)*this.options.page)}),pageDown:h(function(e){this._stepDown((e||1)*this.options.page)}),value:function(e){return arguments.length?(h(this._value).call(this,e),void 0):this._parse(this.element.val())},widget:function(){return this.uiSpinner}}),e.widget("ui.tabs",{version:"1.11.2",delay:300,options:{active:null,collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:function(){var e=/#.*$/;return function(t){var i,s;t=t.cloneNode(!1),i=t.href.replace(e,""),s=location.href.replace(e,"");try{i=decodeURIComponent(i)}catch(n){}try{s=decodeURIComponent(s)}catch(n){}return t.hash.length>1&&i===s}}(),_create:function(){var t=this,i=this.options;this.running=!1,this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all").toggleClass("ui-tabs-collapsible",i.collapsible),this._processTabs(),i.active=this._initialActive(),e.isArray(i.disabled)&&(i.disabled=e.unique(i.disabled.concat(e.map(this.tabs.filter(".ui-state-disabled"),function(e){return t.tabs.index(e)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):e(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var t=this.options.active,i=this.options.collapsible,s=location.hash.substring(1);return null===t&&(s&&this.tabs.each(function(i,n){return e(n).attr("aria-controls")===s?(t=i,!1):void 0}),null===t&&(t=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===t||-1===t)&&(t=this.tabs.length?0:!1)),t!==!1&&(t=this.tabs.index(this.tabs.eq(t)),-1===t&&(t=i?!1:0)),!i&&t===!1&&this.anchors.length&&(t=0),t},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):e()}},_tabKeydown:function(t){var i=e(this.document[0].activeElement).closest("li"),s=this.tabs.index(i),n=!0;if(!this._handlePageNav(t)){switch(t.keyCode){case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:s++;break;case e.ui.keyCode.UP:case e.ui.keyCode.LEFT:n=!1,s--;break;case e.ui.keyCode.END:s=this.anchors.length-1;break;case e.ui.keyCode.HOME:s=0;break;case e.ui.keyCode.SPACE:return t.preventDefault(),clearTimeout(this.activating),this._activate(s),void 0;case e.ui.keyCode.ENTER:return t.preventDefault(),clearTimeout(this.activating),this._activate(s===this.options.active?!1:s),void 0;default:return}t.preventDefault(),clearTimeout(this.activating),s=this._focusNextTab(s,n),t.ctrlKey||(i.attr("aria-selected","false"),this.tabs.eq(s).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",s)},this.delay))}},_panelKeydown:function(t){this._handlePageNav(t)||t.ctrlKey&&t.keyCode===e.ui.keyCode.UP&&(t.preventDefault(),this.active.focus())},_handlePageNav:function(t){return t.altKey&&t.keyCode===e.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):t.altKey&&t.keyCode===e.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(t,i){function s(){return t>n&&(t=0),0>t&&(t=n),t}for(var n=this.tabs.length-1;-1!==e.inArray(s(),this.options.disabled);)t=i?t+1:t-1;return t},_focusNextTab:function(e,t){return e=this._findNextTab(e,t),this.tabs.eq(e).focus(),e},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):"disabled"===e?(this._setupDisabled(t),void 0):(this._super(e,t),"collapsible"===e&&(this.element.toggleClass("ui-tabs-collapsible",t),t||this.options.active!==!1||this._activate(0)),"event"===e&&this._setupEvents(t),"heightStyle"===e&&this._setupHeightStyle(t),void 0)},_sanitizeSelector:function(e){return e?e.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var t=this.options,i=this.tablist.children(":has(a[href])");t.disabled=e.map(i.filter(".ui-state-disabled"),function(e){return i.index(e)}),this._processTabs(),t.active!==!1&&this.anchors.length?this.active.length&&!e.contains(this.tablist[0],this.active[0])?this.tabs.length===t.disabled.length?(t.active=!1,this.active=e()):this._activate(this._findNextTab(Math.max(0,t.active-1),!1)):t.active=this.tabs.index(this.active):(t.active=!1,this.active=e()),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-hidden":"true"}),this.active.length?(this.active.addClass("ui-tabs-active ui-state-active").attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}),this._getPanelForTab(this.active).show().attr({"aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var t=this,i=this.tabs,s=this.anchors,n=this.panels;this.tablist=this._getList().addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").attr("role","tablist").delegate("> li","mousedown"+this.eventNamespace,function(t){e(this).is(".ui-state-disabled")&&t.preventDefault()}).delegate(".ui-tabs-anchor","focus"+this.eventNamespace,function(){e(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this.tabs=this.tablist.find("> li:has(a[href])").addClass("ui-state-default ui-corner-top").attr({role:"tab",tabIndex:-1}),this.anchors=this.tabs.map(function(){return e("a",this)[0] +}).addClass("ui-tabs-anchor").attr({role:"presentation",tabIndex:-1}),this.panels=e(),this.anchors.each(function(i,s){var n,a,o,r=e(s).uniqueId().attr("id"),h=e(s).closest("li"),l=h.attr("aria-controls");t._isLocal(s)?(n=s.hash,o=n.substring(1),a=t.element.find(t._sanitizeSelector(n))):(o=h.attr("aria-controls")||e({}).uniqueId()[0].id,n="#"+o,a=t.element.find(n),a.length||(a=t._createPanel(o),a.insertAfter(t.panels[i-1]||t.tablist)),a.attr("aria-live","polite")),a.length&&(t.panels=t.panels.add(a)),l&&h.data("ui-tabs-aria-controls",l),h.attr({"aria-controls":o,"aria-labelledby":r}),a.attr("aria-labelledby",r)}),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").attr("role","tabpanel"),i&&(this._off(i.not(this.tabs)),this._off(s.not(this.anchors)),this._off(n.not(this.panels)))},_getList:function(){return this.tablist||this.element.find("ol,ul").eq(0)},_createPanel:function(t){return e("<div>").attr("id",t).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)},_setupDisabled:function(t){e.isArray(t)&&(t.length?t.length===this.anchors.length&&(t=!0):t=!1);for(var i,s=0;i=this.tabs[s];s++)t===!0||-1!==e.inArray(s,t)?e(i).addClass("ui-state-disabled").attr("aria-disabled","true"):e(i).removeClass("ui-state-disabled").removeAttr("aria-disabled");this.options.disabled=t},_setupEvents:function(t){var i={};t&&e.each(t.split(" "),function(e,t){i[t]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(e){e.preventDefault()}}),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(t){var i,s=this.element.parent();"fill"===t?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var t=e(this),s=t.css("position");"absolute"!==s&&"fixed"!==s&&(i-=t.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=e(this).outerHeight(!0)}),this.panels.each(function(){e(this).height(Math.max(0,i-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===t&&(i=0,this.panels.each(function(){i=Math.max(i,e(this).height("").height())}).height(i))},_eventHandler:function(t){var i=this.options,s=this.active,n=e(t.currentTarget),a=n.closest("li"),o=a[0]===s[0],r=o&&i.collapsible,h=r?e():this._getPanelForTab(a),l=s.length?this._getPanelForTab(s):e(),u={oldTab:s,oldPanel:l,newTab:r?e():a,newPanel:h};t.preventDefault(),a.hasClass("ui-state-disabled")||a.hasClass("ui-tabs-loading")||this.running||o&&!i.collapsible||this._trigger("beforeActivate",t,u)===!1||(i.active=r?!1:this.tabs.index(a),this.active=o?e():a,this.xhr&&this.xhr.abort(),l.length||h.length||e.error("jQuery UI Tabs: Mismatching fragment identifier."),h.length&&this.load(this.tabs.index(a),t),this._toggle(t,u))},_toggle:function(t,i){function s(){a.running=!1,a._trigger("activate",t,i)}function n(){i.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),o.length&&a.options.show?a._show(o,a.options.show,s):(o.show(),s())}var a=this,o=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),n()}):(i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),r.hide(),n()),r.attr("aria-hidden","true"),i.oldTab.attr({"aria-selected":"false","aria-expanded":"false"}),o.length&&r.length?i.oldTab.attr("tabIndex",-1):o.length&&this.tabs.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),o.attr("aria-hidden","false"),i.newTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_activate:function(t){var i,s=this._findActive(t);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(".ui-tabs-anchor")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:e.noop}))},_findActive:function(t){return t===!1?e():this.tabs.eq(t)},_getIndex:function(e){return"string"==typeof e&&(e=this.anchors.index(this.anchors.filter("[href$='"+e+"']"))),e},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible"),this.tablist.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").removeAttr("role"),this.anchors.removeClass("ui-tabs-anchor").removeAttr("role").removeAttr("tabIndex").removeUniqueId(),this.tablist.unbind(this.eventNamespace),this.tabs.add(this.panels).each(function(){e.data(this,"ui-tabs-destroy")?e(this).remove():e(this).removeClass("ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel").removeAttr("tabIndex").removeAttr("aria-live").removeAttr("aria-busy").removeAttr("aria-selected").removeAttr("aria-labelledby").removeAttr("aria-hidden").removeAttr("aria-expanded").removeAttr("role")}),this.tabs.each(function(){var t=e(this),i=t.data("ui-tabs-aria-controls");i?t.attr("aria-controls",i).removeData("ui-tabs-aria-controls"):t.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(t){var i=this.options.disabled;i!==!1&&(void 0===t?i=!1:(t=this._getIndex(t),i=e.isArray(i)?e.map(i,function(e){return e!==t?e:null}):e.map(this.tabs,function(e,i){return i!==t?i:null})),this._setupDisabled(i))},disable:function(t){var i=this.options.disabled;if(i!==!0){if(void 0===t)i=!0;else{if(t=this._getIndex(t),-1!==e.inArray(t,i))return;i=e.isArray(i)?e.merge([t],i).sort():[t]}this._setupDisabled(i)}},load:function(t,i){t=this._getIndex(t);var s=this,n=this.tabs.eq(t),a=n.find(".ui-tabs-anchor"),o=this._getPanelForTab(n),r={tab:n,panel:o};this._isLocal(a[0])||(this.xhr=e.ajax(this._ajaxSettings(a,i,r)),this.xhr&&"canceled"!==this.xhr.statusText&&(n.addClass("ui-tabs-loading"),o.attr("aria-busy","true"),this.xhr.success(function(e){setTimeout(function(){o.html(e),s._trigger("load",i,r)},1)}).complete(function(e,t){setTimeout(function(){"abort"===t&&s.panels.stop(!1,!0),n.removeClass("ui-tabs-loading"),o.removeAttr("aria-busy"),e===s.xhr&&delete s.xhr},1)})))},_ajaxSettings:function(t,i,s){var n=this;return{url:t.attr("href"),beforeSend:function(t,a){return n._trigger("beforeLoad",i,e.extend({jqXHR:t,ajaxSettings:a},s))}}},_getPanelForTab:function(t){var i=e(t).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+i))}}),e.widget("ui.tooltip",{version:"1.11.2",options:{content:function(){var t=e(this).attr("title")||"";return e("<a>").text(t).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,tooltipClass:null,track:!1,close:null,open:null},_addDescribedBy:function(t,i){var s=(t.attr("aria-describedby")||"").split(/\s+/);s.push(i),t.data("ui-tooltip-id",i).attr("aria-describedby",e.trim(s.join(" ")))},_removeDescribedBy:function(t){var i=t.data("ui-tooltip-id"),s=(t.attr("aria-describedby")||"").split(/\s+/),n=e.inArray(i,s);-1!==n&&s.splice(n,1),t.removeData("ui-tooltip-id"),s=e.trim(s.join(" ")),s?t.attr("aria-describedby",s):t.removeAttr("aria-describedby")},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.options.disabled&&this._disable(),this.liveRegion=e("<div>").attr({role:"log","aria-live":"assertive","aria-relevant":"additions"}).addClass("ui-helper-hidden-accessible").appendTo(this.document[0].body)},_setOption:function(t,i){var s=this;return"disabled"===t?(this[i?"_disable":"_enable"](),this.options[t]=i,void 0):(this._super(t,i),"content"===t&&e.each(this.tooltips,function(e,t){s._updateContent(t.element)}),void 0)},_disable:function(){var t=this;e.each(this.tooltips,function(i,s){var n=e.Event("blur");n.target=n.currentTarget=s.element[0],t.close(n,!0)}),this.element.find(this.options.items).addBack().each(function(){var t=e(this);t.is("[title]")&&t.data("ui-tooltip-title",t.attr("title")).removeAttr("title")})},_enable:function(){this.element.find(this.options.items).addBack().each(function(){var t=e(this);t.data("ui-tooltip-title")&&t.attr("title",t.data("ui-tooltip-title"))})},open:function(t){var i=this,s=e(t?t.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),t&&"mouseover"===t.type&&s.parents().each(function(){var t,s=e(this);s.data("ui-tooltip-open")&&(t=e.Event("blur"),t.target=t.currentTarget=this,i.close(t,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._updateContent(s,t))},_updateContent:function(e,t){var i,s=this.options.content,n=this,a=t?t.type:null;return"string"==typeof s?this._open(t,e,s):(i=s.call(e[0],function(i){e.data("ui-tooltip-open")&&n._delay(function(){t&&(t.type=a),this._open(t,e,i)})}),i&&this._open(t,e,i),void 0)},_open:function(t,i,s){function n(e){u.of=e,o.is(":hidden")||o.position(u)}var a,o,r,h,l,u=e.extend({},this.options.position);if(s){if(a=this._find(i))return a.tooltip.find(".ui-tooltip-content").html(s),void 0;i.is("[title]")&&(t&&"mouseover"===t.type?i.attr("title",""):i.removeAttr("title")),a=this._tooltip(i),o=a.tooltip,this._addDescribedBy(i,o.attr("id")),o.find(".ui-tooltip-content").html(s),this.liveRegion.children().hide(),s.clone?(l=s.clone(),l.removeAttr("id").find("[id]").removeAttr("id")):l=s,e("<div>").html(l).appendTo(this.liveRegion),this.options.track&&t&&/^mouse/.test(t.type)?(this._on(this.document,{mousemove:n}),n(t)):o.position(e.extend({of:i},this.options.position)),o.hide(),this._show(o,this.options.show),this.options.show&&this.options.show.delay&&(h=this.delayedShow=setInterval(function(){o.is(":visible")&&(n(u.of),clearInterval(h))},e.fx.interval)),this._trigger("open",t,{tooltip:o}),r={keyup:function(t){if(t.keyCode===e.ui.keyCode.ESCAPE){var s=e.Event(t);s.currentTarget=i[0],this.close(s,!0)}}},i[0]!==this.element[0]&&(r.remove=function(){this._removeTooltip(o)}),t&&"mouseover"!==t.type||(r.mouseleave="close"),t&&"focusin"!==t.type||(r.focusout="close"),this._on(!0,i,r)}},close:function(t){var i,s=this,n=e(t?t.currentTarget:this.element),a=this._find(n);a&&(i=a.tooltip,a.closing||(clearInterval(this.delayedShow),n.data("ui-tooltip-title")&&!n.attr("title")&&n.attr("title",n.data("ui-tooltip-title")),this._removeDescribedBy(n),a.hiding=!0,i.stop(!0),this._hide(i,this.options.hide,function(){s._removeTooltip(e(this))}),n.removeData("ui-tooltip-open"),this._off(n,"mouseleave focusout keyup"),n[0]!==this.element[0]&&this._off(n,"remove"),this._off(this.document,"mousemove"),t&&"mouseleave"===t.type&&e.each(this.parents,function(t,i){e(i.element).attr("title",i.title),delete s.parents[t]}),a.closing=!0,this._trigger("close",t,{tooltip:i}),a.hiding||(a.closing=!1)))},_tooltip:function(t){var i=e("<div>").attr("role","tooltip").addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||"")),s=i.uniqueId().attr("id");return e("<div>").addClass("ui-tooltip-content").appendTo(i),i.appendTo(this.document[0].body),this.tooltips[s]={element:t,tooltip:i}},_find:function(e){var t=e.data("ui-tooltip-id");return t?this.tooltips[t]:null},_removeTooltip:function(e){e.remove(),delete this.tooltips[e.attr("id")]},_destroy:function(){var t=this;e.each(this.tooltips,function(i,s){var n=e.Event("blur"),a=s.element;n.target=n.currentTarget=a[0],t.close(n,!0),e("#"+i).remove(),a.data("ui-tooltip-title")&&(a.attr("title")||a.attr("title",a.data("ui-tooltip-title")),a.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}})});
\ No newline at end of file diff --git a/src/usr/local/www/jquery/pfSense.js b/src/usr/local/www/jquery/pfSense.js new file mode 100644 index 0000000..15a518e --- /dev/null +++ b/src/usr/local/www/jquery/pfSense.js @@ -0,0 +1,242 @@ +/* + * This file should only contain functions that will be used on more than 2 pages + */ + +$(function() { + // Attach collapsable behaviour to select options + (function() + { + var selects = $('select[data-toggle="collapse"]'); + + selects.on('change', function(){ + var options = $(this).find('option'); + var selectedValue = $(this).find(':selected').val(); + + options.each(function(){ + if ($(this).val() == selectedValue) + return; + + targets = $('.toggle-'+ $(this).val() +'.in:not(.toggle-'+ selectedValue +')'); + + // Hide related collapsables which are visible (.in) + targets.collapse('hide'); + + // Disable all invisible inputs + targets.find(':input').prop('disabled', true); + }); + + $('.toggle-' + selectedValue).collapse('show').find(':input').prop('disabled', false); + }); + + // Trigger change to open currently selected item + selects.trigger('change'); + })(); + + + // Add +/- buttons to certain Groups; to allow adding multiple entries + // This time making the buttons col-2 wide so they can fit on the same line as the + // rest of the group (providing the total width of the group is col-8 or less) + (function() + { + var groups = $('div.form-group.user-duplication-horiz'); + var controlsContainer = $('<div class="col-sm-2"></div>'); + var plus = $('<a class="btn btn-sm btn-success">Add</a>'); + var minus = $('<a class="btn btn-sm btn-warning">Delete</a>'); + + minus.on('click', function(){ + $(this).parents('div.form-group').remove(); + }); + + plus.on('click', function(){ + var group = $(this).parents('div.form-group'); + + var clone = group.clone(true); + clone.find('*').val(''); //removeAttr('value'); + clone.appendTo(group.parent()); + }); + + groups.each(function(idx, group){ + var controlsClone = controlsContainer.clone(true).appendTo(group); + minus.clone(true).appendTo(controlsClone); + + if (group == group.parentNode.lastElementChild) + plus.clone(true).appendTo(controlsClone); + }); + })(); + + // Add +/- buttons to certain Groups; to allow adding multiple entries + (function() + { + var groups = $('div.form-group.user-duplication'); + var controlsContainer = $('<div class="col-sm-10 col-sm-offset-2 controls"></div>'); + var plus = $('<a class="btn btn-xs btn-success">Add</a>'); + var minus = $('<a class="btn btn-xs btn-warning">Delete</a>'); + + minus.on('click', function(){ + $(this).parents('div.form-group').remove(); + }); + + plus.on('click', function(){ + var group = $(this).parents('div.form-group'); + + var clone = group.clone(true); + clone.find('*').val(''); //removeAttr('value'); + clone.appendTo(group.parent()); + }); + + groups.each(function(idx, group){ + var controlsClone = controlsContainer.clone(true).appendTo(group); + minus.clone(true).appendTo(controlsClone); + + if (group == group.parentNode.lastElementChild) + plus.clone(true).appendTo(controlsClone); + }); + })(); + + // Find all ipaddress masks and make dynamic based on address family of input + $('span.pfIpMask + select').each(function (idx, select){ + var input = $(select).prevAll('input[type=text]'); + + input.on('change', function(e){ + var isV6 = (input.val().indexOf(':') != -1), min = 0, max = 128; + if (!isV6) + max = 32; + + if (input.val() == "") + return; + + while (select.options.length > max) + select.remove(0); + + if (select.options.length < max) + { + for (var i=select.options.length; i<=max; i++) + select.options.add(new Option(i, i), 0); + } + }); + + // Fire immediately + input.change(); + }); + + // Add confirm to all btn-danger buttons + $('.btn-danger').on('click', function(e){ + var q = 'Are you sure you wish to '+ $.trim(this.textContent) +'?'; + + if ($(this).attr('title') != undefined) + q = $(this).attr('title')+'?'; + + if (!confirm(q)) + e.preventDefault(); + }); + + // Add toggle-all when there are multiple checkboxes and none of them are radio buttons + $('.control-label + .checkbox.multi').each(function() { + var a = $('<a class="btn btn-xs btn-default">toggle all</a>'); + + if(($(this).html().indexOf("type=\"radio\"") == -1)) { + a.on('click', function() { + var wrap = $(this).parents('.form-group').find('.checkbox.multi'), + all = wrap.find('input[type=checkbox]'), + checked = wrap.find('input[type=checkbox]:checked'); + + all.prop('checked', (all.length != checked.length)); + }); + + a.appendTo($(this)); + } + }); + + // Hide advanced inputs by default + if ($('.advanced').length > 0) + { + var advButt = $('<a id="toggle-advanced" class="btn btn-default">toggle advanced options</a>'); + advButt.on('click', function() { + $('.advanced').parents('.form-group').collapse('toggle'); + }); + + advButt.insertAfter($('#save')); + + $('.advanced').parents('.form-group').collapse({toggle: true}); + } + + // Enable popovers globally + $('[data-toggle="popover"]').popover(); + + // Force correct initial state for toggleable checkboxes + $('input[type=checkbox][data-toggle="collapse"]:not(:checked)').each(function() { + $( $(this).data('target') ).addClass('collapse'); + }); + $('input[type=checkbox][data-toggle="disable"]:not(:checked)').each(function() { + $( $(this).data('target') ).prop('disabled', true); + }); + + // Focus first input + $(':input:enabled:visible:first').focus(); + + // Run in-page defined events + while (func = window.events.shift()) + func(); +}); + +// Implement data-toggle=disable +// Source: https://github.com/synergic-cz/synergic-ui/blob/master/src/js/disable.js +;(function($, window, document) { + 'use strict'; + + var Disable = function($element) { + this.$element = $element; + }; + + Disable.prototype.toggle = function() { + this.$element.prop('disabled', !this.$element.prop('disabled')); + }; + + function Plugin(options) { + $(document).trigger('toggle.sui.disable'); + + this.each(function() { + var $this = $(this); + var data = $this.data('sui.disable'); + + if (!data) { + $this.data('sui.disable', (data = new Disable($this))); + } + + if (options === 'toggle') { + data.toggle(); + } + }); + + $(document).trigger('toggled.sui.disable'); + + return this; + } + + var old = $.fn.disable; + + $.fn.disable = Plugin; + $.fn.disable.Constructor = Disable; + + $.fn.disable.noConflict = function() { + $.fn.disable = old; + return this; + }; + + (function(Plugin, $, window) { + $(window).load(function() { + var $controls = $('[data-toggle=disable]'); + + $controls.each(function() { + var $this = $(this); + var eventType = $this.data('disable-event'); + if (!eventType) { + eventType = 'change'; + } + $this.on(eventType + '.sui.disable.data-api', function() { + Plugin.call($($this.data('target')), 'toggle'); + }); + }); + }); + }(Plugin, $, window, document)); +}(jQuery, window, document));
\ No newline at end of file diff --git a/src/usr/local/www/license.php b/src/usr/local/www/license.php new file mode 100644 index 0000000..ed1256d --- /dev/null +++ b/src/usr/local/www/license.php @@ -0,0 +1,154 @@ +<?php +/* $Id$ */ +/* + license.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-system-license +##|*NAME=System: License page +##|*DESCR=Allow access to the 'System: License' page. +##|*MATCH=license.php* +##|-PRIV + +require("guiconfig.inc"); +include("head.inc"); +?> +<div class="panel panel-default"> + <div class="panel-heading"><h4><?=gettext("License")?></h4></div> + <div class="panel-body"> + <p> + <strong><?=$g['product_name']?><?=gettext(" is Copyright")?> © <?=$g['product_copyright_years']?><?=gettext(" by ")?><?=$g['product_copyright']?><br /> + <?=gettext("All rights reserved")?>.</strong> + </p> + <p> + <strong><?=gettext("m0n0wall is Copyright ")?>© <?=gettext("2002-2015 by Manuel Kasper")?> + (<a href="mailto:mk@neon1.net">mk@neon1.net</a>).<br /> + <?=gettext("All rights reserved")?>.</strong> + </p> + <p> + <?=gettext("Redistribution and use in source and binary forms, with or without")?><br /> + <?=gettext("modification, are permitted provided that the following conditions ". + "are met")?>:<br /> + <br /> + <?=gettext("1. Redistributions of source code must retain the above copyright ". + "notice,")?><br /> + <?=gettext("this list of conditions and the following disclaimer")?>.<br /> + <br /> + <?=gettext("2. Redistributions in binary form must reproduce the above copyright")?><br /> + <?=gettext("notice, this list of conditions and the following disclaimer in ". + "the")?><br /> + <?=gettext("documentation and/or other materials provided with the distribution.")?><br /> + <br /> + <strong><?=gettext("THIS SOFTWARE IS PROVIDED ")?>"<?=gettext("AS IS'' AND ANY EXPRESS ". + "OR IMPLIED WARRANTIES,")?><br /> + <?=gettext("INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY")?><br /> + <?=gettext("AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ". + "SHALL THE")?><br /> + <?=gettext("AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ". + "EXEMPLARY,")?><br /> + <?=gettext("OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT ". + "OF")?><br /> + <?=gettext("SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ". + "BUSINESS")?><br /> + <?=gettext("INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER ". + "IN")?><br /> + <?=gettext("CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)")?><br /> + <?=gettext("ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED ". + "OF THE")?><br /> + <?=gettext("POSSIBILITY OF SUCH DAMAGE")?></strong>. + </p> + <hr /> + <p> + <?= "{$g['product_name']} " . gettext("is based upon/includes various free software packages, ". + "listed below.")?><br /> + <?php printf(gettext("The authors of %s would like to thank the authors of these " . + "software packages for their efforts"),$g['product_name'])?>. + </p> + <p> + FreeBSD (<a href="http://www.freebsd.org" target="_blank">http://www.freebsd.org</a>)<br /> + <?=gettext("Copyright")?> ©<?=gettext("1992-2015 The FreeBSD Project. All rights reserved")?>.<br /> + <br /> + <?=gettext("This product includes PHP, freely available from")?><a href="http://www.php.net/" target="_blank">http://www.php.net</a>.<br /> + <?=gettext("Copyright"); ?> © <?=gettext("1999-2015 The PHP Group. All rights reserved.")?>.<br /> + <br /> + <?=gettext("LightTPD"); ?> (<a href="http://www.lighttpd.net" target="_blank">http://www.lighttpd.net)</a><br /> + <?=gettext("Copyright"); ?> ©<?=gettext("2004, Jan Knescke, incremental")?><jan@kneschke.de> + <?=gettext("All rights reserved.")?><br /> + <br /> + <?=gettext("ISC DHCP server ")?>(<a href="http://www.isc.org/products/DHCP/" target="_blank">http://www.isc.org/products/DHCP</a>)<br /> + <?=gettext("Copyright"); ?> © <?=gettext("2004-2012 Internet Software Consortium, Inc.")?><br /> + <?=gettext("Copyright"); ?> © <?=gettext("1995-2003 Internet Software Consortium")?><br /> + <br /> + <?=gettext("PF"); ?> (<a href="http://www.openbsd.org/faq/pf" target="_blank">http://www.openbsd.org</a>)<br /> + <br /> + <?=gettext("MPD - Multi-link PPP daemon for FreeBSD")?> (<a href="http://www.dellroad.org/mpd" target="_blank">http://www.dellroad.org/mpd</a>)<br /> + <?=gettext("Copyright"); ?> © 2003-2004, Archie L. Cobbs, Michael Bretterklieber, Alexander Motin<br /> + <?=gettext("All rights reserved.")?><br /> + <br /> + <?=gettext("Circular log support for FreeBSD syslogd ")?>(<a href="http://software.wheelhouse.org/syslogd/" target="_blank">http://software.wheelhouse.org/syslogd/</a>)<br /> + <?=gettext("Copyright"); ?> © 2001 Jeff Wheelhouse (jdw@wwwi.com)<br /> + <br /> + <?=gettext("Dnsmasq - a DNS forwarder for NAT firewalls")?> (<a href="http://www.thekelleys.org.uk" target="_blank">http://www.thekelleys.org.uk</a>)<br /> + <?=gettext("Copyright"); ?> © 2000-2012 Simon Kelley.<br /> + <br /> + <?=gettext("IPsec-Tools"); ?> (<a href="http://ipsec-tools.sourceforge.net/" target="_blank">http://ipsec-tools.sourceforge.net/</a>)<br /> + <?=gettext("Copyright"); ?> © <?=gettext("1995-2002 WIDE Project. All rights reserved.")?><br /> + <br /> + <?=gettext("msntp"); ?> (<a href="http://www.hpcf.cam.ac.uk/export" target="_blank">http://www.hpcf.cam.ac.uk/export</a>)<br /> + <?=gettext("Copyright"); ?> ©<?=gettext(" 1996, 1997, 2000 N.M. Maclaren, University of Cambridge. ". + "All rights reserved.")?><br /> + <br /> + <?=gettext("UCD-SNMP"); ?> (<a href="http://www.ece.ucdavis.edu/ucd-snmp" target="_blank">http://www.ece.ucdavis.edu/ucd-snmp</a>)<br /> + <?=gettext("Copyright"); ?> © <?=gettext("1989, 1991, 1992 by Carnegie Mellon University.")?><br /> + <?=gettext("Copyright"); ?> © <?=gettext("1996, 1998-2000 The Regents of the University of ". + "California. All rights reserved")?>.<br /> + <?=gettext("Copyright"); ?> © <?=gettext("2001-2002, Network Associates Technology, Inc. ". + "All rights reserved.")?><br /> + <?=gettext("Portions of this code are copyright")?> © <?=gettext("2001-2002, Cambridge ". + "Broadband Ltd. All rights reserved.")?><br /> + <br /> + <?=gettext("choparp"); ?> (<a href="http://choparp.sourceforge.net/" target="_blank">http://choparp.sourceforge.net</a>)<br /> + <?=gettext("Copyright"); ?> © 1997 Takamichi Tateoka (tree@mma.club.uec.ac.jp)<br /> + <?=gettext("Copyright"); ?> © 2002 Thomas Quinot (thomas@cuivre.fr.eu.org)<br /> + <br /> + <?=gettext("php-radius"); ?> (<a href="http://www.mavetju.org/programming/php.php" target="_blank">http://www.mavetju.org/programming/php.php</a>)<br /> + <?=gettext("Copyright 2000, 2001, 2002 by Edwin Groothuis. All rights reserved.")?><br /> + <?=gettext("This product includes software developed by Edwin Groothuis.")?><br /> + <br /> + <?=gettext("wol"); ?> (<a href="http://ahh.sourceforge.net/wol" target="_blank">http://ahh.sourceforge.net/wol</a>)<br /> + <?=gettext("Copyright"); ?> © 2000,2001,2002,2003,2004 Thomas Krennwallner <krennwallner@aon.at> + <br /> + <?=gettext("OpenVPN"); ?> (<a href="http://openvpn.net/" target="_blank">http://openvpn.net/</a>) + <?=gettext("Copyright (C) 2002-2005 OpenVPN Solutions LLC ")?> + </p> + </div> +</div> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/load_balancer_monitor.php b/src/usr/local/www/load_balancer_monitor.php new file mode 100644 index 0000000..2dd9f31 --- /dev/null +++ b/src/usr/local/www/load_balancer_monitor.php @@ -0,0 +1,160 @@ +<?php +/* $Id$ */ +/* + load_balancer_monitor.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2008 Bill Marquette <bill.marquette@gmail.com>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-services-loadbalancer-monitor +##|*NAME=Services: Load Balancer: Monitors page +##|*DESCR=Allow access to the 'Services: Load Balancer: Monitors' page. +##|*MATCH=load_balancer_monitor.php* +##|-PRIV + +require_once("guiconfig.inc"); + +if (!is_array($config['load_balancer']['monitor_type'])) { + $config['load_balancer']['monitor_type'] = array(); +} +$a_monitor = &$config['load_balancer']['monitor_type']; + +if ($_POST) { + $pconfig = $_POST; + + if ($_POST['apply']) { + $retval = 0; + $retval |= filter_configure(); + $retval |= relayd_configure(); + + $savemsg = get_std_save_message($retval); + clear_subsystem_dirty('loadbalancer'); + } +} + +if ($_GET['act'] == "del") { + if (array_key_exists($_GET['id'], $a_monitor)) { + /* make sure no pools reference this entry */ + if (is_array($config['load_balancer']['lbpool'])) { + foreach ($config['load_balancer']['lbpool'] as $pool) { + if ($pool['monitor'] == $a_monitor[$_GET['id']]['name']) { + $input_errors[] = gettext("This entry cannot be deleted because it is still referenced by at least one pool."); + break; + } + } + } + + if (!$input_errors) { + unset($a_monitor[$_GET['id']]); + write_config(); + mark_subsystem_dirty('loadbalancer'); + header("Location: load_balancer_monitor.php"); + exit; + } + } +} + +$pgtitle = array(gettext("Services"),gettext("Load Balancer"),gettext("Monitor")); +$shortcut_section = "relayd"; + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('loadbalancer')) + print_info_box_np(gettext("The load balancer configuration has been changed") . ".<br />" . gettext("You must apply the changes in order for them to take effect.")); + +/* active tabs */ +$tab_array = array(); +$tab_array[] = array(gettext("Pools"), false, "load_balancer_pool.php"); +$tab_array[] = array(gettext("Virtual Servers"), false, "load_balancer_virtual_server.php"); +$tab_array[] = array(gettext("Monitors"), true, "load_balancer_monitor.php"); +$tab_array[] = array(gettext("Settings"), false, "load_balancer_setting.php"); +display_top_tabs($tab_array); +?> + +<form action="load_balancer_monitor.php" method="post"> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Monitor')?></h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext('Name')?></th> + <th><?=gettext('Type')?></th> + <th><?=gettext('Description')?></th> + <th><?=gettext('Action')?></th> + </tr> + </thead> + <tbody> +<?php +$idx = 0; +foreach($a_monitor as $monitor) { +?> + <tr> + <td> + <?=$monitor['name']?> + </td> + <td> + <?=$monitor['type']?> + </td> + <td> + <?=$monitor['descr']?> + </td> + <td> + <a href="load_balancer_monitor_edit.php?id=<?=$idx?>" class="btn btn-xs btn-info"><?=gettext('Edit')?></a> + <a href="load_balancer_monitor.php?act=del&id=<?=$idx?>" class="btn btn-xs btn-danger"><?=gettext('Delete')?></a> + <a href="load_balancer_monitor_edit.php?act=dup&id=<?=$idx?>" class="btn btn-xs btn-default"><?=gettext('Duplicate')?></a> + </td> + </tr> +<?php + $idx++; +} +?> + </tbody> + </table> + </div> + + <nav class="action-buttons"> + <a href="load_balancer_monitor_edit.php" class="btn btn-success"><?=gettext('Add')?></a> + </nav> + + </div> +</form> + + +<?php + +include("foot.inc"); diff --git a/src/usr/local/www/load_balancer_monitor_edit.php b/src/usr/local/www/load_balancer_monitor_edit.php new file mode 100644 index 0000000..b7134db --- /dev/null +++ b/src/usr/local/www/load_balancer_monitor_edit.php @@ -0,0 +1,383 @@ +<?php +/* $Id$ */ +/* + load_balancer_monitor_edit.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2008 Bill Marquette <bill.marquette@gmail.com>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-services-loadbalancer-monitor-edit +##|*NAME=Services: Load Balancer: Monitor: Edit page +##|*DESCR=Allow access to the 'Services: Load Balancer: Monitor: Edit' page. +##|*MATCH=load_balancer_monitor_edit.php* +##|-PRIV + +require("guiconfig.inc"); + +$referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/load_balancer_monitor.php'); + +if (!is_array($config['load_balancer']['monitor_type'])) { + $config['load_balancer']['monitor_type'] = array(); +} +$a_monitor = &$config['load_balancer']['monitor_type']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_monitor[$id]) { + $pconfig['name'] = $a_monitor[$id]['name']; + $pconfig['type'] = $a_monitor[$id]['type']; + $pconfig['descr'] = $a_monitor[$id]['descr']; + $pconfig['options'] = array(); + $pconfig['options'] = $a_monitor[$id]['options']; +} else { + /* Some sane page defaults */ + $pconfig['options']['path'] = '/'; + $pconfig['options']['code'] = 200; +} + +$changedesc = gettext("Load Balancer: Monitor:") . " "; +$changecount = 0; + +if ($_POST) { + $changecount++; + + unset($input_errors); + $pconfig = $_POST; + + /* turn $_POST['http_options_*'] into $pconfig['options'][*] */ + foreach ($_POST as $key => $val) { + if (stristr($key, 'options') !== false) { + if (stristr($key, $pconfig['type'].'_') !== false) { + $opt = explode('_', $key); + $pconfig['options'][$opt[2]] = $val; + } + unset($pconfig[$key]); + } + } + + /* input validation */ + $reqdfields = explode(" ", "name type descr"); + $reqdfieldsn = array(gettext("Name"), gettext("Type"), gettext("Description")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + /* Ensure that our monitor names are unique */ + for ($i = 0; isset($config['load_balancer']['monitor_type'][$i]); $i++) { + if (($_POST['name'] == $config['load_balancer']['monitor_type'][$i]['name']) && ($i != $id)) { + $input_errors[] = gettext("This monitor name has already been used. Monitor names must be unique."); + } + } + + if (preg_match('/[ \/]/', $_POST['name'])) { + $input_errors[] = gettext("You cannot use spaces or slashes in the 'name' field."); + } + + if (strlen($_POST['name']) > 16) { + $input_errors[] = gettext("The 'name' field must be 16 characters or less."); + } + + switch ($_POST['type']) { + case 'icmp': { + break; + } + case 'tcp': { + break; + } + case 'http': + case 'https': { + if (is_array($pconfig['options'])) { + if (isset($pconfig['options']['host']) && $pconfig['options']['host'] != "") { + if (!is_hostname($pconfig['options']['host'])) { + $input_errors[] = gettext("The hostname can only contain the characters A-Z, 0-9 and '-'."); + } + } + if (isset($pconfig['options']['code']) && $pconfig['options']['code'] != "") { + // Check code + if (!is_rfc2616_code($pconfig['options']['code'])) { + $input_errors[] = gettext("HTTP(s) codes must be from RFC2616."); + } + } + if (!isset($pconfig['options']['path']) || $pconfig['options']['path'] == "") { + $input_errors[] = gettext("The path to monitor must be set."); + } + } + break; + } + case 'send': { + if (is_array($pconfig['options'])) { + if (isset($pconfig['options']['send']) && $pconfig['options']['send'] != "") { + // Check send + } + if (isset($pconfig['options']['expect']) && $pconfig['options']['expect'] != "") { + // Check expect + } + } + break; + } + } + + if (!$input_errors) { + $monent = array(); + if (isset($id) && $a_monitor[$id]) { + $monent = $a_monitor[$id]; + } + if ($monent['name'] != "") { + $changedesc .= " " . sprintf(gettext("modified '%s' monitor:"), $monent['name']); + } + + update_if_changed("name", $monent['name'], $pconfig['name']); + update_if_changed("type", $monent['type'], $pconfig['type']); + update_if_changed("description", $monent['descr'], $pconfig['descr']); + if ($pconfig['type'] == "http" || $pconfig['type'] == "https") { + /* log updates, then clear array and reassign - dumb, but easiest way to have a clear array */ + update_if_changed("path", $monent['options']['path'], $pconfig['options']['path']); + update_if_changed("host", $monent['options']['host'], $pconfig['options']['host']); + update_if_changed("code", $monent['options']['code'], $pconfig['options']['code']); + $monent['options'] = array(); + $monent['options']['path'] = $pconfig['options']['path']; + $monent['options']['host'] = $pconfig['options']['host']; + $monent['options']['code'] = $pconfig['options']['code']; + } + if ($pconfig['type'] == "send") { + /* log updates, then clear array and reassign - dumb, but easiest way to have a clear array */ + update_if_changed("send", $monent['options']['send'], $pconfig['options']['send']); + update_if_changed("expect", $monent['options']['expect'], $pconfig['options']['expect']); + $monent['options'] = array(); + $monent['options']['send'] = $pconfig['options']['send']; + $monent['options']['expect'] = $pconfig['options']['expect']; + } + if ($pconfig['type'] == "tcp" || $pconfig['type'] == "icmp") { + $monent['options'] = array(); + } + + if (isset($id) && $a_monitor[$id]) { + /* modify all pools with this name */ + for ($i = 0; isset($config['load_balancer']['lbpool'][$i]); $i++) { + if ($config['load_balancer']['lbpool'][$i]['monitor'] == $a_monitor[$id]['name']) { + $config['load_balancer']['lbpool'][$i]['monitor'] = $monent['name']; + } + } + $a_monitor[$id] = $monent; + } else { + $a_monitor[] = $monent; + } + + if ($changecount > 0) { + /* Mark config dirty */ + mark_subsystem_dirty('loadbalancer'); + write_config($changedesc); + } + + header("Location: load_balancer_monitor.php"); + exit; + } +} + +$pgtitle = array(gettext("Services"), gettext("Load Balancer"), gettext("Monitor"), gettext("Edit")); +$shortcut_section = "relayd"; + +include("head.inc"); +$types = array("icmp" => gettext("ICMP"), "tcp" => gettext("TCP"), "http" => gettext("HTTP"), "https" => gettext("HTTPS"), "send" => gettext("Send/Expect")); + +?> + + + +<script type="text/javascript"> +//<![CDATA[ +events.push(function(){ + + // Hides all elements of the specified class. This will usually be a section + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // Hide all sections except 't' + function updateType(t){ + switch(t) { + <?php + /* OK, so this is sick using php to generate javascript, but it needed to be done */ + foreach ($types as $key => $val) { + echo " case \"{$key}\": {\n"; + $t = $types; + foreach ($t as $k => $v) { + if ($k != $key) { + echo " hideClass('{$k}', true);\n"; + } + } + echo " }\n"; + } + ?> + } + + hideClass(t, false); + } + + + // On click . . + $('#type').on('change', function() { + updateType($('#type').val()); + }); + + // On page load + updateType($('#type').val()); +}); + +//]]> +</script> + +<?php +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(new Form_Button( + 'Submit', + gettext("Save") +)); + +$section = new Form_Section('Edit Load Balancer - Monitor entry'); + +$section->addInput(new Form_Input( + 'name', + 'Name', + 'text', + $pconfig['name'] +)); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +)); + +$section->addInput(new Form_Select( + 'type', + 'Type', + $pconfig['type'], + $types +)); + +$form->add($section); + +$section = new Form_Section('HTTP Options'); +$section->addClass('http'); + +$section->addInput(new Form_Input( + 'http_options_path', + 'Path', + 'text', + $pconfig['options']['path'] +)); + +$section->addInput(new Form_Input( + 'http_options_host', + 'Host', + 'text', + $pconfig['options']['host'] +))->setHelp('Hostname for Host: header if needed.'); + +$section->addInput(new Form_Select( + 'http_options_code', + 'HTTP Code', + $pconfig['options']['code'], + $rfc2616 +)); + +$form->add($section); + +$section = new Form_Section('HTTPS Options'); +$section->addClass('https'); + +$section->addInput(new Form_Input( + 'https_options_path', + 'Path', + 'text', + $pconfig['options']['path'] +)); + +$section->addInput(new Form_Input( + 'https_options_host', + 'Host', + 'text', + $pconfig['options']['host'] +))->setHelp('Hostname for Host: header if needed.'); + +$section->addInput(new Form_Select( + 'https_options_code', + 'HTTPS Code', + $pconfig['options']['code'], + $rfc2616 +)); + +$form->add($section); + +$section = new Form_Section('Send/Expect Options'); +$section->addClass('send'); + +$section->addInput(new Form_Input( + 'send_options_send', + 'Send', + 'text', + $pconfig['options']['send'] +)); + +$section->addInput(new Form_Input( + 'send_options_expect', + 'Expect', + 'text', + $pconfig['options']['expect'] +)); + +if (isset($id) && $a_monitor[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); + +print($form); + +include("foot.inc"); diff --git a/src/usr/local/www/load_balancer_pool.php b/src/usr/local/www/load_balancer_pool.php new file mode 100644 index 0000000..dbdbb1c --- /dev/null +++ b/src/usr/local/www/load_balancer_pool.php @@ -0,0 +1,193 @@ +<?php +/* $Id$ */ +/* + load_balancer_pool.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2005-2008 Bill Marquette <bill.marquette@gmail.com>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-loadbalancer-pool +##|*NAME=Load Balancer: Pool page +##|*DESCR=Allow access to the 'Load Balancer: Pool' page. +##|*MATCH=load_balancer_pool.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +if (!is_array($config['load_balancer']['lbpool'])) { + $config['load_balancer']['lbpool'] = array(); +} + +$a_pool = &$config['load_balancer']['lbpool']; + + +if ($_POST) { + $pconfig = $_POST; + + if ($_POST['apply']) { + $retval = 0; + $retval |= filter_configure(); + $retval |= relayd_configure(); + + $savemsg = get_std_save_message($retval); + clear_subsystem_dirty('loadbalancer'); + } +} + +if ($_GET['act'] == "del") { + if (array_key_exists($_GET['id'], $a_pool)) { + /* make sure no virtual servers reference this entry */ + if (is_array($config['load_balancer']['virtual_server'])) { + foreach ($config['load_balancer']['virtual_server'] as $vs) { + if ($vs['poolname'] == $a_pool[$_GET['id']]['name']) { + $input_errors[] = gettext("This entry cannot be deleted because it is still referenced by at least one virtual server."); + break; + } + } + } + + if (!$input_errors) { + unset($a_pool[$_GET['id']]); + write_config(); + mark_subsystem_dirty('loadbalancer'); + header("Location: load_balancer_pool.php"); + exit; + } + } +} + +/* Index monitor_type array for easy hyperlinking */ +$mondex = array(); +for ($i = 0; isset($config['load_balancer']['monitor_type'][$i]); $i++) { + $mondex[$config['load_balancer']['monitor_type'][$i]['name']] = $i; +} + +for ($i = 0; isset($config['load_balancer']['lbpool'][$i]); $i++) { + $a_pool[$i]['monitor'] = "<a href=\"/load_balancer_monitor_edit.php?id={$mondex[$a_pool[$i]['monitor']]}\">" . htmlspecialchars($a_pool[$i]['monitor']) . "</a>"; +} + +$pgtitle = array(gettext("Services"), gettext("Load Balancer"),gettext("Pool")); +$shortcut_section = "relayd"; + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg); + +if (is_subsystem_dirty('loadbalancer')) + print_info_box_np(sprintf(gettext("The load balancer configuration has been changed%sYou must apply the changes in order for them to take effect."), "<br />")); + +/* active tabs */ +$tab_array = array(); +$tab_array[] = array(gettext("Pools"), true, "load_balancer_pool.php"); +$tab_array[] = array(gettext("Virtual Servers"), false, "load_balancer_virtual_server.php"); +$tab_array[] = array(gettext("Monitors"), false, "load_balancer_monitor.php"); +$tab_array[] = array(gettext("Settings"), false, "load_balancer_setting.php"); +display_top_tabs($tab_array); + +?> +<form action="load_balancer_pool.php" method="post"> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Pool')?></h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext('Name')?></th> + <th><?=gettext('Mode')?></th> + <th><?=gettext('Servers')?></th> + <th><?=gettext('Port')?></th> + <th><?=gettext('Monitor')?></th> + <th><?=gettext('Description')?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php + +$idx = 0; +foreach($a_pool as $pool) { +?> + <tr> + <td> + <?=$pool['name']?> + </td> + <td> + <?=$pool['mode']?> + </td> + <td> +<?php + $numsvrs = count($pool['servers']) - 1; + + foreach ($pool['servers'] as $server => $ip) { + print($ip); + print(($server < $numsvrs) ? '<br />':''); + } + +?> + </td> + <td> + <?=$pool['port']?> + </td> + <td> + <?=$pool['monitor']?> + </td> + <td> + <?=$pool['descr']?> + </td> + <td> + <a href="load_balancer_pool_edit.php?id=<?=$idx?>" class="btn btn-xs btn-info"><?=gettext('Edit')?></a> + <a href="load_balancer_pool.php?act=del&id=<?=$idx?>" class="btn btn-xs btn-danger"><?=gettext('Delete')?></a> + <a href="load_balancer_pool_edit.php?act=dup&id=<?=$idx?>" class="btn btn-xs btn-default"><?=gettext('Duplicate')?></a> + </td> + </tr> +<?php + $idx++; +} +?> + </tbody> + </table> + </div> + + <nav class="action-buttons"> + <a href="load_balancer_pool_edit.php" class="btn btn-success"><?=gettext('Add')?></a> + </nav> + + </div> +</form> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/load_balancer_pool_edit.php b/src/usr/local/www/load_balancer_pool_edit.php new file mode 100644 index 0000000..876abe5 --- /dev/null +++ b/src/usr/local/www/load_balancer_pool_edit.php @@ -0,0 +1,446 @@ +<?php +/* $Id$ */ +/* + load_balancer_pool_edit.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2005-2008 Bill Marquette <bill.marquette@gmail.com>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-loadbalancer-pool-edit +##|*NAME=Load Balancer: Pool: Edit page +##|*DESCR=Allow access to the 'Load Balancer: Pool: Edit' page. +##|*MATCH=load_balancer_pool_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("filter.inc"); +require_once("util.inc"); + +$referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/load_balancer_pool.php'); + +if (!is_array($config['load_balancer']['lbpool'])) { + $config['load_balancer']['lbpool'] = array(); +} + +$a_pool = &$config['load_balancer']['lbpool']; + +if (is_numericint($_GET['id'])) + $id = $_GET['id']; +if (isset($_POST['id']) && is_numericint($_POST['id'])) + $id = $_POST['id']; + +if (isset($id) && $a_pool[$id]) { + $pconfig['name'] = $a_pool[$id]['name']; + $pconfig['mode'] = $a_pool[$id]['mode']; + $pconfig['descr'] = $a_pool[$id]['descr']; + $pconfig['port'] = $a_pool[$id]['port']; + $pconfig['retry'] = $a_pool[$id]['retry']; + $pconfig['servers'] = &$a_pool[$id]['servers']; + $pconfig['serversdisabled'] = &$a_pool[$id]['serversdisabled']; + $pconfig['monitor'] = $a_pool[$id]['monitor']; +} + +$changedesc = gettext("Load Balancer: Pool:") . " "; +$changecount = 0; + +if ($_POST) { + $changecount++; + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "name mode port monitor servers"); + $reqdfieldsn = array(gettext("Name"),gettext("Mode"),gettext("Port"),gettext("Monitor"),gettext("Server List")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + /* Ensure that our pool names are unique */ + for ($i=0; isset($config['load_balancer']['lbpool'][$i]); $i++) + if (($_POST['name'] == $config['load_balancer']['lbpool'][$i]['name']) && ($i != $id)) + $input_errors[] = gettext("This pool name has already been used. Pool names must be unique."); + + if (preg_match('/[ \/]/', $_POST['name'])) + $input_errors[] = gettext("You cannot use spaces or slashes in the 'name' field."); + + if (strlen($_POST['name']) > 16) + $input_errors[] = gettext("The 'name' field must be 16 characters or less."); + + if (in_array($_POST['name'], $reserved_table_names)) + $input_errors[] = sprintf(gettext("The name '%s' is a reserved word and cannot be used."), $_POST['name']); + + if (is_alias($_POST['name'])) + $input_errors[] = sprintf(gettext("Sorry, an alias is already named %s."), $_POST['name']); + + if (!is_portoralias($_POST['port'])) + $input_errors[] = gettext("The port must be an integer between 1 and 65535, or a port alias."); + + // May as well use is_port as we want a positive integer and such. + if (!empty($_POST['retry']) && !is_port($_POST['retry'])) + $input_errors[] = gettext("The retry value must be an integer between 1 and 65535."); + + if (is_array($_POST['servers'])) { + foreach($pconfig['servers'] as $svrent) { + if (!is_ipaddr($svrent) && !is_subnetv4($svrent)) { + $input_errors[] = sprintf(gettext("%s is not a valid IP address or IPv4 subnet (in \"enabled\" list)."), $svrent); + } + else if (is_subnetv4($svrent) && subnet_size($svrent) > 64) { + $input_errors[] = sprintf(gettext("%s is a subnet containing more than 64 IP addresses (in \"enabled\" list)."), $svrent); + } + } + } + + if (is_array($_POST['serversdisabled'])) { + foreach($pconfig['serversdisabled'] as $svrent) { + if (!is_ipaddr($svrent) && !is_subnetv4($svrent)) { + $input_errors[] = sprintf(gettext("%s is not a valid IP address or IPv4 subnet (in \"disabled\" list)."), $svrent); + } + else if (is_subnetv4($svrent) && subnet_size($svrent) > 64) { + $input_errors[] = sprintf(gettext("%s is a subnet containing more than 64 IP addresses (in \"disabled\" list)."), $svrent); + } + } + } + + $m = array(); + + for ($i=0; isset($config['load_balancer']['monitor_type'][$i]); $i++) + $m[$config['load_balancer']['monitor_type'][$i]['name']] = $config['load_balancer']['monitor_type'][$i]; + + if (!isset($m[$_POST['monitor']])) + $input_errors[] = gettext("Invalid monitor chosen."); + + if (!$input_errors) { + $poolent = array(); + if(isset($id) && $a_pool[$id]) + $poolent = $a_pool[$id]; + + if($poolent['name'] != "") + $changedesc .= sprintf(gettext(" modified '%s' pool:"), $poolent['name']); + + update_if_changed("name", $poolent['name'], $_POST['name']); + update_if_changed("mode", $poolent['mode'], $_POST['mode']); + update_if_changed("description", $poolent['descr'], $_POST['descr']); + update_if_changed("port", $poolent['port'], $_POST['port']); + update_if_changed("retry", $poolent['retry'], $_POST['retry']); + update_if_changed("servers", $poolent['servers'], $_POST['servers']); + update_if_changed("serversdisabled", $poolent['serversdisabled'], $_POST['serversdisabled']); + update_if_changed("monitor", $poolent['monitor'], $_POST['monitor']); + + if (isset($id) && $a_pool[$id]) { + /* modify all virtual servers with this name */ + for ($i = 0; isset($config['load_balancer']['virtual_server'][$i]); $i++) { + if ($config['load_balancer']['virtual_server'][$i]['lbpool'] == $a_pool[$id]['name']) + $config['load_balancer']['virtual_server'][$i]['lbpool'] = $poolent['name']; + } + $a_pool[$id] = $poolent; + } else + $a_pool[] = $poolent; + + if ($changecount > 0) { + /* Mark pool dirty */ + mark_subsystem_dirty('loadbalancer'); + write_config($changedesc); + } + + header("Location: load_balancer_pool.php"); + exit; + } +} + +$pgtitle = array(gettext("Services"), gettext("Load Balancer"),gettext("Pool"),gettext("Edit")); +$shortcut_section = "relayd"; + +include("head.inc"); +?> + +<script type="text/javascript"> +//<![CDATA[ +events.push(function(){ + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // Select every option in the specified multiselect + function AllServers(id, selectAll) { + for (i = 0; i < id.length; i++) { + id.eq(i).prop('selected', selectAll); + } + } + + // Move all selected options from one multiselect to another + function moveOptions(From, To) { + var len = From.length; + var option; + + if(len > 1) { + for(i=0; i<len; i++) { + if(From.eq(i).is(':selected')) { + option = From.eq(i).val(); + To.append(new Option(option, option)); + From.eq(i).remove(); + } + } + } + } + + function checkPoolControls() { + + if ($("#mode").val() == "failover") { + disableInput('movetoenabled', $('[name="servers[]"] option').length > 0); + } else { + disableInput('movetoenabled',false); + } + } + + // Move (copy/delete) all but one of the items in the Enabled (server) list to the Disabled list + function enforceFailover() { + if ($('#mode').val() != 'failover') { + return; + } + + var len = $('[name="servers[]"] option').length; + var option; + + if(len > 1) { + for(i=len-1; i>0; i--) { + option = $('[name="servers[]"] option').eq(i).val(); + $('[name="serversdisabled[]"]').append(new Option(option, option)); + $('[name="servers[]"] option').eq(i).remove(); + } + } + } + + // Make buttons plain buttons, not a submit + $("#btnaddtopool").prop('type','button'); + $("#removeenabled").prop('type','button'); + $("#removedisabled").prop('type','button'); + $("#movetodisabled").prop('type','button'); + $("#movetoenabled").prop('type','button'); + + // On click . . + $("#btnaddtopool").click(function() { + $('[name="servers[]"]').append(new Option($('#ipaddr').val(), $('#ipaddr').val())); + enforceFailover(); + checkPoolControls(); + }); + + $('#mode').on('change', function() { + enforceFailover(); + checkPoolControls(); + }); + + $("#removeenabled").click(function() { + $('[name="servers[]"] option:selected').remove(); + }); + + $("#removedisabled").click(function() { + $('[name="serversdisabled[]"] option:selected').remove(); + }); + + $("#movetodisabled").click(function() { + moveOptions($('[name="servers[]"] option'), $('[name="serversdisabled[]"]')); + }); + + $("#movetoenabled").click(function() { + moveOptions($('[name="serversdisabled[]"] option'), $('[name="servers[]"]')); + }); + + // On initial page load + checkPoolControls(); + + // On submit + $('form').submit(function(){ + AllServers($('[name="servers[]"] option'), true); + AllServers($('[name="serversdisabled[]"] option'), true); + }); + +}); +//]]> +</script> + +<?php +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(new Form_Button( + 'Submit', + gettext("Save") +)); + +$section = new Form_Section('Add/edit Load Balancer - Pool entry'); + +$section->addInput(new Form_Input( + 'name', + 'Name', + 'text', + $pconfig['name'] +)); + +$section->addInput(new Form_Select( + 'mode', + 'Mode', + $pconfig['mode'], + array( + 'loadbalance' => 'Load Balance', + 'failover' => 'Manual Failover' + ) +)); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +)); + +$section->addInput(new Form_Input( + 'port', + 'Port', + 'text', + $pconfig['port'] +))->setHelp('This is the port your servers are listening on. You may also specify a port alias listed in Firewall -> Aliases here.'); + +$section->addInput(new Form_Input( + 'retry', + 'Retry', + 'number', + $pconfig['retry'], + ['min' => '1', 'max' => '65536'] +))->setHelp('Optionally specify how many times to retry checking a server before declaring it down.'); + +$form->add($section); + +$section = new Form_Section('Add item to the pool'); + +$monitorlist = array(); + +foreach ($config['load_balancer']['monitor_type'] as $monitor) + $monitorlist[$monitor['name']] = $monitor['name']; + +if(count($config['load_balancer']['monitor_type'])) { + $section->addInput(new Form_Select( + 'monitor', + 'Monitor', + $pconfig['monitor'], + $monitorlist + )); +} else { + $section->addInput(new Form_StaticText( + 'Monitor', + 'Please add a monitor IP address on the monitors tab if you wish to use this feature."' + )); +} + +$group = new Form_Group('Server IP Address'); + +$group->add(new Form_IpAddress( + 'ipaddr', + 'IP Address', + $pconfig['ipaddr'] +)); + +$group->add(new Form_Button( + 'btnaddtopool', + 'Add to pool' +))->removeClass('btn-primary')->addClass('btn-default'); + +$section->add($group); + +$form->add($section); + +$section = new Form_Section('Current pool members'); + +$group = new Form_Group('Members'); + +$group->add(new Form_Select( + 'serversdisabled', + null, + $pconfig['serversdisabled'], + is_array($pconfig['serversdisabled']) ? array_combine($pconfig['serversdisabled'], $pconfig['serversdisabled']) : array(), + true +))->setHelp('Disabled'); + +$group->add(new Form_Select( + 'servers', + null, + $pconfig['servers'], + is_array($pconfig['servers']) ? array_combine($pconfig['servers'], $pconfig['servers']) : array(), + true +))->setHelp('Enabled (Default)'); + +$section->add($group); + +$group = new Form_Group(''); + +$group->add(new Form_Button( + 'removedisabled', + 'Remove' +))->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$group->add(new Form_Button( + 'removeenabled', + 'Remove' +))->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->add($group); + +$group = new Form_Group(''); + +$group->add(new Form_Button( + 'movetoenabled', + 'Move to enabled list >' +))->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$group->add(new Form_Button( + 'movetodisabled', + '< Move to disabled list' +))->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->add($group); + +if (isset($id) && $a_pool[$id] && $_GET['act'] != 'dup') { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); + +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/load_balancer_setting.php b/src/usr/local/www/load_balancer_setting.php new file mode 100644 index 0000000..371c91f --- /dev/null +++ b/src/usr/local/www/load_balancer_setting.php @@ -0,0 +1,153 @@ +<?php +/* $Id$ */ +/* + load_balancer_setting.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2008 Bill Marquette <bill.marquette@gmail.com>. + Copyright (C) 2012 Pierre POMES <pierre.pomes@gmail.com>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-services-loadbalancer-setting +##|*NAME=Services: Load Balancer: setting page +##|*DESCR=Allow access to the 'Settings: Load Balancer: Settings' page. +##|*MATCH=load_balancer_setting.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("util.inc"); + +if (!is_array($config['load_balancer']['setting'])) { + $config['load_balancer']['setting'] = array(); +} + +$lbsetting = &$config['load_balancer']['setting']; + +if ($_POST) { + if ($_POST['apply']) { + $retval = 0; + $retval |= filter_configure(); + $retval |= relayd_configure(); + + $savemsg = get_std_save_message($retval); + clear_subsystem_dirty('loadbalancer'); + } else { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if ($_POST['timeout'] && !is_numeric($_POST['timeout'])) { + $input_errors[] = gettext("Timeout must be a numeric value"); + } + + if ($_POST['interval'] && !is_numeric($_POST['interval'])) { + $input_errors[] = gettext("Interval must be a numeric value"); + } + + if ($_POST['prefork']) { + if (!is_numeric($_POST['prefork'])) { + $input_errors[] = gettext("Prefork must be a numeric value"); + } else { + if (($_POST['prefork']<=0) || ($_POST['prefork']>32)) { + $input_errors[] = gettext("Prefork value must be between 1 and 32"); + } + } + } + + /* update config if user entry is valid */ + if (!$input_errors) { + $lbsetting['timeout'] = $_POST['timeout']; + $lbsetting['interval'] = $_POST['interval']; + $lbsetting['prefork'] = $_POST['prefork']; + + write_config(); + mark_subsystem_dirty('loadbalancer'); + } + } +} + +$pgtitle = array(gettext("Services"),gettext("Load Balancer"),gettext("Settings")); +$shortcut_section = "relayd"; + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('loadbalancer')) + print_info_box_np(gettext("The load balancer configuration has been changed") . ' ' . + gettext("You must apply the changes in order for them to take effect."), 'Apply', null, false, 'danger'); + +/* active tabs */ +$tab_array = array(); +$tab_array[] = array(gettext("Pools"), false, "load_balancer_pool.php"); +$tab_array[] = array(gettext("Virtual Servers"), false, "load_balancer_virtual_server.php"); +$tab_array[] = array(gettext("Monitors"), false, "load_balancer_monitor.php"); +$tab_array[] = array(gettext("Settings"), true, "load_balancer_setting.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Relayd Global Settings'); + +$section->addInput(new Form_Input( + 'timeout', + 'Timeout', + 'text', + $pconfig['timeout'] +))->setHelp('Set the global timeout in milliseconds for checks. Leave blank to use the default value of 1000 ms'); + +$section->addInput(new Form_Input( + 'interval', + 'Interval', + 'text', + $pconfig['interval'] +))->setHelp('Set the interval in seconds at which the member of a pool will be checked. Leave blank to use the default interval of 10 seconds'); + +$section->addInput(new Form_Input( + 'prefork', + 'Prefork', + 'text', + $pconfig['prefork'] +))->setHelp('Number of processes used by relayd for dns protocol. Leave blank to use the default value of 5 processes'); + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/load_balancer_virtual_server.php b/src/usr/local/www/load_balancer_virtual_server.php new file mode 100644 index 0000000..dec55f1 --- /dev/null +++ b/src/usr/local/www/load_balancer_virtual_server.php @@ -0,0 +1,152 @@ +<?php +/* $Id$ */ +/* + load_balancer_virtual_server.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2005-2008 Bill Marquette <bill.marquette@gmail.com>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-services-loadbalancer-virtualservers +##|*NAME=Services: Load Balancer: Virtual Servers page +##|*DESCR=Allow access to the 'Services: Load Balancer: Virtual Servers' page. +##|*MATCH=load_balancer_virtual_server.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("vslb.inc"); + +if (!is_array($config['load_balancer']['virtual_server'])) { + $config['load_balancer']['virtual_server'] = array(); +} +$a_vs = &$config['load_balancer']['virtual_server']; + +if ($_POST) { + $pconfig = $_POST; + + if ($_POST['apply']) { + $retval = 0; + $retval |= filter_configure(); + $retval |= relayd_configure(); + $savemsg = get_std_save_message($retval); + /* Wipe out old relayd anchors no longer in use. */ + cleanup_lb_marked(); + clear_subsystem_dirty('loadbalancer'); + } +} + +if ($_GET['act'] == "del") { + if (array_key_exists($_GET['id'], $a_vs)) { + + if (!$input_errors) { + cleanup_lb_mark_anchor($a_vs[$_GET['id']]['name']); + unset($a_vs[$_GET['id']]); + write_config(); + mark_subsystem_dirty('loadbalancer'); + header("Location: load_balancer_virtual_server.php"); + exit; + } + } +} + +/* Index lbpool array for easy hyperlinking */ +$poodex = array(); +for ($i = 0; isset($config['load_balancer']['lbpool'][$i]); $i++) { + $poodex[$config['load_balancer']['lbpool'][$i]['name']] = $i; +} +for ($i = 0; isset($config['load_balancer']['virtual_server'][$i]); $i++) { + if ($a_vs[$i]) { + $a_vs[$i]['poolname'] = "<a href=\"/load_balancer_pool_edit.php?id={$poodex[$a_vs[$i]['poolname']]}\">" . htmlspecialchars($a_vs[$i]['poolname']) . "</a>"; + if ($a_vs[$i]['sitedown'] != '') { + $a_vs[$i]['sitedown'] = "<a href=\"/load_balancer_pool_edit.php?id={$poodex[$a_vs[$i]['sitedown']]}\">" . htmlspecialchars($a_vs[$i]['sitedown']) . "</a>"; + } else { + $a_vs[$i]['sitedown'] = 'none'; + } + } +} + +$pgtitle = array(gettext("Services"), gettext("Load Balancer"), gettext("Virtual Servers")); +$shortcut_section = "relayd-virtualservers"; + +include("head.inc"); + +?> +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<form action="load_balancer_virtual_server.php" method="post"> +<?php if ($input_errors) print_input_errors($input_errors); ?> +<?php if ($savemsg) print_info_box($savemsg); ?> +<?php if (is_subsystem_dirty('loadbalancer')): ?><br/> +<?php print_info_box_np(gettext("The virtual server configuration has been changed") . ".<br />" . gettext("You must apply the changes in order for them to take effect."));?><br /> +<?php endif; ?> +<table width="100%" border="0" cellpadding="0" cellspacing="0" summary="load balancer virtual server"> + <tr><td class="tabnavtbl"> +<?php + /* active tabs */ + $tab_array = array(); + $tab_array[] = array(gettext("Pools"), false, "load_balancer_pool.php"); + $tab_array[] = array(gettext("Virtual Servers"), true, "load_balancer_virtual_server.php"); + $tab_array[] = array(gettext("Monitors"), false, "load_balancer_monitor.php"); + $tab_array[] = array(gettext("Settings"), false, "load_balancer_setting.php"); + display_top_tabs($tab_array); +?> + </td></tr> + <tr> + <td> + <div id="mainarea"> +<?php + $t = new MainTable(); + $t->edit_uri('load_balancer_virtual_server_edit.php'); + $t->my_uri('load_balancer_virtual_server.php'); + $t->add_column(gettext('Name'), 'name', 10); + $t->add_column(gettext('Protocol'), 'relay_protocol', 10); + $t->add_column(gettext('IP Address'), 'ipaddr', 15); + $t->add_column(gettext('Port'), 'port', 10); + $t->add_column(gettext('Pool'), 'poolname', 15); + $t->add_column(gettext('Fall Back Pool'), 'sitedown', 15); + $t->add_column(gettext('Description'), 'descr', 30); + $t->add_button('edit'); + $t->add_button('dup'); + $t->add_button('del'); + $t->add_content_array($a_vs); + $t->display(); +?> + </div> + </td> + </tr> +</table> +</form> +<?php include("fend.inc"); ?> +</body> +</html> diff --git a/src/usr/local/www/load_balancer_virtual_server_edit.php b/src/usr/local/www/load_balancer_virtual_server_edit.php new file mode 100644 index 0000000..39bc6c9 --- /dev/null +++ b/src/usr/local/www/load_balancer_virtual_server_edit.php @@ -0,0 +1,315 @@ +<?php +/* $Id$ */ +/* + load_balancer_virtual_server_edit.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2005-2008 Bill Marquette <bill.marquette@gmail.com>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-loadbalancer-virtualserver-edit +##|*NAME=Load Balancer: Virtual Server: Edit page +##|*DESCR=Allow access to the 'Load Balancer: Virtual Server: Edit' page. +##|*MATCH=load_balancer_virtual_server_edit.php* +##|-PRIV + +require("guiconfig.inc"); + +if (isset($_POST['referer'])) { + $referer = $_POST['referer']; +} else { + $referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/load_balancer_virtual_server.php'); +} + +if (!is_array($config['load_balancer']['virtual_server'])) { + $config['load_balancer']['virtual_server'] = array(); +} +$a_vs = &$config['load_balancer']['virtual_server']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_vs[$id]) { + $pconfig = $a_vs[$id]; +} else { + // Sane defaults + $pconfig['mode'] = 'redirect_mode'; +} + +$changedesc = gettext("Load Balancer: Virtual Server:") . " "; +$changecount = 0; + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + switch ($pconfig['mode']) { + case "redirect_mode": { + $reqdfields = explode(" ", "ipaddr name mode"); + $reqdfieldsn = array(gettext("IP Address"), gettext("Name"), gettext("Mode")); + break; + } + case "relay_mode": { + $reqdfields = explode(" ", "ipaddr name mode relay_protocol"); + $reqdfieldsn = array(gettext("IP Address"), gettext("Name"), gettext("Mode"), gettext("Relay Protocol")); + break; + } + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + for ($i = 0; isset($config['load_balancer']['virtual_server'][$i]); $i++) { + if (($_POST['name'] == $config['load_balancer']['virtual_server'][$i]['name']) && ($i != $id)) { + $input_errors[] = gettext("This virtual server name has already been used. Virtual server names must be unique."); + } + } + + if (preg_match('/[ \/]/', $_POST['name'])) { + $input_errors[] = gettext("You cannot use spaces or slashes in the 'name' field."); + } + + if (strlen($_POST['name']) > 32) { + $input_errors[] = gettext("The 'name' field must be 32 characters or less."); + } + + if ($_POST['port'] != "" && !is_portoralias($_POST['port'])) { + $input_errors[] = gettext("The port must be an integer between 1 and 65535, a port alias, or left blank."); + } + + if (!is_ipaddroralias($_POST['ipaddr']) && !is_subnetv4($_POST['ipaddr'])) { + $input_errors[] = sprintf(gettext("%s is not a valid IP address, IPv4 subnet, or alias."), $_POST['ipaddr']); + } else if (is_subnetv4($_POST['ipaddr']) && subnet_size($_POST['ipaddr']) > 64) { + $input_errors[] = sprintf(gettext("%s is a subnet containing more than 64 IP addresses."), $_POST['ipaddr']); + } + + if ((strtolower($_POST['relay_protocol']) == "dns") && !empty($_POST['sitedown'])) { + $input_errors[] = gettext("You cannot select a Fall Back Pool when using the DNS relay protocol."); + } + + if (!$input_errors) { + $vsent = array(); + if (isset($id) && $a_vs[$id]) { + $vsent = $a_vs[$id]; + } + if ($vsent['name'] != "") { + $changedesc .= " " . sprintf(gettext("modified '%s' vs:"), $vsent['name']); + } else { + $changedesc .= " " . sprintf(gettext("created '%s' vs:"), $_POST['name']); + } + + update_if_changed("name", $vsent['name'], $_POST['name']); + update_if_changed("descr", $vsent['descr'], $_POST['descr']); + update_if_changed("poolname", $vsent['poolname'], $_POST['poolname']); + update_if_changed("port", $vsent['port'], $_POST['port']); + update_if_changed("sitedown", $vsent['sitedown'], $_POST['sitedown']); + update_if_changed("ipaddr", $vsent['ipaddr'], $_POST['ipaddr']); + update_if_changed("mode", $vsent['mode'], $_POST['mode']); + update_if_changed("relay protocol", $vsent['relay_protocol'], $_POST['relay_protocol']); + + if ($_POST['sitedown'] == "") { + unset($vsent['sitedown']); + } + + if (isset($id) && $a_vs[$id]) { + if ($a_vs[$id]['name'] != $_POST['name']) { + /* Because the VS name changed, mark the old name for cleanup. */ + cleanup_lb_mark_anchor($a_vs[$id]['name']); + } + $a_vs[$id] = $vsent; + } else { + $a_vs[] = $vsent; + } + + if ($changecount > 0) { + /* Mark virtual server dirty */ + mark_subsystem_dirty('loadbalancer'); + write_config($changedesc); + } + + header("Location: load_balancer_virtual_server.php"); + exit; + } +} + +$pgtitle = array(gettext("Services"), gettext("Load Balancer"), gettext("Virtual Server"), gettext("Edit")); +$shortcut_section = "relayd-virtualservers"; + +include("head.inc"); + +?> + +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<script type="text/javascript" src="/javascript/autosuggest.js?rev=1"></script> +<script type="text/javascript" src="/javascript/suggestions.js"></script> + +<?php if ($input_errors) print_input_errors($input_errors); ?> +<form action="load_balancer_virtual_server_edit.php" method="post" name="iform" id="iform"> + <table width="100%" border="0" cellpadding="6" cellspacing="0" summary="load balancer server entry"> + <tr> + <td colspan="3" valign="top" class="listtopic"><?=gettext("Edit Load Balancer - Virtual Server entry"); ?></td> + </tr> + <tr align="left"> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Name"); ?></td> + <td width="78%" class="vtable" colspan="2"> + <input name="name" type="text" <?if (isset($pconfig['name'])) echo "value=\"" . htmlspecialchars($pconfig['name']) . "\"";?> size="32" maxlength="32" /> + </td> + </tr> + <tr align="left"> + <td width="22%" valign="top" class="vncell"><?=gettext("Description"); ?></td> + <td width="78%" class="vtable" colspan="2"> + <input name="descr" type="text" <?if (isset($pconfig['descr'])) echo "value=\"" . htmlspecialchars($pconfig['descr']) . "\"";?> size="64" /> + </td> + </tr> + <tr align="left"> + <td width="22%" valign="top" class="vncellreq"><?=gettext("IP Address"); ?></td> + <td width="78%" class="vtable" colspan="2"> + <input class="formfldalias" id="ipaddr" name="ipaddr" type="text" <?if (isset($pconfig['ipaddr'])) echo "value=\"" . htmlspecialchars($pconfig['ipaddr']) . "\"";?> size="39" maxlength="39" /> + <br /><?=gettext("This is normally the WAN IP address that you would like the server to listen on. All connections to this IP and port will be forwarded to the pool cluster."); ?> + <br /><?=gettext("You may also specify a host alias listed in Firewall -> Aliases here."); ?> + <script type="text/javascript"> + //<![CDATA[ + var host_aliases = <?= json_encode(get_alias_list(array("host", "network", "url", "urltable"))) ?>; + var oTextbox1 = new AutoSuggestControl(document.getElementById("ipaddr"), new StateSuggestions(host_aliases)); + //]]> + </script> + </td> + </tr> + <tr align="left"> + <td width="22%" valign="top" class="vncell"><?=gettext("Port"); ?></td> + <td width="78%" class="vtable" colspan="2"> + <input class="formfldalias" name="port" id="port" type="text" <?if (isset($pconfig['port'])) echo "value=\"" . htmlspecialchars($pconfig['port']) . "\"";?> size="16" maxlength="16" /> + <br /><?=gettext("This is the port that the clients will connect to. All connections to this port will be forwarded to the pool cluster."); ?> + <br /><?=gettext("If left blank, listening ports from the pool will be used."); ?> + <br /><?=gettext("You may also specify a port alias listed in Firewall -> Aliases here."); ?> + <script type="text/javascript"> + //<![CDATA[ + var port_aliases = <?= json_encode(get_alias_list(array("port", "url_ports", "urltable_ports"))) ?>; + var oTextbox2 = new AutoSuggestControl(document.getElementById("port"), new StateSuggestions(port_aliases)); + //]]> + </script> + </td> + </tr> + <tr align="left"> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Virtual Server Pool"); ?></td> + <td width="78%" class="vtable" colspan="2"> + <?php if (count($config['load_balancer']['lbpool']) == 0): ?> + <b><?=gettext("NOTE:"); ?></b> <?=gettext("Please add a pool on the Pools tab to use this feature."); ?> + <?php else: ?> + <select id="poolname" name="poolname"> + <?php + for ($i = 0; isset($config['load_balancer']['lbpool'][$i]); $i++) { + $selected = ""; + if ($config['load_balancer']['lbpool'][$i]['name'] == $pconfig['poolname']) { + $selected = " selected=\"selected\""; + } + echo "<option value=\"" . htmlspecialchars($config['load_balancer']['lbpool'][$i]['name']) . "\"{$selected}>{$config['load_balancer']['lbpool'][$i]['name']}</option>"; + } + ?> + </select> + <?php endif; ?> + </td> + </tr> + <tr align="left"> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Fall Back Pool"); ?></td> + <td width="78%" class="vtable" colspan="2"> + <?php if (count($config['load_balancer']['lbpool']) == 0): ?> + <b><?=gettext("NOTE:"); ?></b> <?=gettext("Please add a pool on the Pools tab to use this feature."); ?> + <?php else: ?> + <select id="sitedown" name="sitedown"> + <option value=""<?=htmlspecialchars($pconfig['sitedown']) == '' ? ' selected' : ''?>><?=gettext("none"); ?></option> + <?php + for ($i = 0; isset($config['load_balancer']['lbpool'][$i]); $i++) { + $selected = ""; + if ($config['load_balancer']['lbpool'][$i]['name'] == $pconfig['sitedown']) { + $selected = " selected=\"selected\""; + } + echo "<option value=\"" . htmlspecialchars($config['load_balancer']['lbpool'][$i]['name']) . "\"{$selected}>{$config['load_balancer']['lbpool'][$i]['name']}</option>"; + } + ?> + </select> + <br /><?=gettext("The server pool to which clients will be redirected if *ALL* servers in the Virtual Server Pool are offline."); ?> + <br /><?=gettext("This option is NOT compatible with the DNS relay protocol."); ?> + <?php endif; ?> + </td> + </tr> + <tr style="display:none;"><td><input type="hidden" name="mode" value="redirect_mode" /></td></tr> +<!-- + <tr align="left"> + <td width="22%" valign="top" class="vncellreq">Mode</td> + <td width="78%" class="vtable" colspan="2"> + <input id="redirect_mode" type="radio" name="mode" value="redirect"<?=htmlspecialchars($pconfig['mode']) == 'redirect' ? ' checked="checked"': ''?> /> Redirect + <input id="relay_mode" type="radio" name="mode" value="relay"<?=htmlspecialchars($pconfig['mode']) == 'relay' ? ' checked="checked"': ''?> /> Relay + <br /> + </td> + </tr> +--> + <tr id="relay" align="left"> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Relay Protocol"); ?></td> + <td width="78%" class="vtable" colspan="2"> + <select id="relay_protocol" name="relay_protocol"> + <?php + $lb_def_protos = array("tcp", "dns"); + foreach ($lb_def_protos as $lb_proto) { + $selected = ""; + if ($pconfig['relay_protocol'] == $lb_proto) { + $selected = " selected=\"selected\""; + } + echo "<option value=\"{$lb_proto}\"{$selected}>{$lb_proto}</option>"; + } + ?> + </select> + <br /> + </td> + </tr> + <tr align="left"> + <td width="22%" valign="top"> </td> + <td align="left" valign="bottom" width="78%"> + <input name="Submit" type="submit" class="formbtn" value="<?=gettext("Submit"); ?>" /> + <input type="button" class="formbtn" value="<?=gettext("Cancel");?>" onclick="window.location.href='<?=$referer;?>'" /> + <input name="referer" type="hidden" value="<?=$referer;?>" /> + <?php if (isset($id) && $a_vs[$id] && $_GET['act'] != 'dup'): ?> + <input name="id" type="hidden" value="<?=htmlspecialchars($id);?>" /> + <?php endif; ?> + </td> + </tr> + </table> +</form> +<br /> +<span class="red"><strong><?=gettext("Note:"); ?></strong></span> <?=gettext("Don't forget to add a firewall rule for the virtual server/pool after you're finished setting it up."); ?> +<?php include("fend.inc"); ?> +</body> +</html> diff --git a/src/usr/local/www/logo-black.png b/src/usr/local/www/logo-black.png Binary files differnew file mode 100644 index 0000000..124d674 --- /dev/null +++ b/src/usr/local/www/logo-black.png diff --git a/src/usr/local/www/logo.png b/src/usr/local/www/logo.png Binary files differnew file mode 100644 index 0000000..9362fcb --- /dev/null +++ b/src/usr/local/www/logo.png diff --git a/src/usr/local/www/pkg.php b/src/usr/local/www/pkg.php new file mode 100755 index 0000000..c56ece5 --- /dev/null +++ b/src/usr/local/www/pkg.php @@ -0,0 +1,582 @@ +<?php +/* $Id$ */ +/* + pkg.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2004-2012 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: pkgs +*/ + +##|+PRIV +##|*IDENT=page-package-settings +##|*NAME=Package: Settings page +##|*DESCR=Allow access to the 'Package: Settings' page. +##|*MATCH=pkg.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("pkg-utils.inc"); + +function gentitle_pkg($pgname) { + global $config; + return $config['system']['hostname'] . "." . $config['system']['domain'] . " - " . $pgname; +} + +function domTT_title($title_msg) { + print "onmouseout=\"this.style.color = ''; domTT_mouseout(this, event);\" onmouseover=\"domTT_activate(this, event, 'content', '".gettext($title_msg)."', 'trail', true, 'delay', 0, 'fade', 'both', 'fadeMax', 93, 'styleClass', 'niceTitle');\""; +} + +$xml = $_REQUEST['xml']; + +if ($xml == "") { + print_info_box_np(gettext("ERROR: No package defined.")); + exit; +} else { + if (file_exists("/usr/local/pkg/" . $xml)) { + $pkg = parse_xml_config_pkg("/usr/local/pkg/" . $xml, "packagegui"); + } else { + echo "File not found " . htmlspecialchars($xml); + exit; + } +} + +if ($pkg['donotsave'] <> "") { + header("Location: pkg_edit.php?xml=" . $xml); + exit; +} + +if ($pkg['include_file'] != "") { + require_once($pkg['include_file']); +} + +$package_name = $pkg['menu'][0]['name']; +$section = $pkg['menu'][0]['section']; +$config_path = $pkg['configpath']; +$title = $pkg['title']; + +if ($_REQUEST['startdisplayingat']) { + $startdisplayingat = $_REQUEST['startdisplayingat']; +} + +if ($_REQUEST['display_maximum_rows']) { + if ($_REQUEST['display_maximum_rows']) { + $display_maximum_rows = $_REQUEST['display_maximum_rows']; + } +} + +$evaledvar = $config['installedpackages'][xml_safe_fieldname($pkg['name'])]['config']; + +if ($_GET['act'] == "update") { + + if (is_array($config['installedpackages'][$pkg['name']]) && $pkg['name'] != "" && $_REQUEST['ids'] !="") { + #get current values + $current_values=$config['installedpackages'][$pkg['name']]['config']; + #get updated ids + parse_str($_REQUEST['ids'], $update_list); + #sort ids to know what to change + #useful to do not lose data when using sorting and paging + $sort_list=$update_list['ids']; + sort($sort_list); + #apply updates + foreach ($update_list['ids'] as $key=> $value) { + $config['installedpackages'][$pkg['name']]['config'][$sort_list[$key]]=$current_values[$update_list['ids'][$key]]; + } + #save current config + write_config(); + #sync package + eval ("{$pkg['custom_php_resync_config_command']}"); + } + #function called via jquery, no need to continue after save changes. + exit; +} +if ($_GET['act'] == "del") { + // loop through our fieldnames and automatically setup the fieldnames + // in the environment. ie: a fieldname of username with a value of + // testuser would automatically eval $username = "testuser"; + foreach ($evaledvar as $ip) { + if ($pkg['adddeleteeditpagefields']['columnitem']) { + foreach ($pkg['adddeleteeditpagefields']['columnitem'] as $column) { + ${xml_safe_fieldname($column['fielddescr'])} = $ip[xml_safe_fieldname($column['fieldname'])]; + } + } + } + + $a_pkg = &$config['installedpackages'][xml_safe_fieldname($pkg['name'])]['config']; + + if ($a_pkg[$_GET['id']]) { + unset($a_pkg[$_GET['id']]); + write_config(); + if ($pkg['custom_delete_php_command'] <> "") { + if ($pkg['custom_php_command_before_form'] <> "") { + eval($pkg['custom_php_command_before_form']); + } + eval($pkg['custom_delete_php_command']); + } + header("Location: pkg.php?xml=" . $xml); + exit; + } +} + +ob_start(); + +$iflist = get_configured_interface_with_descr(false, true); +$evaledvar = $config['installedpackages'][xml_safe_fieldname($pkg['name'])]['config']; + +if ($pkg['custom_php_global_functions'] <> "") { + eval($pkg['custom_php_global_functions']); +} + +if ($pkg['custom_php_command_before_form'] <> "") { + eval($pkg['custom_php_command_before_form']); +} + +$pgtitle = array($title); +include("head.inc"); + +?> + +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<script type="text/javascript" src="javascript/domTT/domLib.js"></script> +<script type="text/javascript" src="javascript/domTT/domTT.js"></script> +<script type="text/javascript" src="javascript/domTT/behaviour.js"></script> +<script type="text/javascript" src="javascript/domTT/fadomatic.js"></script> +<script type="text/javascript"> +//<![CDATA[ + function setFilter(filtertext) { + jQuery('#pkg_filter').val(filtertext); + document.pkgform.submit(); + } + + <?php + if ($pkg['adddeleteeditpagefields']['movable']) { + ?> + jQuery(document).ready(function() { + jQuery('#mainarea table tbody').sortable({ + items: 'tr.sortable', + cursor: 'move', + distance: 10, + opacity: 0.8, + helper: function(e, ui) { + ui.children().each(function() { + jQuery(this).width(jQuery(this).width()); + }); + return ui; + }, + }); + }); + function save_changes_to_xml(xml) { + var ids=jQuery('#mainarea table tbody').sortable('serialize', {key:"ids[]"}); + var strloading="<img src='/themes/<?= $g['theme']; ?>/images/misc/loader.gif' alt='loader' /> " + "<?=gettext('Saving changes...');?>"; + if (confirm("<?=gettext("Do you really want to save changes?");?>")) { + jQuery.ajax({ + type: 'get', + cache: false, + url: "<?=$_SERVER['SCRIPT_NAME'];?>", + data: {xml:'<?=$xml?>', act:'update', ids: ids}, + beforeSend: function() { + jQuery('#savemsg').empty().html(strloading); + }, + error: function(data) { + jQuery('#savemsg').empty().html('Error:' + data); + }, + success: function(data) { + jQuery('#savemsg').empty().html(data); + } + }); + } + } + <?php + } + ?> +//]]> +</script> +<form action="pkg.php" name="pkgform" method="get"> +<input type='hidden' name='xml' value='<?=$_REQUEST['xml']?>' /> +<?php if ($_GET['savemsg'] <> "") $savemsg = htmlspecialchars($_GET['savemsg']); ?> +<div id="savemsg"></div> +<?php if ($savemsg) print_info_box($savemsg); ?> +<table width="100%" border="0" cellpadding="0" cellspacing="0" summary="package settings"> +<?php + if ($pkg['tabs'] <> "") { + $tab_array = array(); + foreach ($pkg['tabs']['tab'] as $tab) { + if ($tab['tab_level']) { + $tab_level = $tab['tab_level']; + } else { + $tab_level = 1; + } + if (isset($tab['active'])) { + $active = true; + } else { + $active = false; + } + if (isset($tab['no_drop_down'])) { + $no_drop_down = true; + } + $urltmp = ""; + if ($tab['url'] <> "") { + $urltmp = $tab['url']; + } + if ($tab['xml'] <> "") { + $urltmp = "pkg_edit.php?xml=" . $tab['xml']; + } + + $addresswithport = getenv("HTTP_HOST"); + $colonpos = strpos($addresswithport, ":"); + if ($colonpos !== False) { + //my url is actually just the IP address of the pfsense box + $myurl = substr($addresswithport, 0, $colonpos); + } else { + $myurl = $addresswithport; + } + // eval url so that above $myurl item can be processed if need be. + $url = str_replace('$myurl', $myurl, $urltmp); + + $tab_array[$tab_level][] = array( + $tab['text'], + $active, + $url + ); + } + + ksort($tab_array); + foreach ($tab_array as $tab) { + echo '<tr><td>'; + display_top_tabs($tab, $no_drop_down); + echo '</td></tr>'; + } + } +?> +<tr><td><div id="mainarea"><table width="100%" border="0" cellpadding="0" cellspacing="0" summary="main area"> + <tr> + <td class="tabcont"> + <table width="100%" border="0" cellpadding="6" cellspacing="0" summary="tabs"> +<?php + /* Handle filtering bar A-Z */ + $include_filtering_inputbox = false; + $colspan = 0; + if ($pkg['adddeleteeditpagefields']['columnitem'] <> "") { + foreach ($pkg['adddeleteeditpagefields']['columnitem'] as $column) { + $colspan++; + } + } + if ($pkg['fields']['field']) { + // First find the sorting type field if it exists + foreach ($pkg['fields']['field'] as $field) { + if ($field['type'] == "sorting") { + if (isset($field['include_filtering_inputbox'])) { + $include_filtering_inputbox = true; + } + if ($display_maximum_rows < 1) { + if ($field['display_maximum_rows']) { + $display_maximum_rows = $field['display_maximum_rows']; + } + } + echo "<tr><td class='listhdrr' colspan='$colspan' align='center'>"; + echo "Filter by: "; + $isfirst = true; + for ($char = 65; $char < 91; $char++) { + if (!$isfirst) { + echo " | "; + } + echo "<a href=\"#\" onclick=\"setFilter('" . chr($char) . "');\">" . chr($char) . "</a>"; + $isfirst = false; + } + echo "</td></tr>"; + echo "<tr><td class='listhdrr' colspan='$colspan' align='center'>"; + if ($field['sortablefields']) { + echo "Filter field: <select name='pkg_filter_type'>"; + foreach ($field['sortablefields']['item'] as $si) { + if ($si['name'] == $_REQUEST['pkg_filter_type']) { + $SELECTED = "selected=\"selected\""; + } else { + $SELECTED = ""; + } + echo "<option value='{$si['name']}' {$SELECTED}>{$si['name']}</option>"; + } + echo "</select>"; + } + if ($include_filtering_inputbox) { + echo " Filter text: <input id='pkg_filter' name='pkg_filter' value='" . $_REQUEST['pkg_filter'] . "' /> <input type='submit' value='Filter' />"; + } + echo "</td></tr><tr><td><font size='-3'> </font></td></tr>"; + } + } + } +?> + <tr> +<?php + if ($display_maximum_rows) { + $totalpages = ceil(round((count($evaledvar) / $display_maximum_rows), 9)); + $page = 1; + $tmpcount = 0; + $tmppp = 0; + if (is_array($evaledvar)) { + foreach ($evaledvar as $ipa) { + if ($tmpcount == $display_maximum_rows) { + $page++; + $tmpcount = 0; + } + if ($tmppp == $startdisplayingat) { + break; + } + $tmpcount++; + $tmppp++; + } + } + echo "<tr><td colspan='" . count($pkg['adddeleteeditpagefields']['columnitem']) . "'>"; + echo "<table width='100%' summary=''>"; + echo "<tr>"; + echo "<td align='left'>Displaying page $page of $totalpages</b></td>"; + echo "<td align='right'>Rows per page: <select onchange='document.pkgform.submit();' name='display_maximum_rows'>"; + for ($x = 0; $x < 250; $x++) { + if ($x == $display_maximum_rows) { + $SELECTED = "selected=\"selected\""; + } else { + $SELECTED = ""; + } + echo "<option value='$x' $SELECTED>$x</option>\n"; + $x = $x + 4; + } + echo "</select></td></tr>"; + echo "</table>"; + echo "</td></tr>"; + } + $cols = 0; + if ($pkg['adddeleteeditpagefields']['columnitem'] <> "") { + foreach ($pkg['adddeleteeditpagefields']['columnitem'] as $column) { + echo "<td class=\"listhdrr\">" . $column['fielddescr'] . "</td>"; + $cols++; + } + } +?> + </tr> +<?php + $i = 0; + $pagination_startingrow = 0; + $pagination_counter = 0; + if ($evaledvar) { + foreach ($evaledvar as $ip) { + if ($startdisplayingat) { + if ($i < $startdisplayingat) { + $i++; + continue; + } + } + if ($_REQUEST['pkg_filter']) { + // Handle filtered items + if ($pkg['fields']['field'] && !$filter_regex) { + // First find the sorting type field if it exists + foreach ($pkg['fields']['field'] as $field) { + if ($field['type'] == "sorting") { + if ($field['sortablefields']['item']) { + foreach ($field['sortablefields']['item'] as $sf) { + if ($sf['name'] == $_REQUEST['pkg_filter_type']) { + $filter_fieldname = $sf['fieldname']; + #Use a default regex on sortable fields when none is declared + if ($sf['regex']) { + $filter_regex = str_replace("%FILTERTEXT%", $_REQUEST['pkg_filter'], trim($sf['regex'])); + } else { + $filter_regex = "/{$_REQUEST['pkg_filter']}/i"; + } + } + } + } + } + } + } + // Do we have something to filter on? + unset($filter_matches); + if ($pkg['adddeleteeditpagefields']['columnitem'] <> "") { + foreach ($pkg['adddeleteeditpagefields']['columnitem'] as $column) { + $fieldname = $ip[xml_safe_fieldname($column['fieldname'])]; + if ($column['fieldname'] == $filter_fieldname) { + if ($filter_regex) { + //echo "$filter_regex - $fieldname<p/>"; + preg_match($filter_regex, $fieldname, $filter_matches); + break; + } + } + } + } + if (!$filter_matches) { + $i++; + continue; + } + } + if ($pkg['adddeleteeditpagefields']['movable']) { + echo "<tr valign=\"top\" class=\"sortable\" id=\"id_{$i}\">\n"; + } else { + echo "<tr valign=\"top\">\n"; + } + if ($pkg['adddeleteeditpagefields']['columnitem'] <> "") { + foreach ($pkg['adddeleteeditpagefields']['columnitem'] as $column) { + if ($column['fieldname'] == "description") { + $class = "listbg"; + } else { + $class = "listlr"; + } +?> + <td class="<?=$class;?>" ondblclick="document.location='pkg_edit.php?xml=<?=$xml?>&act=edit&id=<?=$i;?>';"> +<?php + $fieldname = $ip[xml_safe_fieldname($column['fieldname'])]; + #Check if columnitem has a type field declared + if ($column['type'] == "checkbox") { + if ($fieldname == "") { + echo gettext("No"); + } else { + echo gettext("Yes"); + } + } else if ($column['type'] == "interface") { + echo $column['prefix'] . $iflist[$fieldname] . $column['suffix']; + } else { + #Check if columnitem has an encoding field declared + if ($column['encoding'] == "base64") { + echo $column['prefix'] . base64_decode($fieldname) . $column['suffix']; + #Check if there is a custom info to show when $fieldname is not empty + } else if ($column['listmodeon'] && $fieldname != "") { + echo $column['prefix'] . gettext($column['listmodeon']). $column['suffix']; + #Check if there is a custom info to show when $fieldname is empty + } else if ($column['listmodeoff'] && $fieldname == "") { + echo $column['prefix'] .gettext($column['listmodeoff']). $column['suffix']; + } else { + echo $column['prefix'] . $fieldname ." ". $column['suffix']; + } + } +?> + </td> +<?php + } // foreach columnitem + } // if columnitem +?> + <td valign="middle" class="list nowrap"> + <table border="0" cellspacing="0" cellpadding="1" summary="icons"> + <tr> +<?php + #Show custom description to edit button if defined + $edit_msg=($pkg['adddeleteeditpagefields']['edittext']?$pkg['adddeleteeditpagefields']['edittext']:"Edit this item"); +?> + <td valign="middle"><a href="pkg_edit.php?xml=<?=$xml?>&act=edit&id=<?=$i;?>"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_e.gif" width="17" height="17" border="0" <?=domTT_title($edit_msg)?> alt="edit" /></a></td> +<?php + #Show custom description to delete button if defined + $delete_msg=($pkg['adddeleteeditpagefields']['deletetext']?$pkg['adddeleteeditpagefields']['deletetext']:"Delete this item"); +?> + <td valign="middle"><a href="pkg.php?xml=<?=$xml?>&act=del&id=<?=$i;?>" onclick="return confirm('<?=gettext("Do you really want to delete this item?");?>')"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_x.gif" width="17" height="17" border="0" <?=domTT_title($delete_msg)?> alt="delete" /></a></td> + </tr> + </table> + </td> +<?php + echo "</tr>\n"; // Pairs with an echo tr some way above + // Handle pagination and display_maximum_rows + if ($display_maximum_rows) { + if ($pagination_counter == ($display_maximum_rows-1) or + $i == (count($evaledvar)-1)) { + $colcount = count($pkg['adddeleteeditpagefields']['columnitem']); + $final_footer = ""; + $final_footer .= "<tr><td colspan='$colcount'>"; + $final_footer .= "<table width='100%' summary=''><tr>"; + $final_footer .= "<td align='left'>"; + $startingat = $startdisplayingat - $display_maximum_rows; + if ($startingat > -1) { + $final_footer .= "<a href='pkg.php?xml=" . $_REQUEST['xml'] . "&startdisplayingat={$startingat}&display_maximum_rows={$display_maximum_rows}'>"; + } else if ($startdisplayingat > 1) { + $final_footer .= "<a href='pkg.php?xml=" . $_REQUEST['xml'] . "&startdisplayingat=0&display_maximum_rows={$display_maximum_rows}'>"; + } + $final_footer .= "<font size='2'><< Previous page</font></a>"; + if ($tmppp + $display_maximum_rows > count($evaledvar)) { + $endingrecord = count($evaledvar); + } else { + $endingrecord = $tmppp + $display_maximum_rows; + } + $final_footer .= "</td><td align='center'>"; + $tmppp++; + $final_footer .= "<font size='2'>Displaying {$tmppp} - {$endingrecord} / " . count($evaledvar) . " records"; + $final_footer .= "</font></td><td align='right'> "; + if (($i+1) < count($evaledvar)) { + $final_footer .= "<a href='pkg.php?xml=" . $_REQUEST['xml'] . "&startdisplayingat=" . ($startdisplayingat + $display_maximum_rows) . "&display_maximum_rows={$display_maximum_rows}'>"; + } + $final_footer .= "<font size='2'>Next page >></font></a>"; + $final_footer .= "</td></tr></table></td></tr>"; + $i = count($evaledvar); + break; + } + } + $i++; + $pagination_counter++; + } // foreach evaledvar + } // if evaledvar +?> + <tr> + <td colspan="<?=$cols?>"></td> + <td> + <table border="0" cellspacing="0" cellpadding="1" summary="icons"> + <tr> +<?php + #Show custom description to add button if defined + $add_msg=($pkg['adddeleteeditpagefields']['addtext']?$pkg['adddeleteeditpagefields']['addtext']:"Add a new item"); +?> + <td valign="middle"><a href="pkg_edit.php?xml=<?=$xml?>&id=<?=$i?>"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" width="17" height="17" border="0" <?=domTT_title($add_msg)?> alt="add" /></a></td> +<?php + #Show description button and info if defined + if ($pkg['adddeleteeditpagefields']['description']) { +?> + <td valign="middle"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_info_pkg.gif" width="17" height="17" border="0" <?=domTT_title($pkg['adddeleteeditpagefields']['description'])?> alt="info" /></td> +<?php + } +?> + </tr> + </table> + </td> + </tr> + <?=$final_footer?> +<?php + #Show save button only when movable is defined + if ($pkg['adddeleteeditpagefields']['movable']) { +?> + <tr> + <td><input class="formbtn" type="button" value="Save" name="Submit" onclick="save_changes_to_xml('<?=$xml?>')" /></td> + </tr> +<?php + } +?> + </table> + </td> + </tr> +</table></div></td></tr> +</table> + +</form> +<?php include("fend.inc"); ?> + +<?php + echo "<!-- filter_fieldname: {$filter_fieldname} -->"; + echo "<!-- filter_regex: {$filter_regex} -->"; +?> + +</body> +</html> diff --git a/src/usr/local/www/pkg_edit.php b/src/usr/local/www/pkg_edit.php new file mode 100644 index 0000000..0b01a28 --- /dev/null +++ b/src/usr/local/www/pkg_edit.php @@ -0,0 +1,1267 @@ +<?php +/* $Id$ */ +/* + pkg_edit.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2004-2012 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: pkgs +*/ + +##|+PRIV +##|*IDENT=page-package-edit +##|*NAME=Package: Edit page +##|*DESCR=Allow access to the 'Package: Edit' page. +##|*MATCH=pkg_edit.php* +##|-PRIV + +ini_set('max_execution_time', '0'); + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("pkg-utils.inc"); + +/* dummy stubs needed by some code that was MFC'd */ +function pfSenseHeader($location) { + header("Location: " . $location); +} + +function gentitle_pkg($pgname) { + global $pfSense_config; + return $pfSense_config['system']['hostname'] . "." . $pfSense_config['system']['domain'] . " - " . $pgname; +} + +function domTT_title($title_msg) { + if (!empty($title_msg)) { + $title_msg = preg_replace("/\s+/", " ", $title_msg); + $title_msg = preg_replace("/'/", "\'", $title_msg); + return "onmouseout=\"this.style.color = ''; domTT_mouseout(this, event);\" onmouseover=\"domTT_activate(this, event, 'content', '{$title_msg}', 'trail', true, 'delay', 0, 'fade', 'both', 'fadeMax', 93, 'delay', 300, 'styleClass', 'niceTitle');\""; + } +} + +$xml = htmlspecialchars($_GET['xml']); +if ($_POST['xml']) { + $xml = htmlspecialchars($_POST['xml']); +} + +$xml_fullpath = realpath('/usr/local/pkg/' . $xml); + +if ($xml == "" || $xml_fullpath === false || + substr($xml_fullpath, 0, strlen('/usr/local/pkg/')) != '/usr/local/pkg/') { + print_info_box_np(gettext("ERROR: No valid package defined.")); + die; +} else { + $pkg = parse_xml_config_pkg($xml_fullpath, "packagegui"); +} + +if ($pkg['include_file'] <> "") { + require_once($pkg['include_file']); +} + +if (!isset($pkg['adddeleteeditpagefields'])) { + $only_edit = true; +} else { + $only_edit = false; +} + +$package_name = $pkg['menu'][0]['name']; +$section = $pkg['menu'][0]['section']; +$config_path = $pkg['configpath']; +$name = $pkg['name']; +$title = $pkg['title']; +$pgtitle = $title; + +$id = $_GET['id']; +if (isset($_POST['id'])) { + $id = htmlspecialchars($_POST['id']); +} + +// Not posting? Then user is editing a record. There must be a valid id +// when editing a record. +if (!$id && !$_POST) { + $id = "0"; +} + +if (!is_numeric($id)) { + header("Location: /"); + exit; +} + +if ($pkg['custom_php_global_functions'] <> "") { + eval($pkg['custom_php_global_functions']); +} + +// grab the installedpackages->package_name section. +if ($config['installedpackages'] && !is_array($config['installedpackages'][xml_safe_fieldname($pkg['name'])]['config'])) { + $config['installedpackages'][xml_safe_fieldname($pkg['name'])]['config'] = array(); +} + +// If the first entry in the array is an empty <config/> tag, kill it. +if ($config['installedpackages'] && (count($config['installedpackages'][xml_safe_fieldname($pkg['name'])]['config']) > 0) + && ($config['installedpackages'][xml_safe_fieldname($pkg['name'])]['config'][0] == "")) { + array_shift($config['installedpackages'][xml_safe_fieldname($pkg['name'])]['config']); +} + +$a_pkg = &$config['installedpackages'][xml_safe_fieldname($pkg['name'])]['config']; + +if ($_GET['savemsg'] <> "") { + $savemsg = htmlspecialchars($_GET['savemsg']); +} + +if ($pkg['custom_php_command_before_form'] <> "") { + eval($pkg['custom_php_command_before_form']); +} + +if ($_POST) { + $firstfield = ""; + $rows = 0; + + $input_errors = array(); + $reqfields = array(); + $reqfieldsn = array(); + foreach ($pkg['fields']['field'] as $field) { + if (($field['type'] == 'input') && isset($field['required'])) { + if ($field['fieldname']) { + $reqfields[] = $field['fieldname']; + } + if ($field['fielddescr']) { + $reqfieldsn[] = $field['fielddescr']; + } + } + } + do_input_validation($_POST, $reqfields, $reqfieldsn, $input_errors); + + if ($pkg['custom_php_validation_command']) { + eval($pkg['custom_php_validation_command']); + } + + if ($_POST['act'] == "del") { + if ($pkg['custom_delete_php_command']) { + if ($pkg['custom_php_command_before_form'] <> "") { + eval($pkg['custom_php_command_before_form']); + } + eval($pkg['custom_delete_php_command']); + } + write_config($pkg['delete_string']); + // resync the configuration file code if defined. + if ($pkg['custom_php_resync_config_command'] <> "") { + if ($pkg['custom_php_command_before_form'] <> "") { + eval($pkg['custom_php_command_before_form']); + } + eval($pkg['custom_php_resync_config_command']); + } + } else { + if (!$input_errors && $pkg['custom_add_php_command']) { + if ($pkg['donotsave'] <> "" or $pkg['preoutput'] <> "") { + ?> + +<?php include("head.inc"); ?> +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<?php + } + if ($pkg['preoutput']) { + echo "<pre>"; + } + eval($pkg['custom_add_php_command']); + if ($pkg['preoutput']) { + echo "</pre>"; + } + } + } + + // donotsave is enabled. lets simply exit. + if (empty($pkg['donotsave'])) { + + // store values in xml configuration file. + if (!$input_errors) { + $pkgarr = array(); + foreach ($pkg['fields']['field'] as $fields) { + switch ($fields['type']) { + case "rowhelper": + // save rowhelper items. + #$rowhelpername=($fields['fieldname'] ? $fields['fieldname'] : "row"); + $rowhelpername="row"; + foreach ($fields['rowhelper']['rowhelperfield'] as $rowhelperfield) { + foreach ($_POST as $key => $value) { + if (preg_match("/^{$rowhelperfield['fieldname']}(\d+)$/", $key, $matches)) { + $pkgarr[$rowhelpername][$matches[1]][$rowhelperfield['fieldname']] = $value; + } + } + } + break; + default: + $fieldname = $fields['fieldname']; + if ($fieldname == "interface_array") { + $fieldvalue = $_POST[$fieldname]; + } elseif (is_array($_POST[$fieldname])) { + $fieldvalue = implode(',', $_POST[$fieldname]); + } else { + $fieldvalue = trim($_POST[$fieldname]); + if ($fields['encoding'] == 'base64') { + $fieldvalue = base64_encode($fieldvalue); + } + } + if ($fieldname) { + $pkgarr[$fieldname] = $fieldvalue; + } + } + } + + if (isset($id) && $a_pkg[$id]) { + $a_pkg[$id] = $pkgarr; + } else { + $a_pkg[] = $pkgarr; + } + + write_config($pkg['addedit_string']); + // late running code + if ($pkg['custom_add_php_command_late'] <> "") { + eval($pkg['custom_add_php_command_late']); + } + + if (isset($pkg['filter_rules_needed'])) { + filter_configure(); + } + + // resync the configuration file code if defined. + if ($pkg['custom_php_resync_config_command'] <> "") { + eval($pkg['custom_php_resync_config_command']); + } + + parse_package_templates(); + + /* if start_command is defined, restart w/ this */ + if ($pkg['start_command'] <> "") { + exec($pkg['start_command'] . ">/dev/null 2&>1"); + } + + /* if restart_command is defined, restart w/ this */ + if ($pkg['restart_command'] <> "") { + exec($pkg['restart_command'] . ">/dev/null 2&>1"); + } + + if ($pkg['aftersaveredirect'] <> "") { + pfSenseHeader($pkg['aftersaveredirect']); + } elseif (!$pkg['adddeleteeditpagefields']) { + pfSenseHeader("pkg_edit.php?xml={$xml}&id=0"); + } elseif (!$pkg['preoutput']) { + pfSenseHeader("pkg.php?xml=" . $xml); + } + exit; + } else { + $get_from_post = true; + } + } elseif (!$input_errors) { + exit; + } +} + +if ($pkg['title'] <> "") { + $edit = ($only_edit ? '' : ": " . gettext("Edit")); + $title = $pkg['title'] . $edit; +} else { + $title = gettext("Package Editor"); +} + +$pgtitle = $title; + +if ($pkg['custom_php_after_head_command']) { + $closehead = false; + include("head.inc"); + eval($pkg['custom_php_after_head_command']); + echo "</head>\n"; +} else { + include("head.inc"); +} + +?> + +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> + +<?php include("fbegin.inc"); ?> + +<script type="text/javascript" src="/javascript/autosuggest.js?rev=1"></script> +<script type="text/javascript" src="/javascript/suggestions.js"></script> + +<?php if ($pkg['fields']['field'] <> "") { ?> +<script type="text/javascript"> +//<![CDATA[ + //Everything inside it will load as soon as the DOM is loaded and before the page contents are loaded + jQuery(document).ready(function() { + + //Sortable function + jQuery('#mainarea table tbody').sortable({ + items: 'tr.sortable', + cursor: 'move', + distance: 10, + opacity: 0.8, + helper: function(e, ui) { + ui.children().each(function() { + jQuery(this).width(jQuery(this).width()); + }); + return ui; + }, + }); + + //delete current line jQuery function + jQuery('#maintable td .delete').live('click', function() { + //do not remove first line + if (jQuery("#maintable tr").length > 2) { + jQuery(this).parent().parent().remove(); + return false; + } + }); + + //add new line jQuery function + jQuery('#mainarea table .add').click(function() { + //get table size and assign as new id + var c_id=jQuery("#maintable tr").length; + var new_row=jQuery("table#maintable tr:last").html().replace(/(name|id)="(\w+)(\d+)"/g,"$1='$2"+c_id+"'"); + //apply new id to created line rowhelperid + jQuery("table#maintable tr:last").after("<tr>"+new_row+"<\/tr>"); + return false; + }); + // Call enablechange function + enablechange(); + }); + + function enablechange() { + <?php + foreach ($pkg['fields']['field'] as $field) { + if (isset($field['enablefields']) or isset($field['checkenablefields'])) { + echo "\tif (jQuery('form[name=\"iform\"] input[name=\"{$field['fieldname']}\"]').prop('checked') == false) {\n"; + + if (isset($field['enablefields'])) { + foreach (explode(',', $field['enablefields']) as $enablefield) { + echo "\t\tif (jQuery('form[name=\"iform\"] input[name=\"{$enablefield}\"]').length > 0) {\n"; + echo "\t\t\tjQuery('form[name=\"iform\"] input[name=\"{$enablefield}\"]').prop('disabled',true);\n"; + echo "\t\t}\n"; + } + } + + if (isset($field['checkenablefields'])) { + foreach (explode(',', $field['checkenablefields']) as $checkenablefield) { + echo "\t\tif (jQuery('form[name=\"iform\"] input[name=\"{$checkenablefield}\"]').length > 0) {\n"; + echo "\t\t\tjQuery('form[name=\"iform\"] input[name=\"{$checkenablefield}\"]').prop('checked',true);\n"; + echo "\t\t}\n"; + } + } + + echo "\t}\n\telse {\n"; + + if (isset($field['enablefields'])) { + foreach (explode(',', $field['enablefields']) as $enablefield) { + echo "\t\tif (jQuery('form[name=\"iform\"] input[name=\"{$enablefield}\"]').length > 0) {\n"; + echo "\t\t\tjQuery('form[name=\"iform\"] input[name=\"{$enablefield}\"]').prop('disabled',false);\n"; + echo "\t\t}\n"; + } + } + + if (isset($field['checkenablefields'])) { + foreach (explode(',', $field['checkenablefields']) as $checkenablefield) { + echo "\t\tif (jQuery('form[name=\"iform\"] input[name=\"{$checkenablefield}\"]').length > 0) {\n"; + echo "\t\t\tjQuery('form[name=\"iform\"] input[name=\"{$checkenablefield}\"]').prop('checked',false);\n"; + echo "\t\t}\n"; + } + } + + echo "\t}\n"; + } + } + ?> + } +//]]> +</script> +<?php } ?> +<script type="text/javascript" src="javascript/domTT/domLib.js"></script> +<script type="text/javascript" src="javascript/domTT/domTT.js"></script> +<script type="text/javascript" src="javascript/domTT/behaviour.js"></script> +<script type="text/javascript" src="javascript/domTT/fadomatic.js"></script> +<script type="text/javascript" src="/javascript/row_helper_dynamic.js"></script> + +<?php if (!empty($input_errors)) print_input_errors($input_errors); ?> +<form name="iform" action="pkg_edit.php" method="post"> +<input type="hidden" name="xml" value="<?= htmlspecialchars($xml) ?>" /> +<?php if ($savemsg) print_info_box($savemsg); ?> +<table width="100%" border="0" cellpadding="0" cellspacing="0" summary="package edit"> +<?php +if ($pkg['tabs'] <> "") { + $tab_array = array(); + foreach ($pkg['tabs']['tab'] as $tab) { + if ($tab['tab_level']) { + $tab_level = $tab['tab_level']; + } else { + $tab_level = 1; + } + if (isset($tab['active'])) { + $active = true; + } else { + $active = false; + } + if (isset($tab['no_drop_down'])) { + $no_drop_down = true; + } + $urltmp = ""; + if ($tab['url'] <> "") { + $urltmp = $tab['url']; + } + if ($tab['xml'] <> "") { + $urltmp = "pkg_edit.php?xml=" . $tab['xml']; + } + + $addresswithport = getenv("HTTP_HOST"); + $colonpos = strpos($addresswithport, ":"); + if ($colonpos !== False) { + //my url is actually just the IP address of the pfsense box + $myurl = substr($addresswithport, 0, $colonpos); + } else { + $myurl = $addresswithport; + } + // eval url so that above $myurl item can be processed if need be. + $url = str_replace('$myurl', $myurl, $urltmp); + + $tab_array[$tab_level][] = array( + $tab['text'], + $active, + $url + ); + } + + ksort($tab_array); + foreach ($tab_array as $tabid => $tab) { + echo '<tr><td>'; + display_top_tabs($tab, $no_drop_down, $tabid); + echo '</td></tr>'; + } +} + +?> +<tr><td><div id="mainarea"><table id="t" class="tabcont" width="100%" border="0" cellpadding="6" cellspacing="0" summary="main area"> +<?php + $cols = 0; + $savevalue = gettext("Save"); + if ($pkg['savetext'] <> "") { + $savevalue = $pkg['savetext']; + } + /* If a package's XML has <advanced_options/> configured, then setup + * the table rows for the fields that have <advancedfield/> set. + * These fields will be placed below other fields in a separate area titled 'Advanced Features'. + * These advanced fields are not normally configured and generally left to default to 'default settings'. + */ + + if ($pkg['advanced_options'] == "enabled") { + $adv_filed_count = 0; + $advanced = "<td> </td>"; + $advanced .= "<tr><td colspan=\"2\" class=\"listtopic\">". gettext("Advanced features") . "<br /></td></tr>\n"; + } + foreach ($pkg['fields']['field'] as $pkga) { + if ($pkga['type'] == "sorting") { + continue; + } + + if ($pkga['type'] == "listtopic") { + $input = "<tr id='td_{$pkga['fieldname']}'><td> </td></tr>"; + $input .= "<tr id='tr_{$pkga['fieldname']}'><td colspan=\"2\" class=\"listtopic\">{$pkga['name']}<br /></td></tr>\n"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $advanced .= $input; + $adv_filed_count++; + } else { + echo $input; + } + continue; + } + + if ($pkga['combinefields'] == "begin") { + $input="<tr valign='top' id='tr_{$pkga['fieldname']}'>"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $advanced .= $input; + } else { + echo $input; + } + } + + $size = ""; + $colspan=""; + if (isset($pkga['dontdisplayname'])) { + $input=""; + // If this is in a set of combined fields and; + // - it is a "begin" (case already handled above) or + // - usecolspan2 is in effect (so we want to spread all the combined fields horizontally) + // then we do not want this "tr" to be inserted. + // Thus only insert the "tr" if the not (!) of the above condition. + if (!((isset($pkga['combinefields'])) && (($pkga['combinefields'] == "begin") || (isset($pkga['usecolspan2']))))) { + $input .= "<tr valign='top' id='tr_{$pkga['fieldname']}'>"; + } + if (isset($pkga['usecolspan2'])) { + $colspan="colspan='2'"; + } else { + $input .= "<td width='22%' class='vncell{$req}'> </td>"; + } + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $advanced .= $input; + $adv_filed_count++; + } else { + echo $input; + } + } else if (!isset($pkga['placeonbottom'])) { + unset($req); + if (isset($pkga['required'])) { + $req = 'req'; + } + $input=""; + // If this is in a set of combined fields and; + // - it is a "begin" (case already handled above) or + // - usecolspan2 is in effect (so we want to spread all the combined fields horizontally) + // then we do not want this "tr" to be inserted. + // Thus only insert the "tr" if the not (!) of the above condition. + if (!((isset($pkga['combinefields'])) && (($pkga['combinefields'] == "begin") || (isset($pkga['usecolspan2']))))) { + $input .= "<tr>"; + } + $input .= "<td valign='top' width=\"22%\" class=\"vncell{$req}\">"; + $input .= fixup_string($pkga['fielddescr']); + $input .= "</td>"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $advanced .= $input; + $adv_filed_count++; + } else { + echo $input; + } + } + if ($pkga['combinefields'] == "begin") { + $input="<td class=\"vncell\"><table summary=\"advanced\"><tr>"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $advanced .= $input; + } else { + echo $input; + } + } + + $class=(isset($pkga['combinefields']) ? '' : 'class="vtable"'); + if (!isset($pkga['placeonbottom'])) { + $input="<td valign='top' {$colspan} {$class}>"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $advanced .= $input; + $adv_filed_count++; + } else { + echo $input; + } + } + + // if user is editing a record, load in the data. + $fieldname = $pkga['fieldname']; + if ($get_from_post) { + $value = $_POST[$fieldname]; + if (is_array($value)) { + $value = implode(',', $value); + } + } else { + if (isset($id) && $a_pkg[$id]) { + $value = $a_pkg[$id][$fieldname]; + } else { + $value = $pkga['default_value']; + } + } + switch ($pkga['type']) { + case "input": + $size = ($pkga['size'] ? " size='{$pkga['size']}' " : ""); + $input = "<input {$size} id='{$pkga['fieldname']}' name='{$pkga['fieldname']}' class='formfld unknown' value=\"" . htmlspecialchars($value) ."\" />\n"; + $input .= "<br />" . fixup_string($pkga['description']) . "\n"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $js_array[] = $pkga['fieldname']; + $advanced .= display_advanced_field($pkga['fieldname']).$input ."</div>\n"; + } else { + echo $input; + } + break; + + case "password": + $size = ($pkga['size'] ? " size='{$pkga['size']}' " : ""); + $input = "<input " . $size . " id='" . $pkga['fieldname'] . "' type='password' name='" . $pkga['fieldname'] . "' class='formfld pwd' value=\"" . htmlspecialchars($value) . "\" />\n"; + $input .= "<br />" . fixup_string($pkga['description']) . "\n"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $js_array[] = $pkga['fieldname']; + $advanced .= display_advanced_field($pkga['fieldname']).$input ."</div>\n"; + } else { + echo $input; + } + break; + + case "info": + $input = fixup_string($pkga['description']) . "\n"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $js_array[] = $pkga['fieldname']; + $advanced .= display_advanced_field($pkga['fieldname']).$input ."</div>\n"; + } else { + echo $input; + } + break; + + case "select": + $fieldname = $pkga['fieldname']; + if (isset($pkga['multiple'])) { + $multiple = 'multiple="multiple"'; + $items = explode(',', $value); + $fieldname .= "[]"; + } else { + $multiple = ''; + $items = array($value); + } + $size = ($pkga['size'] ? " size='{$pkga['size']}' " : ""); + $onchange = (isset($pkga['onchange']) ? "onchange=\"{$pkga['onchange']}\"" : ''); + $input = "<select id='" . $pkga['fieldname'] . "' $multiple $size $onchange name=\"$fieldname\">\n"; + foreach ($pkga['options']['option'] as $opt) { + $selected = (in_array($opt['value'], $items) ? 'selected="selected"' : ''); + $input .= "\t<option value=\"{$opt['value']}\" {$selected}>{$opt['name']}</option>\n"; + } + $input .= "</select>\n<br />\n" . fixup_string($pkga['description']) . "\n"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $js_array[] = $pkga['fieldname']; + $advanced .= display_advanced_field($pkga['fieldname']).$input; + $advanced .= "</div>\n"; + } else { + echo $input; + } + break; + + case "select_source": + $fieldname = $pkga['fieldname']; + if (isset($pkga['multiple'])) { + $multiple = 'multiple="multiple"'; + $items = explode(',', $value); + $fieldname .= "[]"; + } else { + $multiple = ''; + $items = array($value); + } + $size = (isset($pkga['size']) ? "size=\"{$pkga['size']}\"" : ''); + $onchange = (isset($pkga['onchange']) ? "onchange=\"{$pkga['onchange']}\"" : ''); + $input = "<select id='{$pkga['fieldname']}' {$multiple} {$size} {$onchange} name=\"{$fieldname}\">\n"; + + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $js_array[] = $pkga['fieldname']; + $advanced .= display_advanced_field($pkga['fieldname']) .$input; + $advanced .= "</div>\n"; + } else { + echo $input; + } + $source_url = $pkga['source']; + eval("\$pkg_source_txt = &$source_url;"); + $input=""; + #check if show disable option is present on xml + if (isset($pkga['show_disable_value'])) { + array_push($pkg_source_txt, + array(($pkga['source_name']? $pkga['source_name'] : $pkga['name'])=> $pkga['show_disable_value'], ($pkga['source_value']? $pkga['source_value'] : $pkga['value'])=> $pkga['show_disable_value'])); + } + foreach ($pkg_source_txt as $opt) { + $source_name =($pkga['source_name']? $opt[$pkga['source_name']] : $opt[$pkga['name']]); + $source_value =($pkga['source_value'] ? $opt[$pkga['source_value']] : $opt[$pkga['value']]); + $selected = (in_array($source_value, $items)? 'selected="selected"' : ''); + $input .= "\t<option value=\"{$source_value}\" $selected>{$source_name}</option>\n"; + } + $input .= "</select>\n<br />\n" . fixup_string($pkga['description']) . "\n"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $advanced .= $input; + } else { + echo $input; + } + break; + + case "vpn_selection" : + $input = "<select id='{$pkga['fieldname']}' name='{$vpn['name']}'>\n"; + foreach ($config['ipsec']['phase1'] as $vpn) { + $input .= "\t<option value=\"{$vpn['descr']}\">{$vpn['descr']}</option>\n"; + } + $input .= "</select>\n"; + $input .= "<br />" . fixup_string($pkga['description']) . "\n"; + + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $js_array[] = $pkga['fieldname']; + $advanced .= display_advanced_field($pkga['fieldname']).$input; + $advanced .= "</div>\n"; + } else { + echo $input; + } + break; + + case "checkbox": + $checkboxchecked =($value == "on" ? " checked=\"checked\"" : ""); + $onchange = (isset($pkga['onchange']) ? "onchange=\"{$pkga['onchange']}\"" : ''); + if (isset($pkga['enablefields']) || isset($pkga['checkenablefields'])) { + $onclick = ' onclick="javascript:enablechange();"'; + } + $input = "<input id='{$pkga['fieldname']}' type='checkbox' name='{$pkga['fieldname']}' {$checkboxchecked} {$onclick} {$onchange} />\n"; + $input .= "<br />" . fixup_string($pkga['description']) . "\n"; + + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $js_array[] = $pkga['fieldname']; + $advanced .= display_advanced_field($pkga['fieldname']).$input; + $advanced .= "</div>\n"; + } else { + echo $input; + } + break; + + case "textarea": + if ($pkga['rows']) { + $rows = " rows='{$pkga['rows']}' "; + } + if ($pkga['cols']) { + $cols = " cols='{$pkga['cols']}' "; + } + if (($pkga['encoding'] == 'base64') && !$get_from_post && !empty($value)) { + $value = base64_decode($value); + } + $wrap =($pkga['wrap'] == "off" ? 'wrap="off" style="white-space:nowrap;"' : ''); + $input = "<textarea {$rows} {$cols} name='{$pkga['fieldname']}'{$wrap}>{$value}</textarea>\n"; + $input .= "<br />" . fixup_string($pkga['description']) . "\n"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $js_array[] = $pkga['fieldname']; + $advanced .= display_advanced_field($pkga['fieldname']).$input; + $advanced .= "</div>\n"; + } else { + echo $input; + } + break; + + case "aliases": + // Use xml tag <typealiases> to filter type aliases + $size = ($pkga['size'] ? "size=\"{$pkga['size']}\"" : ''); + $fieldname = $pkga['fieldname']; + $a_aliases = &$config['aliases']['alias']; + $addrisfirst = 0; + $aliasesaddr = ""; + $value = "value='{$value}'"; + + if (isset($a_aliases)) { + if (!empty($pkga['typealiases'])) { + foreach ($a_aliases as $alias) { + if ($alias['type'] == $pkga['typealiases']) { + if ($addrisfirst == 1) { + $aliasesaddr .= ","; + } + $aliasesaddr .= "'" . $alias['name'] . "'"; + $addrisfirst = 1; + } + } + } else { + foreach ($a_aliases as $alias) { + if ($addrisfirst == 1) { + $aliasesaddr .= ","; + } + $aliasesaddr .= "'" . $alias['name'] . "'"; + $addrisfirst = 1; + } + } + } + + $input = "<input name='{$fieldname}' type='text' class='formfldalias' id='{$fieldname}' {$size} {$value} />\n<br />"; + $input .= fixup_string($pkga['description']) . "\n"; + + $script = "<script type='text/javascript'>\n"; + $script .= "//<![CDATA[\n"; + $script .= "var aliasarray = new Array({$aliasesaddr})\n"; + $script .= "var oTextbox1 = new AutoSuggestControl(document.getElementById('{$fieldname}'), new StateSuggestions(aliasarray))\n"; + $script .= "//]]>\n"; + $script .= "</script>"; + + echo $input; + echo $script; + break; + + case "interfaces_selection": + $ips = array(); + $interface_regex=(isset($pkga['hideinterfaceregex']) ? $pkga['hideinterfaceregex'] : "nointerfacestohide"); + if (is_array($config['interfaces'])) { + foreach ($config['interfaces'] as $iface_key=>$iface_value) { + if (isset($iface_value['enable']) && !preg_match("/$interface_regex/", $iface_key)) { + $iface_description=($iface_value['descr'] !="" ? strtoupper($iface_value['descr']) : strtoupper($iface_key)); + if (isset($pkga['showips'])) { + $iface_description .= " address"; + } + $ips[] = array('ip'=> $iface_key, 'description'=> $iface_description); + } + } + } + if (is_array($config['virtualip']) && isset($pkga['showvirtualips'])) { + foreach ($config['virtualip']['vip'] as $vip) { + if (!preg_match("/$interface_regex/", $vip['interface'])) { + $vip_description=($vip['descr'] !="" ? " ({$vip['descr']}) " : " "); + } + switch ($vip['mode']) { + case "ipalias": + case "carp": + $ips[] = array('ip' => $vip['subnet'], 'description' => "{$vip['subnet']} $vip_description"); + break; + case "proxyarp": + if ($vip['type'] == "network") { + $start = ip2long32(gen_subnet($vip['subnet'], $vip['subnet_bits'])); + $end = ip2long32(gen_subnet_max($vip['subnet'], $vip['subnet_bits'])); + $len = $end - $start; + for ($i = 0; $i <= $len; $i++) { + $ips[]= array('ip' => long2ip32($start+$i), 'description' => long2ip32($start+$i)." from {$vip['subnet']}/{$vip['subnet_bits']} {$vip_description}"); + } + } else { + $ips[]= array('ip' => $vip['subnet'], 'description' => "{$vip['subnet']} $vip_description"); + } + break; + } + } + } + sort($ips); + if (isset($pkga['showlistenall'])) { + array_unshift($ips, array('ip' => 'All', 'description' => 'Listen on All interfaces/ip addresses ')); + } + if (!preg_match("/$interface_regex/", "loopback")) { + $iface_description=(isset($pkga['showips']) ? "127.0.0.1 (loopback)" : "loopback"); + array_push($ips, array('ip' => 'lo0', 'description' => $iface_description)); + } + + #show interfaces array on gui + $size = ($pkga['size'] ? "size=\"{$pkga['size']}\"" : ''); + $multiple = ''; + $fieldname = $pkga['fieldname']; + if (isset($pkga['multiple'])) { + $fieldname .= '[]'; + $multiple = 'multiple="multiple"'; + } + $input = "<select id='{$pkga['fieldname']}' name=\"{$fieldname}\" {$size} {$multiple}>\n"; + if (is_array($value)) { + $values = $value; + } else { + $values = explode(',', $value); + } + foreach ($ips as $iface) { + $selected = (in_array($iface['ip'], $values) ? 'selected="selected"' : ''); + $input .= "<option value=\"{$iface['ip']}\" {$selected}>{$iface['description']}</option>\n"; + } + $input .= "</select>\n<br />" . fixup_string($pkga['description']) . "\n"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $advanced .= $input; + } else { + echo $input; + } + break; + + case "radio": + $input = "<input type='radio' id='{$pkga['fieldname']}' name='{$pkga['fieldname']}' value='{$value}' />"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $advanced .= $input; + } else { + echo $input; + } + break; + + case "button": + $input = "<input type='submit' id='{$pkga['fieldname']}' name='{$pkga['fieldname']}' class='formbtn' value='{$pkga['fieldname']}' />\n"; + if (isset($pkga['placeonbottom'])) { + $pkg_buttons .= $input; + } else { + echo $input ."\n<br />" . fixup_string($pkga['description']) . "\n"; + } + break; + + case "schedule_selection": + $input = "<select id='{$pkga['fieldname']}' name='{$pkga['fieldname']}'>\n"; + $schedules = array(); + $schedules[] = "none"; + if (is_array($config['schedules']['schedule'])) { + foreach ($config['schedules']['schedule'] as $schedule) { + if ($schedule['name'] <> "") { + $schedules[] = $schedule['name']; + } + } + } + foreach ($schedules as $schedule) { + $selected = ($value == $schedule ? "selected=\"selected\"" : ""); + if ($schedule == "none") { + $input .= "<option value=\"\" {$selected}>{$schedule}</option>\n"; + } else { + $input .= "<option value=\"{$schedule}\" {$selected}>{$schedule}</option>\n"; + } + } + $input .= "</select>\n<br />\n" . fixup_string($pkga['description']) . "\n"; + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $js_array[] = $pkga['fieldname']; + $advanced .= display_advanced_field($pkga['fieldname']).$input; + $advanced .= "</div>\n"; + } else { + echo $input; + } + break; + + case "rowhelper": + #$rowhelpername=($fields['fieldname'] ? $fields['fieldname'] : "row"); + $rowhelpername="row"; + ?> + <script type="text/javascript"> + //<![CDATA[ + <?php + $rowcounter = 0; + $fieldcounter = 0; + foreach ($pkga['rowhelper']['rowhelperfield'] as $rowhelper) { + echo "rowname[{$fieldcounter}] = \"{$rowhelper['fieldname']}\";\n"; + echo "rowtype[{$fieldcounter}] = \"{$rowhelper['type']}\";\n"; + echo "rowsize[{$fieldcounter}] = \"{$rowhelper['size']}\";\n"; + $fieldcounter++; + } + ?> + //]]> + </script> + <table id="maintable" summary="main table"> + <tr id='<?="tr_{$pkga['fieldname']}";?>'> + <?php + foreach ($pkga['rowhelper']['rowhelperfield'] as $rowhelper) { + echo "<td ".domTT_title($rowhelper['description'])."><b>" . fixup_string($rowhelper['fielddescr']) . "</b></td>\n"; + } + + $rowcounter = 0; + $trc = 0; + + //Use assigned $a_pkg or create an empty array to enter loop + if (isset($a_pkg[$id][$rowhelpername])) { + $saved_rows=$a_pkg[$id][$rowhelpername]; + } else { + $saved_rows[] = array(); + } + + foreach ($saved_rows as $row) { + echo "</tr>\n<tr class=\"sortable\" id=\"id_{$rowcounter}\">\n"; + foreach ($pkga['rowhelper']['rowhelperfield'] as $rowhelper) { + unset($value); + if ($rowhelper['value'] <> "") { + $value = $rowhelper['value']; + } + $fieldname = $rowhelper['fieldname']; + // if user is editing a record, load in the data. + if (isset($id) && $a_pkg[$id]) { + $value = $row[$fieldname]; + } + $options = ""; + $type = $rowhelper['type']; + $description = $rowhelper['description']; + $fieldname = $rowhelper['fieldname']; + if ($type == "option") { + $options = &$rowhelper['options']['option']; + } + if ($rowhelper['size']) { + $size = $rowhelper['size']; + } else if ($pkga['size']) { + $size = $pkga['size']; + } else { + $size = "8"; + } + display_row($rowcounter, $value, $fieldname, $type, $rowhelper, $size); + + $text = ""; + $trc++; + } + $rowcounter++; + echo "<td>"; + #echo "<a onclick=\"removeRow(this); return false;\" href=\"#\"><img border=\"0\" src=\"./themes/".$g['theme']."/images/icons/icon_x.gif\" alt=\"remove\" /></a>"; + echo "<a class='delete' href=\"#\"><img border='0' src='./themes/{$g['theme']}/images/icons/icon_x.gif' alt='delete' /></a>"; + echo "</td>\n"; + } + ?> + </tr> + <tbody></tbody> + </table> + + <!-- <br /><a onclick="javascript:addRowTo('maintable'); return false;" href="#"><img border="0" src="./themes/<?#= $g['theme']; ?>/images/icons/icon_plus.gif" alt="add" /></a>--> + <br /><a class="add" href="#"><img border="0" src="./themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" alt="add" /></a> + <br /><?php if ($pkga['description'] != "") echo $pkga['description']; ?> + <script type="text/javascript"> + //<![CDATA[ + field_counter_js = <?= $fieldcounter ?>; + rows = <?= $rowcounter ?>; + totalrows = <?php echo $rowcounter; ?>; + loaded = <?php echo $rowcounter; ?>; + //typesel_change(); + //]]> + </script> + + <?php + break; + } + #check typehint value + if ($pkga['typehint']) { + echo " " . $pkga['typehint']; + } + #check combinefields options + if (isset($pkga['combinefields'])) { + // At the end of each combined-fields field we always want to end a td tag. + $input = "</td>"; + // The tr tag end is used to end the whole set of combined fields, + // but also if usecolspan2 is not in effect then we also put each combined field in its own tr. + if (($pkga['combinefields'] == "end") || (!isset($pkga['usecolspan2']))) { + $input .= "</tr>"; + } + // At the end of the combined fields we finish up the table that encloses the combined fields... + if ($pkga['combinefields'] == "end") { + $input .= "</table></td></tr>"; + } + } else { + $input = "</td></tr>"; + if ($pkga['usecolspan2']) { + $input .= "</tr><br />"; + } + } + if (isset($pkga['advancedfield']) && isset($adv_filed_count)) { + $advanced .= "{$input}\n"; + } else { + echo "{$input}\n"; + } + #increment counter + $i++; + } + + #print advanced settings if any after reading all fields + if (isset($advanced) && $adv_filed_count > 0) { + echo $advanced; + } + + ?> + <tr> + <td> </td> + </tr> + <tr> + <td width="22%" valign="top"> </td> + <td width="78%"> + <div id="buttons"> + <?php + if ($pkg['note'] != "") { + echo "<p><span class=\"red\"><strong>" . gettext("Note") . ":</strong></span> {$pkg['note']}</p>"; + } + //if (isset($id) && $a_pkg[$id]) // We'll always have a valid ID in our hands + echo "<input name='id' type='hidden' value=\"" . htmlspecialchars($id) . "\" />"; + echo "<input name='Submit' type='submit' class='formbtn' value=\"" . htmlspecialchars($savevalue) . "\" />\n{$pkg_buttons}\n"; + if (!$only_edit) { + echo "<input class=\"formbtn\" type=\"button\" value=\"".gettext("Cancel")."\" onclick=\"window.location.href='" . $_SERVER['HTTP_REFERER'] . "'\" />"; + } + ?> + </div> + </td> + </tr> + +</table> +</div></td></tr> +</table> +</form> + +<?php if ($pkg['custom_php_after_form_command']) eval($pkg['custom_php_after_form_command']); ?> + +<?php + /* JavaScript to handle the advanced fields. */ + if ($pkg['advanced_options'] == "enabled") { + echo "<script type=\"text/javascript\">\n"; + echo "//<![CDATA[\n"; + foreach ($js_array as $advfieldname) { + echo "function show_" . $advfieldname . "() {\n"; + echo "\tjQuery('#showadv_{$advfieldname}').empty();\n"; + echo "\tjQuery('#show_{$advfieldname}').css('display', 'block');\n"; + echo "}\n\n"; + } + echo "//]]>\n"; + echo "</script>\n"; + } +?> + +<?php include("fend.inc"); ?> +</body> +</html> + +<?php +/* + * ROW Helpers function + */ +function display_row($trc, $value, $fieldname, $type, $rowhelper, $size) { + global $text, $config; + echo "<td>\n"; + switch ($type) { + case "input": + echo "<input size='{$size}' name='{$fieldname}{$trc}' id='{$fieldname}{$trc}' class='formfld unknown' value=\"" . htmlspecialchars($value) . "\" />\n"; + break; + case "checkbox": + echo "<input size='{$size}' type='checkbox' id='{$fieldname}{$trc}' name='{$fieldname}{$trc}' value='ON' ".($value?"CHECKED":"")." />\n"; + break; + case "password": + echo "<input size='{$size}' type='password' id='{$fieldname}{$trc}' name='{$fieldname}{$trc}' class='formfld pwd' value=\"" . htmlspecialchars($value) . "\" />\n"; + break; + case "textarea": + echo "<textarea rows='2' cols='12' id='{$fieldname}{$trc}' class='formfld unknown' name='{$fieldname}{$trc}'>{$value}</textarea>\n"; + case "select": + echo "<select style='height:22px;' id='{$fieldname}{$trc}' name='{$fieldname}{$trc}' {$title}>\n"; + foreach ($rowhelper['options']['option'] as $rowopt) { + $text .= "<option value='{$rowopt['value']}'>{$rowopt['name']}</option>"; + echo "<option value='{$rowopt['value']}'".($rowopt['value'] == $value?" selected=\"selected\"":"").">{$rowopt['name']}</option>\n"; + } + echo "</select>\n"; + break; + case "interfaces_selection": + $size = ($size ? "size=\"{$size}\"" : ''); + $multiple = ''; + if (isset($rowhelper['multiple'])) { + $fieldname .= '[]'; + $multiple = "multiple=\"multiple\""; + } + echo "<select style='height:22px;' id='{$fieldname}{$trc}' name='{$fieldname}{$trc}' {$size} {$multiple}>\n"; + $ifaces = get_configured_interface_with_descr(); + $additional_ifaces = $rowhelper['add_to_interfaces_selection']; + if (!empty($additional_ifaces)) { + $ifaces = array_merge($ifaces, explode(',', $additional_ifaces)); + } + if (is_array($value)) { + $values = $value; + } else { + $values = explode(',', $value); + } + $ifaces["lo0"] = "loopback"; + echo "<option><name></name><value></value></option>/n"; + foreach ($ifaces as $ifname => $iface) { + $text .="<option value=\"{$ifname}\">$iface</option>"; + echo "<option value=\"{$ifname}\" ".(in_array($ifname, $values) ? 'selected="selected"' : '').">{$iface}</option>\n"; + } + echo "</select>\n"; + break; + case "select_source": + echo "<select style='height:22px;' id='{$fieldname}{$trc}' name='{$fieldname}{$trc}'>\n"; + if (isset($rowhelper['show_disable_value'])) { + echo "<option value='{$rowhelper['show_disable_value']}'>{$rowhelper['show_disable_value']}</option>\n"; + } + $source_url = $rowhelper['source']; + eval("\$pkg_source_txt = &$source_url;"); + foreach ($pkg_source_txt as $opt) { + $source_name = ($rowhelper['source_name'] ? $opt[$rowhelper['source_name']] : $opt[$rowhelper['name']]); + $source_value = ($rowhelper['source_value'] ? $opt[$rowhelper['source_value']] : $opt[$rowhelper['value']]); + $text .= "<option value='{$source_value}'>{$source_name}</option>"; + echo "<option value='{$source_value}'".($source_value == $value?" selected=\"selected\"":"").">{$source_name}</option>\n"; + } + echo "</select>\n"; + break; + } + echo "</td>\n"; +} + +function fixup_string($string) { + global $config; + // fixup #1: $myurl -> http[s]://ip_address:port/ + $https = ""; + $port = $config['system']['webguiport']; + if ($port <> "443" and $port <> "80") { + $urlport = ":" . $port; + } else { + $urlport = ""; + } + + if ($config['system']['webgui']['protocol'] == "https") { + $https = "s"; + } + $myurl = "http" . $https . "://" . getenv("HTTP_HOST") . $urlport; + $newstring = str_replace("\$myurl", $myurl, $string); + $string = $newstring; + // fixup #2: $wanip + $curwanip = get_interface_ip(); + $newstring = str_replace("\$wanip", $curwanip, $string); + $string = $newstring; + // fixup #3: $lanip + $lancfg = $config['interfaces']['lan']; + $lanip = $lancfg['ipaddr']; + $newstring = str_replace("\$lanip", $lanip, $string); + $string = $newstring; + // fixup #4: fix'r'up here. + return $newstring; +} + +/* + * Parse templates if they are defined + */ +function parse_package_templates() { + global $pkg, $config; + $rows = 0; + if ($pkg['templates']['template'] <> "") { + foreach ($pkg['templates']['template'] as $pkg_template_row) { + $filename = $pkg_template_row['filename']; + $template_text = $pkg_template_row['templatecontents']; + $firstfield = ""; + /* calculate total row helpers count and */ + /* change fields defined as fieldname_fieldvalue to their value */ + foreach ($pkg['fields']['field'] as $fields) { + switch ($fields['type']) { + case "rowhelper": + // save rowhelper items. + $row_helper_total_rows = 0; + $row_helper_data = ""; + foreach ($fields['rowhelper']['rowhelperfield'] as $rowhelperfield) { + foreach ($_POST as $key => $value) { + if (preg_match("/^{$rowhelperfield['fieldname']}(\d+)$/", $key, $matches)) { + $row_helper_total_rows++; + $row_helper_data .= $value; + $sep = ""; + ereg($rowhelperfield['fieldname'] . "_fieldvalue\[(.*)\]", $template_text, $sep); + foreach ($sep as $se) { + $separator = $se; + } + if ($separator <> "") { + $row_helper_data = ereg_replace(" ", $separator, $row_helper_data); + $template_text = ereg_replace("\[{$separator}\]", "", $template_text); + } + $template_text = str_replace($rowhelperfield['fieldname'] . "_fieldvalue", $row_helper_data, $template_text); + } + } + } + break; + default: + $fieldname = $fields['fieldname']; + $fieldvalue = $_POST[$fieldname]; + $template_text = str_replace($fieldname . "_fieldvalue", $fieldvalue, $template_text); + } + } + /* replace $domain_total_rows with total rows */ + $template_text = str_replace("$domain_total_rows", $row_helper_total_rows, $template_text); + + /* replace cr's */ + $template_text = str_replace("\\n", "\n", $template_text); + + /* write out new template file */ + $fout = fopen($filename, "w"); + fwrite($fout, $template_text); + fclose($fout); + } + } +} + +/* Return html div fields */ +function display_advanced_field($fieldname) { + $div = "<div id='showadv_{$fieldname}'>\n"; + $div .= "<input type='button' onclick='show_{$fieldname}()' value='" . gettext("Advanced") . "' /> - " . gettext("Show advanced option") ."</div>\n"; + $div .= "<div id='show_{$fieldname}' style='display:none'>\n"; + return $div; +} + +?> diff --git a/src/usr/local/www/pkg_mgr.php b/src/usr/local/www/pkg_mgr.php new file mode 100644 index 0000000..aa0ee0f --- /dev/null +++ b/src/usr/local/www/pkg_mgr.php @@ -0,0 +1,246 @@ +<?php +/* $Id$ */ +/* + pkg_mgr.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2004-2012 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2013 Marcello Coutinho + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_MODULE: pkgs +*/ + +##|+PRIV +##|*IDENT=page-system-packagemanager +##|*NAME=System: Package Manager page +##|*DESCR=Allow access to the 'System: Package Manager' page. +##|*MATCH=pkg_mgr.php* +##|-PRIV + +ini_set('max_execution_time', '0'); + +require_once("globals.inc"); +require_once("guiconfig.inc"); +require_once("pkg-utils.inc"); + +$timezone = $config['system']['timezone']; +if (!$timezone) + $timezone = "Etc/UTC"; + +date_default_timezone_set($timezone); + +/* if upgrade in progress, alert user */ +if(is_subsystem_dirty('packagelock')) { + $pgtitle = array(gettext("System"),gettext("Package Manager")); + include("head.inc"); + print_info_box_np("Please wait while packages are reinstalled in the background."); + include("foot.inc"); + exit; +} + +//get_pkg_info only if cache file has more then $g[min_pkg_cache_file_time] seconds +$pkg_cache_file_time=($g['min_pkg_cache_file_time'] ? $g['min_pkg_cache_file_time'] : 120); + +$xmlrpc_base_url = get_active_xml_rpc_base_url(); +if (!file_exists("{$g['tmp_path']}/pkg_info.cache") || (time() - filemtime("{$g['tmp_path']}/pkg_info.cache")) > $pkg_cache_file_time) { + $pkg_info = get_pkg_info('all', array("noembedded", "name", "category", "website", "version", "status", "descr", "maintainer", "required_version", "maximum_version", "pkginfolink", "config_file")); + //create cache file after get_pkg_info + if($pkg_info) { + $fout = fopen("{$g['tmp_path']}/pkg_info.cache", "w"); + fwrite($fout, serialize($pkg_info)); + fclose($fout); + //$pkg_sizes = get_pkg_sizes(); + } else { + $using_cache = true; + if(file_exists("{$g['tmp_path']}/pkg_info.cache")) { + $savemsg = sprintf(gettext("Unable to retrieve package info from %s. Cached data will be used."), $xmlrpc_base_url); + $pkg_info = unserialize(@file_get_contents("{$g['tmp_path']}/pkg_info.cache")); + } else { + $savemsg = sprintf(gettext('Unable to communicate with %1$s. Please verify DNS and interface configuration, and that %2$s has functional Internet connectivity.'), $xmlrpc_base_url, $g['product_name']); + } + } +} else { + $pkg_info = unserialize(@file_get_contents("{$g['tmp_path']}/pkg_info.cache")); +} + +if (! empty($_GET)) + if (isset($_GET['ver'])) + $requested_version = htmlspecialchars($_GET['ver']); + +$pgtitle = array(gettext("System"),gettext("Package Manager")); +include("head.inc"); + +/* Print package server mismatch warning. See https://redmine.pfsense.org/issues/484 */ +if (!verify_all_package_servers()) + print_info_box(package_server_mismatch_message()); + +/* Print package server SSL warning. See https://redmine.pfsense.org/issues/484 */ +if (check_package_server_ssl() === false) + print_info_box(package_server_ssl_failure_message()); + +if ($savemsg) + print_info_box($savemsg); + +$version = rtrim(file_get_contents("/etc/version")); + +$tab_array = array(); +$tab_array[] = array(gettext("Available Packages"), $requested_version <> "" ? false : true, "pkg_mgr.php"); +$tab_array[] = array(gettext("Installed Packages"), false, "pkg_mgr_installed.php"); +display_top_tabs($tab_array); + +$version = rtrim(file_get_contents("/etc/version")); +if($pkg_info) { + $pkg_keys = array_keys($pkg_info); + natcasesort($pkg_keys); + + //Check categories + $categories=array(); + if(is_array($pkg_keys)) { + foreach($pkg_keys as $key) { + if (!package_skip_tests($pkg_info[$key],$requested_version)) + $categories[$pkg_info[$key]['category']]++; + } + } + ksort($categories); + $cm_count=0; + $tab_array = array(); + $visible_categories=array(); + $categories_min_count=($g['pkg_categories_min_count'] ? $g['pkg_categories_min_count'] : 3); + $categories_max_display=($g['pkg_categories_max_display'] ? $g['pkg_categories_max_display'] : 6); + + /* check selected category or define default category to show */ + if (isset($_REQUEST['category'])) + $menu_category = $_REQUEST['category']; + else if (isset($g['pkg_default_category'])) + $menu_category = $g['pkg_default_category']; + else + $menu_category = "All"; + + $menu_category = (isset($_REQUEST['category']) ? $_REQUEST['category'] : "All"); + $show_category = ($menu_category == "Other" || $menu_category == "All"); + + $tab_array[] = array(gettext("All"), $menu_category=="All" ? true : false, "pkg_mgr.php?category=All"); + foreach ($categories as $category => $c_count) { + if ($c_count >= $categories_min_count && $cm_count <= $categories_max_display) { + $tab_array[] = array(gettext($category) , $menu_category==$category ? true : false, "pkg_mgr.php?category={$category}"); + $visible_categories[]=$category; + $cm_count++; + } + } + $tab_array[] = array(gettext("Other Categories"), $menu_category=="Other" ? true : false, "pkg_mgr.php?category=Other"); + if (count($categories) > 1) + display_top_tabs($tab_array); +} + +if(!$pkg_info || !is_array($pkg_keys)):?> + <div class="alert alert-warning"> + <?=gettext("There are currently no packages available for installation.")?> + </div> +<?php else: ?> + <div class="table-responsive"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th><?=gettext("Name")?></th> +<?php if ($show_category):?> + <th><?=gettext("Category")?></th> +<?php endif;?> + <th><?=gettext("Status")?></th> +<?php if (!$g['disablepackagehistory']):?> + <th><?=gettext("Version")?></th> +<?php endif;?> + <th><?=gettext("Platform")?></th> + <th><?=gettext("Description")?></th> + </tr> + </thead> + <tbody> +<?php + foreach($pkg_keys as $key): + $index = &$pkg_info[$key]; + if(get_pkg_id($index['name']) >= 0 ) + continue; + + if (package_skip_tests($index,$requested_version)) + continue; + + /* get history/changelog git dir */ + $commit_dir=explode("/",$index['config_file']); + $changeloglink = "https://github.com/pfsense/pfsense-packages/commits/master/config/"; + if ($commit_dir[(count($commit_dir)-2)] == "config") + $changeloglink .= $commit_dir[(count($commit_dir)-1)]; + else + $changeloglink .= $commit_dir[(count($commit_dir)-2)]; + + if ($menu_category != "All" && $index['category'] != $menu_category && !($menu_category == "Other" && !in_array($index['category'], $visible_categories))) + continue; +?> + <tr> + <td> +<?php if ($index['website']):?> + <a title="<?=gettext("Visit official website")?>" target="_blank" href="<?=htmlspecialchars($index['website'])?>"> +<?php endif; ?> + <?=htmlspecialchars($index['name'])?> + </a> + </td> +<?php if ($show_category):?> + <td> + <?=gettext($index['category'])?> + </td> +<?php endif;?> + <td> + <?=ucfirst(strtolower($index['status']))?> + </td> +<?php if (!$g['disablepackagehistory']):?> + <td> + <a target="_blank" title="<?=gettext("View changelog")?>" href="<?=htmlspecialchars($changeloglink)?>"> + <?=htmlspecialchars($index['version'])?> + </a> + </td> +<?php endif;?> + <td> + <?=$index['required_version']?> + <?php if ($index['maximum_version']):?> + (< $index['maximum_version']) + <?php endif;?> + </td> + <td> + <?=$index['descr']?> + </td> + <td> + <a title="<?=gettext("Click to install")?>" href="pkg_mgr_install.php?id=<?=$index['name']?>" class="btn btn-success">install</a> +<?php if(!$g['disablepackageinfo'] && $index['pkginfolink'] && $index['pkginfolink'] != $index['website']):?> + <a target="_blank" title="<?=gettext("View more inforation")?>" href="<?=htmlspecialchars($index['pkginfolink'])?>" class="btn btn-default">info</a> +<?php endif;?> + </td> + </tr> +<?php + endforeach; +endif;?> + </tbody> + </table> + </div> +<?php include("foot.inc")?>
\ No newline at end of file diff --git a/src/usr/local/www/pkg_mgr_install.php b/src/usr/local/www/pkg_mgr_install.php new file mode 100644 index 0000000..afad62f --- /dev/null +++ b/src/usr/local/www/pkg_mgr_install.php @@ -0,0 +1,278 @@ +<?php +/* $Id$ */ +/* + pkg_mgr_install.php + part of pfSense (https://www.pfsense.org) + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2004-2010 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2005 Colin Smith + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /bin/rm + pfSense_MODULE: pkgs +*/ + +##|+PRIV +##|*IDENT=page-system-packagemanager-installpackage +##|*NAME=System: Package Manager: Install Package page +##|*DESCR=Allow access to the 'System: Package Manager: Install Package' page. +##|*MATCH=pkg_mgr_install.php* +##|-PRIV + +ini_set('max_execution_time', '0'); + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("pkg-utils.inc"); + +global $static_output; + +$static_output = ""; +$static_status = ""; +$sendto = "output"; + +if ($_POST) { + if (empty($_POST['id']) && $_POST['mode'] != 'reinstallall') { + header("Location: pkg_mgr_installed.php"); + return; + } +} else if ($_GET) { + switch ($_GET['mode']) { + case 'reinstallall': + case 'showlog': + break; + case 'installedinfo': + case 'reinstallxml': + case 'reinstallpkg': + case 'delete': + if (empty($_GET['pkg'])) { + header("Location: pkg_mgr_installed.php"); + return; + } + break; + default: + if (empty($_GET['id'])) { + header("Location: pkg_mgr_installed.php"); + return; + } + break; + } +} + +$pgtitle = array(gettext("System"),gettext("Package Manager"),gettext("Install Package")); +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("Available packages"), false, "pkg_mgr.php"); +$tab_array[] = array(gettext("Installed packages"), false, "pkg_mgr_installed.php"); +$tab_array[] = array(gettext("Package Installer"), true, ""); +display_top_tabs($tab_array); +?> +<form action="pkg_mgr_install.php" method="post" class="form-horizontal"> + <h2>Add / remove package</h2> +<?php if ((empty($_GET['mode']) && $_GET['id']) || (!empty($_GET['mode']) && (!empty($_GET['pkg']) || $_GET['mode'] == 'reinstallall') && ($_GET['mode'] != 'installedinfo' && $_GET['mode'] != 'showlog'))): + if (empty($_GET['mode']) && $_GET['id']) { + $pkgname = str_replace(array("<", ">", ";", "&", "'", '"', '.', '/'), "", htmlspecialchars_decode($_GET['id'], ENT_QUOTES | ENT_HTML401)); + $pkgmode = 'installed'; + } else if (!empty($_GET['mode']) && !empty($_GET['pkg'])) { + $pkgname = str_replace(array("<", ">", ";", "&", "'", '"', '.', '/'), "", htmlspecialchars_decode($_GET['pkg'], ENT_QUOTES | ENT_HTML401)); + $pkgmode = str_replace(array("<", ">", ";", "&", "'", '"', '.', '/'), "", htmlspecialchars_decode($_GET['mode'], ENT_QUOTES | ENT_HTML401)); + } else if ($_GET['mode'] == 'reinstallall') { + $pkgmode = 'reinstallall'; + } + + switch ($pkgmode) { + case 'reinstallall': + $pkgname = 'All packages'; + $pkgtxt = 'reinstalled'; + break; + case 'reinstallxml': + $pkg_gui_xml_text = " GUI XML components"; + case 'reinstallpkg': + $pkgtxt = 'reinstalled'; + break; + case 'delete': + $pkgtxt = 'deleted'; + break; + default: + $pkgtxt = $pkgmode; + break; + } +?> + <div class="panel panel-default"> + <div class="panel-body"> + <p>Package: <b><?=$pkgname;?></b> will be <?=$pkgtxt;?>.</p> + </div> + <div class="panel-footer"> + <input type="hidden" name="id" value="<?=$pkgname;?>" /> + <input type="hidden" name="mode" value="<?=$pkgmode;?>" /> + </div> + </div> +<?php endif;?> + +<?php if (!empty($_POST['id']) || $_GET['mode'] == 'showlog' || ($_GET['mode'] == 'installedinfo' && !empty($_GET['pkg']))):?> + <div class="panel panel-default"> + <div class="panel-heading"> + <h2 id="status"><?=gettext("Beginning package installation.")?></h2> + </div> + + <div class="panel-body"> + <textarea rows="15" class="form-control" id="output"></textarea> + + <div class="progress"> + <div id="progressbar" class="progress-bar progress-bar-striped" role="progressbar" aria-valuemin="0" aria-valuemax="100" style="width: 1%"><span class="sr-only">1% Complete</span></div> + </div> + </div> + </div> +<?php endif?> +</form> +<?php + +ob_flush(); + +if ($_GET) { + $pkgname = str_replace(array("<", ">", ";", "&", "'", '"', '.', '/'), "", htmlspecialchars_decode($_GET['pkg'], ENT_QUOTES | ENT_HTML401)); + switch ($_GET['mode']) { + case 'showlog': + if (strpos($pkgname, ".")) { + update_output_window(gettext("Something is wrong on the request.")); + } else if (file_exists("/tmp/pkg_mgr_{$pkgname}.log")) { + update_output_window(@file_get_contents("/tmp/pkg_mgr_{$pkgname}.log")); + } else { + update_output_window(gettext("Log was not retrievable.")); + } + break; + case 'installedinfo': + if (file_exists("/tmp/{$pkgname}.info")) { + $status = @file_get_contents("/tmp/{$pkgname}.info"); + update_status("{$pkgname} " . gettext("installation completed.")); + update_output_window($status); + } else { + update_output_window(sprintf(gettext("Could not find %s."), $pkgname)); + } + break; + default: + break; + } +} else if ($_POST) { + $pkgid = str_replace(array("<", ">", ";", "&", "'", '"', '.', '/'), "", htmlspecialchars_decode($_POST['id'], ENT_QUOTES | ENT_HTML401)); + + /* All other cases make changes, so mount rw fs */ + conf_mount_rw(); + /* Write out configuration to create a backup prior to pkg install. */ + write_config(gettext("Creating restore point before package installation.")); + + switch ($_POST['mode']) { + case 'delete': + uninstall_package($pkgid); + update_status(gettext("Package deleted.")); + $static_output .= "\n" . gettext("Package deleted."); + update_output_window($static_output); + filter_configure(); + break; + case 'reinstallxml': + pkg_fetch_config_file($pkgid); + pkg_fetch_additional_files($pkgid); + break; + case 'reinstallpkg': + delete_package_xml($pkgid); + if (install_package($pkgid) != 0) { + update_status(gettext("Package reinstallation failed.")); + $static_output .= "\n" . gettext("Package reinstallation failed."); + update_output_window($static_output); + } else { + update_status(gettext("Package reinstalled.")); + $static_output .= "\n" . gettext("Package reinstalled."); + update_output_window($static_output); + filter_configure(); + } + @file_put_contents("/tmp/{$pkgid}.info", $static_output); + $pkgid = htmlspecialchars($pkgid); + echo "<script type='text/javascript'>document.location=\"pkg_mgr_install.php?mode=installedinfo&pkg={$pkgid}\";</script>"; + send_event("service restart packages"); + break; + case 'reinstallall': + if (is_array($config['installedpackages']) && is_array($config['installedpackages']['package'])) { + $todo = array(); + foreach ($config['installedpackages']['package'] as $package) { + $todo[] = array('name' => $package['name'], 'version' => $package['version']); + } + foreach ($todo as $pkgtodo) { + $static_output = ""; + if ($pkgtodo['name']) { + update_output_window($static_output); + uninstall_package($pkgtodo['name']); + install_package($pkgtodo['name']); + } + } + update_status(gettext("All packages reinstalled.")); + $static_output .= "\n" . gettext("All packages reinstalled."); + update_output_window($static_output); + filter_configure(); + send_event("service restart packages"); + } else { + update_output_window(gettext("No packages are installed.")); + } + break; + case 'installed': + default: + $status = install_package($pkgid); + if ($status != 0) { + update_status(gettext("Installation of") . " {$pkgid} " . gettext("FAILED!")); + $static_output .= "\n" . gettext("Installation halted."); + update_output_window($static_output); + } else { + $status_a = gettext(sprintf("Installation of %s completed.", $pkgid)); + update_status($status_a); + $status = get_after_install_info($pkgid); + if ($status) { + $static_output .= "\n" . gettext("Installation completed.") . "\n{$pkgid} " . gettext("setup instructions") . ":\n{$status}"; + } else { + $static_output .= "\n" . gettext("Installation completed. Please check to make sure that the package is configured from the respective menu then start the package."); + } + + @file_put_contents("/tmp/{$pkgid}.info", $static_output); + echo "<script type='text/javascript'>document.location=\"pkg_mgr_install.php?mode=installedinfo&pkg={$pkgid}\";</script>"; + } + filter_configure(); + break; + } + + // Delete all temporary package tarballs and staging areas. + unlink_if_exists("/tmp/apkg_*"); + rmdir_recursive("/var/tmp/instmp*"); + + // close log + if ($fd_log) { + fclose($fd_log); + } + + /* Restore to read only fs */ + conf_mount_ro(); +} + +include('foot.inc')?>
\ No newline at end of file diff --git a/src/usr/local/www/pkg_mgr_installed.php b/src/usr/local/www/pkg_mgr_installed.php new file mode 100644 index 0000000..91c90be --- /dev/null +++ b/src/usr/local/www/pkg_mgr_installed.php @@ -0,0 +1,179 @@ +<?php +/* $Id$ */ +/* + pkg_mgr_installed.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2004-2012 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: pkgs +*/ + +##|+PRIV +##|*IDENT=page-system-packagemanager-installed +##|*NAME=System: Package Manager: Installed page +##|*DESCR=Allow access to the 'System: Package Manager: Installed' page. +##|*MATCH=pkg_mgr_installed.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("pkg-utils.inc"); + +$timezone = $config['system']['timezone']; +if (!$timezone) { + $timezone = "Etc/UTC"; +} + +date_default_timezone_set($timezone); + +/* if upgrade in progress, alert user */ +if (is_subsystem_dirty('packagelock')) { + $pgtitle = array(gettext("System"), gettext("Package Manager")); + include("head.inc"); + print_info_box_np("Please wait while packages are reinstalled in the background."); + include("foot.inc"); + exit; +} + +if(is_array($config['installedpackages']['package'])) { + foreach($config['installedpackages']['package'] as $instpkg) { + $tocheck[] = $instpkg['name']; + } + $currentvers = get_pkg_info($tocheck, array('version', 'xmlver', 'pkginfolink', 'descr')); +} +$closehead = false; +$pgtitle = array(gettext("System"), gettext("Package Manager")); +include("head.inc"); + +/* Print package server mismatch warning. See https://redmine.pfsense.org/issues/484 */ +if (!verify_all_package_servers()) + print_info_box(package_server_mismatch_message()); + +/* Print package server SSL warning. See https://redmine.pfsense.org/issues/484 */ +if (check_package_server_ssl() === false) + print_info_box(package_server_ssl_failure_message()); + +$version = file_get_contents("/etc/version"); +$tab_array = array(); +$tab_array[] = array(gettext("Available Packages"), false, "pkg_mgr.php"); +// $tab_array[] = array("{$version} " . gettext("packages"), false, "pkg_mgr.php"); +// $tab_array[] = array("Packages for any platform", false, "pkg_mgr.php?ver=none"); +// $tab_array[] = array("Packages for a different platform", $requested_version == "other" ? true : false, "pkg_mgr.php?ver=other"); +$tab_array[] = array(gettext("Installed Packages"), true, "pkg_mgr_installed.php"); +display_top_tabs($tab_array); + +if(!is_array($config['installedpackages']['package'])):?> + <div class="alert alert-warning"> + <?=gettext("There are no packages currently installed.")?> + </div> +<?php else: ?> + <div class="table-responsive"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th><span class="sr-only"><?=gettext("Status")?></span></th> + <th><?=gettext("Name")?></th> + <th><?=gettext("Category")?></th> + <th><?=gettext("Version")?></th> + <th><?=gettext("Description")?></th> + </tr> + </thead> + <tbody> +<?php + $instpkgs = array(); + foreach($config['installedpackages']['package'] as $instpkg) { + $instpkgs[] = $instpkg['name']; + } + natcasesort($instpkgs); + + foreach ($instpkgs as $index => $pkgname): + $pkg = $config['installedpackages']['package'][$index]; + if(!$pkg['name']) + continue; + + // get history/changelog git dir + $commit_dir=explode("/",$pkg['config_file']); + $changeloglink ="https://github.com/pfsense/pfsense-packages/commits/master/config/".$commit_dir[(count($commit_dir)-2)]; + #check package version + $latest_package = $currentvers[$pkg['name']]['version']; + if ($latest_package) { + // we're running a newer version of the package + if(strcmp($pkg['version'], $latest_package) > 0) { + $status = 'Newer then available ('. $latest_package .')'; + $statusicon = 'exclamation'; + } + // we're running an older version of the package + if(strcmp($pkg['version'], $latest_package) < 0) { + $status = 'Upgrade available to '.$latest_package; + $statusicon = 'plus'; + } + // we're running the current version + if(!strcmp($pkg['version'], $latest_package)) { + $status = 'Up-to-date'; + $statusicon = 'ok'; + } + $pkgdescr = $currentvers[$pkg['name']]['descr']; + } else { + // unknown available package version + $status = 'Unknown'; + $statusicon = 'question'; + $pkgdescr = $pkg['descr']; + } +?> + <tr> + <td> + <i title="<?=$status?>" class="icon icon-<?=$statusicon?>-sign"></i> + </td> + <td> + <?=$pkg['name']?> + </td> + <td> + <?=$pkg['category']?> + </td> + <td> +<?php if (!$g['disablepackagehistory']):?> + <a target="_blank" title="<?=gettext("View changelog")?>" href="<?=htmlspecialchars($changeloglink)?>"> +<?php endif;?> + <?=htmlspecialchars($pkg['version'])?> + </a> + </td> + <td> + <?=$pkgdescr?> + </td> + <td> + <a href="pkg_mgr_install.php?mode=delete&pkg=<?=$pkg['name']?>" class="btn btn-danger">remove</a> + <a href="pkg_mgr_install.php?mode=reinstallpkg&pkg=<?=$pkg['name']?>" class="btn btn-info">reinstall</a> + <a href="pkg_mgr_install.php?mode=reinstallxml&pkg=<?=$pkg['name']?>" class="btn btn-info"><?=gettext("reinstall GUI")?></a> +<?php if(!$g['disablepackageinfo'] && $pkg['pkginfolink'] && $pkg['pkginfolink'] != $pkg['website']):?> + <a target="_blank" title="<?=gettext("View more inforation")?>" href="<?=htmlspecialchars($pkg['pkginfolink'])?>" class="btn btn-default">info</a> +<?php endif;?> + </td> + </tr> +<?php endforeach;?> + </tbody> +</table> +</div> +<?php endif; ?> +<?php include("foot.inc")?>
\ No newline at end of file diff --git a/src/usr/local/www/pkg_mgr_settings.php b/src/usr/local/www/pkg_mgr_settings.php new file mode 100644 index 0000000..da3c393 --- /dev/null +++ b/src/usr/local/www/pkg_mgr_settings.php @@ -0,0 +1,115 @@ +<?php +/* $Id$ */ +/* + pkg_mgr_settings.php + part of pfSense + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2009 Jim Pingle <jimp@pfsense.org> + Copyright (C) 2004-2010 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2005 Colin Smith + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: pkgs +*/ + +##|+PRIV +##|*IDENT=page-pkg-mgr-settings +##|*NAME=Packages: Settings page +##|*DESCR=Allow access to the 'Packages: Settings' page. +##|*MATCH=pkg_mgr_settings.php* +##|-PRIV + +ini_set('max_execution_time', '0'); + +require_once("globals.inc"); +require_once("guiconfig.inc"); +require_once("pkg-utils.inc"); + +if ($_POST) { + if (!$input_errors) { + if ($_POST['alturlenable'] == "yes") { + $config['system']['altpkgrepo']['enable'] = true; + $config['system']['altpkgrepo']['xmlrpcbaseurl'] = $_POST['pkgrepourl']; + } else { + unset($config['system']['altpkgrepo']['enable']); + } + write_config(); + } + + write_config(); +} + +$curcfg = $config['system']['altpkgrepo']; +$closehead = false; +$pgtitle = array(gettext("System"), gettext("Package Settings")); +include("head.inc"); + +// Print package server mismatch warning. See https://redmine.pfsense.org/issues/484 +if (!verify_all_package_servers()) + print_info_box(package_server_mismatch_message()); + +// Print package server SSL warning. See https://redmine.pfsense.org/issues/484 +if (check_package_server_ssl() === false) + print_info_box(package_server_ssl_failure_message()); + +if ($savemsg) + print_info_box($savemsg); + +$version = file_get_contents("/etc/version"); +$tab_array = array(); +$tab_array[] = array(sprintf(gettext("%s packages"), $version), false, "pkg_mgr.php"); +$tab_array[] = array(gettext("Installed Packages"), false, "pkg_mgr_installed.php"); +$tab_array[] = array(gettext("Package Settings"), true, "pkg_mgr_settings.php"); +display_top_tabs($tab_array); + +print_info_box(gettext('This page allows an alternate package repository to be configured, primarily for temporary use as a testing mechanism.' . + 'The contents of unofficial packages servers cannot be verified and may contain malicious files.' . + 'The package server settings should remain at their default values to ensure that verifiable and trusted packages are recevied.' . + 'A warning is printed on the Dashboard and in the package manager when an unofficial package server is in use.'), 'default'); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Alternate package repository'); + +$section->addInput(new Form_Checkbox( + 'alturlenable', + 'Enable Alternate', + 'Use a non-official server for packages', + $curcfg['enable'] +))->toggles('.form-group:not(:first-child)'); + +$section->addInput(new Form_Input( + 'pkgrepourl', + 'Package Repository URL', + 'text', + $curcfg['xmlrpcbaseurl'] ? $curcfg['xmlrpcbaseurl'] : $g[''] +))->setHelp(sprintf("This is where %s will check for packages when the",$g['product_name']) . + '<a href="pkg_mgr.php">' . ' ' . 'System: Packages' . ' </a>' . 'page is viewed.'); + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/reboot.php b/src/usr/local/www/reboot.php new file mode 100755 index 0000000..dc4b2c6 --- /dev/null +++ b/src/usr/local/www/reboot.php @@ -0,0 +1,83 @@ +<?php +/* $Id$ */ +/* + reboot.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-rebootsystem +##|*NAME=Diagnostics: Reboot System page +##|*DESCR=Allow access to the 'Diagnostics: Reboot System' page. +##|*MATCH=reboot.php* +##|-PRIV + +// Set DEBUG to true to prevent the system_reboot() function from being called +define("DEBUG", false); + +require("guiconfig.inc"); +require("functions.inc"); +require("captiveportal.inc"); + +$pgtitle = array(gettext("Diagnostics"),gettext("Reboot System")); +include("head.inc"); + + +if ($_SERVER['REQUEST_METHOD'] == 'POST') { +?> + <meta http-equiv="refresh" content="70;url=/"> + <div class="alert alert-success" role="alert"> + <?=gettext("The system is rebooting now. This may take one minute or so.")?> + </div> +<?php + + if(DEBUG) + print("Not actually rebooting (DEBUG is set true)"); + else + system_reboot(); + +} else { + + +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">Are you sure you want to reboot the system?</h2></div> + <div class="panel-body">Click "Yes" to reboot the system immediately, or "No" to go to the system dashboard without rebooting. (There will be a brief delay before the dashboard appears.)<br /><br /> + <form action="reboot.php" method="post"> + <input type="submit" class="btn btn-danger pull-center" name="Submit" value="Yes"> + <a href="/" class="btn btn-default">No</a> + </form> + </div> +</div> + +<?php + +} + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/restart_httpd.php b/src/usr/local/www/restart_httpd.php new file mode 100644 index 0000000..2f59bbf --- /dev/null +++ b/src/usr/local/www/restart_httpd.php @@ -0,0 +1,68 @@ +<?php +/* $Id$ */ +/* + restart_httpd.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2005 Bill Marquette <bill.marquette@gmail.com>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /bin/chmod + pfSense_MODULE: pkgs +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-restart-httpd +##|*NAME=Diagnostics: Restart HTTPD : System page +##|*DESCR=Allow access to the 'Diagnostics: Restart HTTPD: System' page. +##|*MATCH=restart_httpd.php* +##|-PRIV + +require_once("guiconfig.inc"); + +$pgtitle = array(gettext("Restarting httpd")); +include("head.inc"); +?> + +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<form> +<?php include("fbegin.inc"); ?> + +<?=gettext("Mounting file systems read/write");?>... +<?php flush(); sleep(1); conf_mount_rw(); ?> +<?=gettext("Done");?>.<br /> +<?=gettext("Forcing all PHP file permissions to 0755");?>... +<?php flush(); sleep(1); system('/bin/chmod -R 0755 /usr/local/www/*.php'); ?> +<?=gettext("Done");?>.<br /> +<?=gettext("Mounting file systems read only");?>... +<?php flush(); sleep(1); conf_mount_ro(); ?> +<?=gettext("Done");?>.<br /> +<?=gettext("Restarting mini_httpd");?>... +<?php flush(); sleep(1); system_webgui_start(); ?> +<?=gettext("Done");?>.<br /> + +<?php +include("fend.inc"); +?> diff --git a/src/usr/local/www/services_captiveportal.php b/src/usr/local/www/services_captiveportal.php new file mode 100644 index 0000000..04a9e20 --- /dev/null +++ b/src/usr/local/www/services_captiveportal.php @@ -0,0 +1,1260 @@ +<?php +/* + services_captiveportal.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-services-captiveportal +##|*NAME=Services: Captive portal page +##|*DESCR=Allow access to the 'Services: Captive portal' page. +##|*MATCH=services_captiveportal.php* +##|-PRIV + +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("captiveportal.inc"); + +if (substr($_GET['act'], 0, 3) == "get") { + $nocsrf = true; +} + +require_once("guiconfig.inc"); + +global $cpzone; +global $cpzoneid; + +$cpzoneid = 1; /* Just a default */ +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + +if (empty($cpzone) || empty($config['captiveportal'][$cpzone])) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +$pgtitle = array(gettext("Services"), gettext("Captive portal"), $a_cp[$cpzone]['zone']); +$shortcut_section = "captiveportal"; + +if ($_GET['act'] == "viewhtml") { + if ($a_cp[$cpzone] && $a_cp[$cpzone]['page']['htmltext']) { + echo base64_decode($a_cp[$cpzone]['page']['htmltext']); + } + exit; +} else if ($_GET['act'] == "gethtmlhtml" && $a_cp[$cpzone] && $a_cp[$cpzone]['page']['htmltext']) { + $file_data = base64_decode($a_cp[$cpzone]['page']['htmltext']); + $file_size = strlen($file_data); + + header("Content-Type: text/html"); + header("Content-Disposition: attachment; filename=portal.html"); + header("Content-Length: $file_size"); + echo $file_data; + + exit; +} else if ($_GET['act'] == "delhtmlhtml" && $a_cp[$cpzone] && $a_cp[$cpzone]['page']['htmltext']) { + unset($a_cp[$cpzone]['page']['htmltext']); + write_config(sprintf(gettext("Captive Portal: zone %s: Restore default portal page"), $cpzone)); + header("Location: services_captiveportal.php?zone={$cpzone}"); + exit; +} else if ($_GET['act'] == "viewerrhtml") { + if ($a_cp[$cpzone] && $a_cp[$cpzone]['page']['errtext']) { + echo base64_decode($a_cp[$cpzone]['page']['errtext']); + } + exit; +} else if ($_GET['act'] == "geterrhtml" && $a_cp[$cpzone] && $a_cp[$cpzone]['page']['errtext']) { + $file_data = base64_decode($a_cp[$cpzone]['page']['errtext']); + $file_size = strlen($file_data); + + header("Content-Type: text/html"); + header("Content-Disposition: attachment; filename=err.html"); + header("Content-Length: $file_size"); + echo $file_data; + + exit; +} else if ($_GET['act'] == "delerrhtml" && $a_cp[$cpzone] && $a_cp[$cpzone]['page']['errtext']) { + unset($a_cp[$cpzone]['page']['errtext']); + write_config(sprintf(gettext("Captive Portal: zone %s: Restore default error page"), $cpzone)); + header("Location: services_captiveportal.php?zone={$cpzone}"); + exit; +} else if ($_GET['act'] == "viewlogouthtml") { + if ($a_cp[$cpzone] && $a_cp[$cpzone]['page']['logouttext']) { + echo base64_decode($a_cp[$cpzone]['page']['logouttext']); + } + exit; +} else if ($_GET['act'] == "getlogouthtml" && $a_cp[$cpzone] && $a_cp[$cpzone]['page']['logouttext']) { + $file_data = base64_decode($a_cp[$cpzone]['page']['logouttext']); + $file_size = strlen($file_data); + + header("Content-Type: text/html"); + header("Content-Disposition: attachment; filename=logout.html"); + header("Content-Length: $file_size"); + echo $file_data; + + exit; +} else if ($_GET['act'] == "dellogouthtml" && $a_cp[$cpzone] && $a_cp[$cpzone]['page']['logouttext']) { + unset($a_cp[$cpzone]['page']['logouttext']); + write_config(sprintf(gettext("Captive Portal: zone %s: Restore default logout page"), $cpzone)); + header("Location: services_captiveportal.php?zone={$cpzone}"); + exit; +} + +if (!is_array($config['ca'])) { + $config['ca'] = array(); +} + +$a_ca =& $config['ca']; + +if (!is_array($config['cert'])) { + $config['cert'] = array(); +} + +$a_cert =& $config['cert']; + +if ($a_cp[$cpzone]) { + $cpzoneid = $pconfig['zoneid'] = $a_cp[$cpzone]['zoneid']; + $pconfig['cinterface'] = $a_cp[$cpzone]['interface']; + $pconfig['maxproc'] = $a_cp[$cpzone]['maxproc']; + $pconfig['maxprocperip'] = $a_cp[$cpzone]['maxprocperip']; + $pconfig['timeout'] = $a_cp[$cpzone]['timeout']; + $pconfig['idletimeout'] = $a_cp[$cpzone]['idletimeout']; + $pconfig['freelogins_count'] = $a_cp[$cpzone]['freelogins_count']; + $pconfig['freelogins_resettimeout'] = $a_cp[$cpzone]['freelogins_resettimeout']; + $pconfig['freelogins_updatetimeouts'] = isset($a_cp[$cpzone]['freelogins_updatetimeouts']); + $pconfig['enable'] = isset($a_cp[$cpzone]['enable']); + $pconfig['auth_method'] = $a_cp[$cpzone]['auth_method']; + $pconfig['localauth_priv'] = isset($a_cp[$cpzone]['localauth_priv']); + $pconfig['radacct_enable'] = isset($a_cp[$cpzone]['radacct_enable']); + $pconfig['radmac_enable'] = isset($a_cp[$cpzone]['radmac_enable']); + $pconfig['radmac_secret'] = $a_cp[$cpzone]['radmac_secret']; + $pconfig['reauthenticate'] = isset($a_cp[$cpzone]['reauthenticate']); + $pconfig['reauthenticateacct'] = $a_cp[$cpzone]['reauthenticateacct']; + $pconfig['httpslogin_enable'] = isset($a_cp[$cpzone]['httpslogin']); + $pconfig['httpsname'] = $a_cp[$cpzone]['httpsname']; + $pconfig['preauthurl'] = strtolower($a_cp[$cpzone]['preauthurl']); + $pconfig['blockedmacsurl'] = strtolower($a_cp[$cpzone]['blockedmacsurl']); + $pconfig['certref'] = $a_cp[$cpzone]['certref']; + $pconfig['nohttpsforwards'] = isset($a_cp[$cpzone]['nohttpsforwards']); + $pconfig['logoutwin_enable'] = isset($a_cp[$cpzone]['logoutwin_enable']); + $pconfig['peruserbw'] = isset($a_cp[$cpzone]['peruserbw']); + $pconfig['bwdefaultdn'] = $a_cp[$cpzone]['bwdefaultdn']; + $pconfig['bwdefaultup'] = $a_cp[$cpzone]['bwdefaultup']; + $pconfig['nomacfilter'] = isset($a_cp[$cpzone]['nomacfilter']); + $pconfig['noconcurrentlogins'] = isset($a_cp[$cpzone]['noconcurrentlogins']); + $pconfig['radius_protocol'] = $a_cp[$cpzone]['radius_protocol']; + $pconfig['redirurl'] = $a_cp[$cpzone]['redirurl']; + $pconfig['radiusip'] = $a_cp[$cpzone]['radiusip']; + $pconfig['radiusip2'] = $a_cp[$cpzone]['radiusip2']; + $pconfig['radiusip3'] = $a_cp[$cpzone]['radiusip3']; + $pconfig['radiusip4'] = $a_cp[$cpzone]['radiusip4']; + $pconfig['radiusport'] = $a_cp[$cpzone]['radiusport']; + $pconfig['radiusport2'] = $a_cp[$cpzone]['radiusport2']; + $pconfig['radiusport3'] = $a_cp[$cpzone]['radiusport3']; + $pconfig['radiusport4'] = $a_cp[$cpzone]['radiusport4']; + $pconfig['radiusacctport'] = $a_cp[$cpzone]['radiusacctport']; + $pconfig['radiuskey'] = $a_cp[$cpzone]['radiuskey']; + $pconfig['radiuskey2'] = $a_cp[$cpzone]['radiuskey2']; + $pconfig['radiuskey3'] = $a_cp[$cpzone]['radiuskey3']; + $pconfig['radiuskey4'] = $a_cp[$cpzone]['radiuskey4']; + $pconfig['radiusvendor'] = $a_cp[$cpzone]['radiusvendor']; + $pconfig['radiussession_timeout'] = isset($a_cp[$cpzone]['radiussession_timeout']); + $pconfig['radiussrcip_attribute'] = $a_cp[$cpzone]['radiussrcip_attribute']; + $pconfig['passthrumacadd'] = isset($a_cp[$cpzone]['passthrumacadd']); + $pconfig['passthrumacaddusername'] = isset($a_cp[$cpzone]['passthrumacaddusername']); + $pconfig['radmac_format'] = $a_cp[$cpzone]['radmac_format']; + $pconfig['reverseacct'] = isset($a_cp[$cpzone]['reverseacct']); + $pconfig['radiusnasid'] = $a_cp[$cpzone]['radiusnasid']; + $pconfig['page'] = array(); + if ($a_cp[$cpzone]['page']['htmltext']) { + $pconfig['page']['htmltext'] = $a_cp[$cpzone]['page']['htmltext']; + } + if ($a_cp[$cpzone]['page']['errtext']) { + $pconfig['page']['errtext'] = $a_cp[$cpzone]['page']['errtext']; + } + if ($a_cp[$cpzone]['page']['logouttext']) { + $pconfig['page']['logouttext'] = $a_cp[$cpzone]['page']['logouttext']; + } +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if ($_POST['enable']) { + $reqdfields = explode(" ", "zone cinterface"); + $reqdfieldsn = array(gettext("Zone name"), gettext("Interface")); + + if (isset($_POST['auth_method']) && $_POST['auth_method'] == "radius") { + $reqdfields[] = "radius_protocol"; + $reqdfieldsn[] = gettext("RADIUS Protocol"); + $reqdfields[] = "radiusip"; + $reqdfieldsn[] = gettext("Primary RADIUS server IP address"); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + /* make sure no interfaces are bridged or used on other zones */ + if (is_array($_POST['cinterface'])) { + foreach ($pconfig['cinterface'] as $cpbrif) { + if (link_interface_to_bridge($cpbrif)) { + $input_errors[] = sprintf(gettext("The captive portal cannot be used on interface %s since it is part of a bridge."), $cpbrif); + } + foreach ($a_cp as $cpkey => $cp) { + if ($cpkey != $cpzone || empty($cpzone)) { + if (in_array($cpbrif, explode(",", $cp['interface']))) { + $input_errors[] = sprintf(gettext("The captive portal cannot be used on interface %s since it is used already on %s instance."), $cpbrif, $cp['zone']); + } + } + } + } + } + + if ($_POST['httpslogin_enable']) { + if (!$_POST['certref']) { + $input_errors[] = gettext("Certificate must be specified for HTTPS login."); + } + if (!$_POST['httpsname'] || !is_domain($_POST['httpsname'])) { + $input_errors[] = gettext("The HTTPS server name must be specified for HTTPS login."); + } + } + } + + if ($_POST['timeout']) { + if (!is_numeric($_POST['timeout']) || ($_POST['timeout'] < 1)) { + $input_errors[] = gettext("The timeout must be at least 1 minute."); + } else if (isset($config['dhcpd']) && is_array($config['dhcpd'])) { + foreach ($config['dhcpd'] as $dhcpd_if => $dhcpd_data) { + if (!isset($dhcpd_data['enable'])) { + continue; + } + if (!is_array($_POST['cinterface']) || !in_array($dhcpd_if, $_POST['cinterface'])) { + continue; + } + + $deftime = 7200; // Default lease time + if (isset($dhcpd_data['defaultleasetime']) && is_numeric($dhcpd_data['defaultleasetime'])) { + $deftime = $dhcpd_data['defaultleasetime']; + } + + if ($_POST['timeout'] > $deftime) { + $input_errors[] = gettext("Hard timeout must be less or equal Default lease time set on DHCP Server"); + } + } + } + } + + if ($_POST['idletimeout'] && (!is_numeric($_POST['idletimeout']) || ($_POST['idletimeout'] < 1))) { + $input_errors[] = gettext("The idle timeout must be at least 1 minute."); + } + + if ($_POST['freelogins_count'] && (!is_numeric($_POST['freelogins_count']))) { + $input_errors[] = gettext("The pass-through credit count must be a number or left blank."); + } else if ($_POST['freelogins_count'] && is_numeric($_POST['freelogins_count']) && ($_POST['freelogins_count'] >= 1)) { + if (empty($_POST['freelogins_resettimeout']) || !is_numeric($_POST['freelogins_resettimeout']) || ($_POST['freelogins_resettimeout'] <= 0)) { + $input_errors[] = gettext("The waiting period to restore pass-through credits must be above 0 hours."); + } + } + + if (($_POST['radiusip'] && !is_ipaddr($_POST['radiusip']))) { + $input_errors[] = sprintf(gettext("A valid IP address must be specified. [%s]"), $_POST['radiusip']); + } + + if (($_POST['radiusip2'] && !is_ipaddr($_POST['radiusip2']))) { + $input_errors[] = sprintf(gettext("A valid IP address must be specified. [%s]"), $_POST['radiusip2']); + } + + if (($_POST['radiusip3'] && !is_ipaddr($_POST['radiusip3']))) { + $input_errors[] = sprintf(gettext("A valid IP address must be specified. [%s]"), $_POST['radiusip3']); + } + + if (($_POST['radiusip4'] && !is_ipaddr($_POST['radiusip4']))) { + $input_errors[] = sprintf(gettext("A valid IP address must be specified. [%s]"), $_POST['radiusip4']); + } + + if (($_POST['radiusport'] && !is_port($_POST['radiusport']))) { + $input_errors[] = sprintf(gettext("A valid port number must be specified. [%s]"), $_POST['radiusport']); + } + + if (($_POST['radiusport2'] && !is_port($_POST['radiusport2']))) { + $input_errors[] = sprintf(gettext("A valid port number must be specified. [%s]"), $_POST['radiusport2']); + } + + if (($_POST['radiusport3'] && !is_port($_POST['radiusport3']))) { + $input_errors[] = sprintf(gettext("A valid port number must be specified. [%s]"), $_POST['radiusport3']); + } + + if (($_POST['radiusport4'] && !is_port($_POST['radiusport4']))) { + $input_errors[] = sprintf(gettext("A valid port number must be specified. [%s]"), $_POST['radiusport4']); + } + + if (($_POST['radiusacctport'] && !is_port($_POST['radiusacctport']))) { + $input_errors[] = sprintf(gettext("A valid port number must be specified. [%s]"), $_POST['radiusacctport']); + } + + if ($_POST['maxproc'] && (!is_numeric($_POST['maxproc']) || ($_POST['maxproc'] < 4) || ($_POST['maxproc'] > 100))) { + $input_errors[] = gettext("The maximum number of concurrent connections per client IP address may not be larger than the global maximum."); + } + + if (trim($_POST['radiusnasid']) !== "" && !preg_match("/^[\x21-\x7e]{3,253}$/i", trim($_POST['radiusnasid']))) { + $input_errors[] = gettext("The NAS-Identifier must be 3-253 characters long and should only contain ASCII characters."); + } + + if (!$input_errors) { + $newcp =& $a_cp[$cpzone]; + //$newcp['zoneid'] = $a_cp[$cpzone]['zoneid']; + if (empty($newcp['zoneid'])) { + $newcp['zoneid'] = 2; + foreach ($a_cp as $keycpzone => $cp) { + if ($cp['zoneid'] == $newcp['zoneid'] && $keycpzone != $cpzone) { + $newcp['zoneid'] += 2; /* Reserve space for SSL config if needed */ + } + } + + $cpzoneid = $newcp['zoneid']; + } + $oldifaces = explode(",", $newcp['interface']); + if (is_array($_POST['cinterface'])) { + $newcp['interface'] = implode(",", $_POST['cinterface']); + } + $newcp['maxproc'] = $_POST['maxproc']; + $newcp['maxprocperip'] = $_POST['maxprocperip'] ? $_POST['maxprocperip'] : false; + $newcp['timeout'] = $_POST['timeout']; + $newcp['idletimeout'] = $_POST['idletimeout']; + $newcp['freelogins_count'] = $_POST['freelogins_count']; + $newcp['freelogins_resettimeout'] = $_POST['freelogins_resettimeout']; + $newcp['freelogins_updatetimeouts'] = $_POST['freelogins_updatetimeouts'] ? true : false; + if ($_POST['enable']) { + $newcp['enable'] = true; + } else { + unset($newcp['enable']); + } + $newcp['auth_method'] = $_POST['auth_method']; + $newcp['localauth_priv'] = isset($_POST['localauth_priv']); + $newcp['radacct_enable'] = $_POST['radacct_enable'] ? true : false; + $newcp['reauthenticate'] = $_POST['reauthenticate'] ? true : false; + $newcp['radmac_enable'] = $_POST['radmac_enable'] ? true : false; + $newcp['radmac_secret'] = $_POST['radmac_secret'] ? $_POST['radmac_secret'] : false; + $newcp['reauthenticateacct'] = $_POST['reauthenticateacct']; + if ($_POST['httpslogin_enable']) { + $newcp['httpslogin'] = true; + } else { + unset($newcp['httpslogin']); + } + $newcp['httpsname'] = $_POST['httpsname']; + $newcp['preauthurl'] = $_POST['preauthurl']; + $newcp['blockedmacsurl'] = $_POST['blockedmacsurl']; + $newcp['peruserbw'] = $_POST['peruserbw'] ? true : false; + if (isset($_POST['bwdefaultdn'])) { + $newcp['bwdefaultdn'] = $_POST['bwdefaultdn']; + } else { + unset($newcp['bwdefaultdn']); + } + if (isset($_POST['bwdefaultup'])) { + $newcp['bwdefaultup'] = $_POST['bwdefaultup']; + } else { + unset($newcp['bwdefaultup']); + } + $newcp['certref'] = $_POST['certref']; + $newcp['nohttpsforwards'] = $_POST['nohttpsforwards'] ? true : false; + $newcp['logoutwin_enable'] = $_POST['logoutwin_enable'] ? true : false; + $newcp['nomacfilter'] = $_POST['nomacfilter'] ? true : false; + $newcp['noconcurrentlogins'] = $_POST['noconcurrentlogins'] ? true : false; + $newcp['radius_protocol'] = $_POST['radius_protocol']; + $newcp['redirurl'] = $_POST['redirurl']; + if (isset($_POST['radiusip'])) { + $newcp['radiusip'] = $_POST['radiusip']; + } else { + unset($newcp['radiusip']); + } + if (isset($_POST['radiusip2'])) { + $newcp['radiusip2'] = $_POST['radiusip2']; + } else { + unset($newcp['radiusip2']); + } + if (isset($_POST['radiusip3'])) { + $newcp['radiusip3'] = $_POST['radiusip3']; + } else { + unset($newcp['radiusip3']); + } + if (isset($_POST['radiusip4'])) { + $newcp['radiusip4'] = $_POST['radiusip4']; + } else { + unset($newcp['radiusip4']); + } + $newcp['radiusport'] = $_POST['radiusport']; + $newcp['radiusport2'] = $_POST['radiusport2']; + if (isset($_POST['radiusport3'])) { + $newcp['radiusport3'] = $_POST['radiusport3']; + } + if (isset($_POST['radiusport4'])) { + $newcp['radiusport4'] = $_POST['radiusport4']; + } + $newcp['radiusacctport'] = $_POST['radiusacctport']; + $newcp['radiuskey'] = $_POST['radiuskey']; + $newcp['radiuskey2'] = $_POST['radiuskey2']; + $newcp['radiuskey3'] = $_POST['radiuskey3']; + $newcp['radiuskey4'] = $_POST['radiuskey4']; + $newcp['radiusvendor'] = $_POST['radiusvendor'] ? $_POST['radiusvendor'] : false; + $newcp['radiussession_timeout'] = $_POST['radiussession_timeout'] ? true : false; + $newcp['radiussrcip_attribute'] = $_POST['radiussrcip_attribute']; + $newcp['passthrumacadd'] = $_POST['passthrumacadd'] ? true : false; + $newcp['passthrumacaddusername'] = $_POST['passthrumacaddusername'] ? true : false; + $newcp['radmac_format'] = $_POST['radmac_format'] ? $_POST['radmac_format'] : false; + $newcp['reverseacct'] = $_POST['reverseacct'] ? true : false; + $newcp['radiusnasid'] = trim($_POST['radiusnasid']); + + if (!is_array($newcp['page'])) { + $newcp['page'] = array(); + } + + /* file upload? */ + if (is_uploaded_file($_FILES['htmlfile']['tmp_name'])) { + $newcp['page']['htmltext'] = base64_encode(file_get_contents($_FILES['htmlfile']['tmp_name'])); + } + if (is_uploaded_file($_FILES['errfile']['tmp_name'])) { + $newcp['page']['errtext'] = base64_encode(file_get_contents($_FILES['errfile']['tmp_name'])); + } + if (is_uploaded_file($_FILES['logoutfile']['tmp_name'])) { + $newcp['page']['logouttext'] = base64_encode(file_get_contents($_FILES['logoutfile']['tmp_name'])); + } + + write_config(); + + /* Clear up unselected interfaces */ + $newifaces = explode(",", $newcp['interface']); + $toremove = array_diff($oldifaces, $newifaces); + + if (!empty($toremove)) { + foreach ($toremove as $removeif) { + $removeif = get_real_interface($removeif); + mwexec("/sbin/ipfw zone {$cpzoneid} mdel {$removeif}"); + } + } + + captiveportal_configure_zone($newcp); + unset($newcp, $newifaces, $toremove); + filter_configure(); + header("Location: services_captiveportal_zones.php"); + exit; + } else { + if (is_array($_POST['cinterface'])) { + $pconfig['cinterface'] = implode(",", $_POST['cinterface']); + } + } +} + +function build_radiusnas_list() { + $list = array(); + + $iflist = get_configured_interface_with_descr(); + foreach ($iflist as $ifdesc => $ifdescr) { + $ipaddr = get_interface_ip($ifdesc); + if (is_ipaddr($ipaddr)) { + $list[$ifdescr] = $ifdescr . ' - ' . $ipaddr; + } + } + + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $sn) { + if ($sn['mode'] == "proxyarp" && $sn['type'] == "network") { + $start = ip2long32(gen_subnet($sn['subnet'], $sn['subnet_bits'])); + $end = ip2long32(gen_subnet_max($sn['subnet'], $sn['subnet_bits'])); + $len = $end - $start; + + for ($i = 0; $i <= $len; $i++) { + $snip = long2ip32($start+$i); + $list[$snip] = $sn['descr'] . ' - ' . $snip; + } + } else + $list[$sn['subnet']] = $sn['descr'] . ' - ' . $sn['subnet']; + } + } + + return($list); +} + +function build_cert_list() { + global $a_cert; + + $list = array(); + + foreach($a_cert as $cert) + $list[$cert['refid']] = $cert['descr']; + + return($list); +} + +$closehead = false; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +$tab_array = array(); +$tab_array[] = array(gettext("Captive portal(s)"), true, "services_captiveportal.php?zone={$cpzone}"); +$tab_array[] = array(gettext("MAC"), false, "services_captiveportal_mac.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Allowed IP addresses"), false, "services_captiveportal_ip.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Allowed Hostnames"), false, "services_captiveportal_hostname.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Vouchers"), false, "services_captiveportal_vouchers.php?zone={$cpzone}"); +$tab_array[] = array(gettext("File Manager"), false, "services_captiveportal_filemanager.php?zone={$cpzone}"); +display_top_tabs($tab_array, true); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Captive Portal Configuration'); + +$section->addInput(new Form_Checkbox( + 'enable', + 'Enable', + 'Enable Captive Portal', + $pconfig['enable'] +)); + +$section->addInput(new Form_Select( + 'cinterface', + 'Interfaces', + explode(",", $pconfig['cinterface']), + get_configured_interface_with_descr(), + true +))->addClass('general')->setHelp('Select the interface(s) to enable for captive portal.'); + +$section->addInput(new Form_Input( + 'maxprocperip', + 'Maximum concurrent connections', + 'number', + $pconfig['maxprocperip'], + ['min' => '0', 'max' => '100'] +))->setHelp('Timits the number of concurrent connections to the captive portal HTTP(S) server. This does not set how many users can be logged in ' . + 'to the captive portal, but rather how many users can load the portal page or authenticate at the same time! ' . + 'Possible setting allowed is: minimum 4 connections per client IP address, with a total maximum of 100 connections.'); + +$section->addInput(new Form_Input( + 'idletimeout', + 'Idle timeout (Minutes)', + 'number', + $pconfig['idletimeout'] +))->setHelp('Clients will be disconnected after this amount of inactivity. They may log in again immediately, though. Leave this field blank for no idle timeout.'); + +$section->addInput(new Form_Input( + 'timeout', + 'Hard timeout (Minutes)', + 'number', + $pconfig['timeout'] +))->setHelp('Clients will be disconnected after this amount of time, regardless of activity. They may log in again immediately, though. ' . + 'Leave this field blank for no hard timeout (not recommended unless an idle timeout is set).'); + +$section->addInput(new Form_Input( + 'freelogins_count', + 'Pass-through credits per MAC address.', + 'number', + $pconfig['freelogins_count'] +))->setHelp('Allows passing through the captive portal without authentication a limited number of times per MAC address. Once used up, ' . + 'the client can only log in with valid credentials until the waiting period specified below has expired. Recommended to set ' . + 'a hard timeout and/or idle timeout when using this for it to be effective.'); + +$section->addInput(new Form_Checkbox( + 'freelogins_updatetimeouts', + 'Reset waiting period', + 'Enable waiting period reset on attempted access', + $pconfig['freelogins_updatetimeouts'] +))->setHelp('If enabled, the waiting period is reset to the original duration if access is attempted when all pass-through credits have already been exhausted.'); + +$section->addInput(new Form_Checkbox( + 'logoutwin_enable', + 'Logout popup window', + 'Enable logout popup window', + $pconfig['logoutwin_enable'] +))->setHelp('If enabled, a popup window will appear when clients are allowed through the captive portal. ' . + 'This allows clients to explicitly disconnect themselves before the idle or hard timeout occurs.'); + +$section->addInput(new Form_Input( + 'preauthurl', + 'Pre-authentication redirect URL', + 'text', + $pconfig['preauthurl'] +))->setHelp('Use this field to set $PORTAL_REDIRURL$ variable which can be accessed using your custom captive portal index.php page or error pages.'); + +$section->addInput(new Form_Input( + 'redirurl', + 'After authentication Redirection URL', + 'text', + $pconfig['redirurl'] +))->setHelp('Clients will be redirected to this URL instead of the one they initially tried to access after they\'ve authenticated'); + +$section->addInput(new Form_Input( + 'blockedmacsurl', + 'Blocked MAC address redirect URL', + 'text', + $pconfig['blockedmacsurl'] +))->setHelp('Blocked MAC addresses will be redirected to this URL when attempting access.'); + +$section->addInput(new Form_Checkbox( + 'noconcurrentlogins', + 'Concurrent user logins', + 'Disable Concurrent user logins', + $pconfig['noconcurrentlogins'] +))->setHelp('If enabled only the most recent login per username will be active. Subsequent logins will cause machines previously logged in with the ' . + 'same username to be disconnected.'); + +$section->addInput(new Form_Checkbox( + 'nomacfilter', + 'MAC filtering', + 'Disable MAC filtering', + $pconfig['nomacfilter'] +))->setHelp('If enabled no attempts will be made to ensure that the MAC address of clients stays the same while they are logged in. ' . + 'This is required when the MAC address of the client cannot be determined (usually because there are routers betweenpfSenseand the clients). ' . + 'If this is enabled, RADIUS MAC authentication cannot be used.'); + +$section->addInput(new Form_Checkbox( + 'passthrumacadd', + 'Pass-through MAC Auto Entry', + 'Enable Pass-through MAC automatic additions', + $pconfig['passthrumacadd'] +))->setHelp(sprintf('If this enabled a MAC passthrough entry is automatically added after the user has successfully authenticated. Users of that MAC address will ' . + 'never have to authenticate again. To remove the passthrough MAC entry you either have to log in and remove it manually from the ' . + '%s or send a POST from another system.' . + 'If this is enabled, RADIUS MAC authentication cannot be used. Also, the logout window will not be shown.', '<a href="services_captiveportal_mac.php">MAC tab</a>')); + +$section->addInput(new Form_Checkbox( + 'passthrumacaddusername', + null, + 'Enable Pass-through MAC automatic addition with username', + $pconfig['passthrumacaddusername'] +))->setHelp(sprintf('If enabled with the automatically MAC passthrough entry created, the username used during authentication will be saved. ' . + 'To remove the passthrough MAC entry you either have to log in and remove it manually from the %s or send a POST from another system.', + '<a href="services_captiveportal_mac.php">MAC tab</a>')); + +$section->addInput(new Form_Checkbox( + 'peruserbw', + 'Per-user bandwidth restriction', + 'Enable per-user bandwidth restriction', + $pconfig['peruserbw'] +)); + +$section->addInput(new Form_Input( + 'bwdefaultdn', + 'Default download (Kbit/s)', + 'number', + $pconfig['bwdefaultdn'] +)); + +$section->addInput(new Form_Input( + 'bwdefaultup', + 'Default download (Kbit/s)', + 'number', + $pconfig['bwdefaultup'] +))->setHelp('If this option is set, the captive portal will restrict each user who logs in to the specified default bandwidth. ' . + 'RADIUS can override the default settings. Leave empty or set to 0 for no limit.'); + +$form->add($section); + +$section = new Form_Section('Authentication'); +$section->addClass('Authentication'); + +$group = new Form_Group('Authentication method'); + +$group->add(new Form_Checkbox( + 'auth_method', + null, + 'No Authentication', + $pconfig['auth_method'] == 'none', + 'none' +))->displayasRadio(); + +$group->add(new Form_Checkbox( + 'auth_method', + null, + 'Local/Vouchers', + $pconfig['auth_method'] == 'local', + 'local' +))->displayasRadio()->setHelp('<a href="system_usermanager.php">User Manager</a>'); + +$group->add(new Form_Checkbox( + 'auth_method', + null, + 'RADIUS Authentication', + $pconfig['auth_method'] == 'radius', + 'radius' +))->displayasRadio(); + +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'localauth_priv', + null, + 'Allow only users/groups with "Captive portal login" privilege set', + $pconfig['localauth_priv'] +)); + +$group = new Form_Group('Radius protocol'); + +$group->add(new Form_Checkbox( + 'radius_protocol', + null, + 'PAP', + $pconfig['radius_protocol'] == 'PAP', + 'PAP' +))->displayasRadio(); + +$group->add(new Form_Checkbox( + 'radius_protocol', + null, + 'CHAP-MD5', + $pconfig['radius_protocol'] == 'CHAP_MD5', + 'CHAP_MD5' +))->displayasRadio(); + +$group->add(new Form_Checkbox( + 'radius_protocol', + null, + 'MSCHAPv1', + $pconfig['radius_protocol'] == 'MSCHAPv1', + 'MSCHAPv1' +))->displayasRadio(); + +$group->add(new Form_Checkbox( + 'radius_protocol', + null, + 'MSCHAPv2', + $pconfig['radius_protocol'] == 'MSCHAPv2', + 'SCHAPv2' +))->displayasRadio(); + +$section->add($group); + +$form->add($section); + +$section = new Form_Section('Primary Authentication Source'); +$section->addClass('Primary'); + +$group = new Form_Group('Primary RADIUS server'); + +$group->add(new Form_IpAddress( + 'radiusip', + null, + $pconfig['radiusip'] +)); + +$group->add(new Form_Input( + 'radiusport', + null, + 'number', + $pconfig['radiusport'] +)); + +$group->add(new Form_Input( + 'radiuskey', + null, + 'text', + $pconfig['radiuskey'] +)); + +$section->add($group); + +$group = new Form_Group('Secondary RADIUS server'); + +$group->add(new Form_IpAddress( + 'radiusip2', + null, + $pconfig['radiusip2'] +))->setHelp('IP address of the RADIUS server to authenticate against.'); + +$group->add(new Form_Input( + 'radiusport2', + null, + 'number', + $pconfig['radiusport2'] +))->setHelp('Leave blank for default (1812)'); + +$group->add(new Form_Input( + 'radiuskey3', + null, + 'text', + $pconfig['radiuskey3'] +))->setHelp('Leave blank to not use a RADIUS shared secret (not recommended)'); + +$section->add($group); + +$form->add($section); + +$section = new Form_Section('Secondary Authentication Source'); +$section->addClass('Secondary'); + +$group = new Form_Group('Primary RADIUS server'); + +$group->add(new Form_IpAddress( + 'radiusip4', + null, + $pconfig['radiusip4'] +)); + +$group->add(new Form_Input( + 'radiusport4', + null, + 'number', + $pconfig['radiusport4'] +)); + +$group->add(new Form_Input( + 'radiuskey4', + null, + 'text', + $pconfig['radiuskey4'] +)); + +$section->add($group); + +$group = new Form_Group('Secondary RADIUS server'); + +$group->add(new Form_IpAddress( + 'radiusip', + null, + $pconfig['radiusip'] +))->setHelp('IP address of the RADIUS server to authenticate against.'); + +$group->add(new Form_Input( + 'radiusport', + null, + 'number', + $pconfig['radiusport'] +))->setHelp('Leave blank for default (1812)'); + +$group->add(new Form_Input( + 'radiuskey', + null, + 'text', + $pconfig['radiuskey'] +))->setHelp('Leave blank to not use a RADIUS shared secret (not recommended)'); + +$section->add($group); +$form->add($section); + +$section = new Form_Section('Accounting'); +$section->addClass('Accounting'); + +$section->addInput(new Form_Checkbox( + 'radacct_enable', + 'RADIUS', + 'Send RADIUS accounting packets to the primary RADIUS server.', + $pconfig['radacct_enable'] +)); + +$section->addInput(new Form_Input( + 'radiusacctport', + 'Accounting Port', + 'text', + $pconfig['radiusacctport'] +))->setHelp('Leave blank to use the default port (1813).'); + +$group = new Form_Group('Accounting updates'); + +$group->add(new Form_Checkbox( + 'reauthenticateacct', + null, + 'No Accounting updates', + !$pconfig['reauthenticateacct'] +))->displayasRadio(); + +$group->add(new Form_Checkbox( + 'reauthenticateacct', + null, + 'Stop/stop Accounting', + $pconfig['reauthenticateacct'] == 'stopstart' +))->displayasRadio(); + +$group->add(new Form_Checkbox( + 'reauthenticateacct', + null, + 'Interim update', + $pconfig['reauthenticateacct'] == 'interimupdate' +))->displayasRadio(); + +$section->add($group); + +$form->add($section); + +$section = new Form_Section('RADIUS options'); +$section->addClass('Radius'); + +$section->addInput(new Form_Checkbox( + 'reauthenticate', + 'Reathentication', + 'Reauthenticate connected users every minute', + $pconfig['reauthenticate'] +))->setHelp('If reauthentication is enabled, Access-Requests will be sent to the RADIUS server for each user that is logged in every minute. ' . + 'If an Access-Reject is received for a user, that user is disconnected from the captive portal immediately.'); + +$section->addInput(new Form_Checkbox( + 'radmac_enable', + 'RADIUS MAC Authentication', + 'Enable RADIUS MAC authentication', + $pconfig['radmac_enable'] +))->setHelp('If this option is enabled, the captive portal will try to authenticate users by sending their MAC address as the username ' . + 'and the password entered below to the RADIUS server.'); + +$section->addInput(new Form_Input( + 'radmac_secret', + 'MAC authentication secret', + 'text', + $pconfig['radmac_secret'] +)); + +$section->addInput(new Form_Select( + 'radiussrcip_attribute', + 'RADIUS NAS IP Attribute', + $pconfig['radiussrcip_attribute'], + build_radiusnas_list() +))->setHelp('Choose the IP to use for calling station attribute.'); + +$section->addInput(new Form_Checkbox( + 'radiussession_timeout', + 'Session timeout', + 'Use RADIUS Session-Timeout attributes', + $pconfig['radiussession_timeout'] +))->setHelp('When enabled, clients will be disconnected after the amount of time retrieved from the RADIUS Session-Timeout attribute.'); + +$section->addInput(new Form_Select( + 'radiusvendor', + 'Type', + $pconfig['radiusvendor'], + ['default' => 'default', 'cisco' => 'cisco'] +))->setHelp('If RADIUS type is set to Cisco, in Access-Requests the value of Calling-Station-ID will be set to the client\'s IP address and the ' . + 'Called-Station-Id to the client\'s MAC address. Default behavior is Calling-Station-Id = client\'s MAC address and ' . + 'Called-Station-ID = pfSense\'s WAN IP address.'); + +$section->addInput(new Form_Checkbox( + 'reverseacct', + 'Accounting style', + 'Invert Acct-Input-Octets and Acct-Output-Octets', + $pconfig['reverseacct'] +))->setHelp('When enabled, data counts for RADIUS accounting packets will be taken from the client perspective, not the NAS. ' . + 'Acct-Input-Octets will represent download, and Acct-Output-Octets will represent upload.'); + +$section->addInput(new Form_Input( + 'radiusnasid', + 'NAS Identifier', + 'text', + $pconfig['radiusnasid'] +))->setHelp('Specify a NAS identifier to override the default value (pfSense.localdomain)'); + +$section->addInput(new Form_Select( + 'radmac_format', + 'MAC address format', + $pconfig['radmac_format'], + ['default' => 'Default', 'singledash' => 'Single dash', 'ietf' => 'IETF', 'cisco' => 'Cisco', 'unformatted' => 'Unformatted'] +))->setHelp('This option changes the MAC address format used in the whole RADIUS system. Change this if you also need to change the username format for ' . + 'RADIUS MAC authentication.' . '<br />' . + 'Default: 00:11:22:33:44:55' . '<br />' . + 'Single dash: 001122-334455' . '<br />' . + 'IETF: 00-11-22-33-44-55' . '<br />' . + 'Cisco: 0011.2233.4455' . '<br />' . + 'Unformatted: 001122334455'); + +$form->add($section); + +$section = new Form_Section('HTTPS options'); +$section->addClass('HTTPS'); + +$section->addInput(new Form_Checkbox( + 'httpslogin_enable', + 'Login', + 'Enable HTTPS login', + $pconfig['httpslogin_enable'] +))->setHelp('When enabled, the username and password will be transmitted over an HTTPS connection to protect against eavesdroppers. ' . + 'A server name and certificate must also be specified below.'); + +$section->addInput(new Form_Input( + 'httpsname', + 'HTTPS server name', + 'text', + $pconfig['httpsname'] +))->setHelp('This name will be used in the form action for the HTTPS POST and should match the Common Name (CN) in your certificate ' . + '(otherwise, the client browser will most likely display a security warning). ' . + 'Make sure captive portal clients can resolve this name in DNS and verify on the client that the IP resolves to the correct interface IP on pfSense.'); + +$section->addInput(new Form_Select( + 'certref', + 'SSL Certigicate', + $pconfig['certref'], + build_cert_list() +))->setHelp('If no certificates are defined, you may define one here: ' . '<a href="system_certmanager.php">System > Cert Manager</a>'); + +$section->addInput(new Form_Checkbox( + 'nohttpsforwards', + 'HTTPS Forwards', + 'Disable HTTPS Forwards', + $pconfig['nohttpsforwards'] +))->setHelp('If this option is set, attempts to connect to SSL/HTTPS (Port 443) sites will not be forwarded to the captive portal' . + 'This prevents certificate errors from being presented to the user even if HTTPS logins are enabled. ' . + 'Users must attempt a connecton to an HTTP (Port 80) site to get forwarded to the captive portal. ' . + 'If HTTPS logins are enabled, the user will be redirected to the HTTPS login page.'); + +$form->add($section); + +$section = new Form_Section('HTML page contents'); +$section->addClass('HTML'); + +$section->addInput(new Form_Input( + 'htmlfile', + 'Portal page contents', + 'file', + $pconfig['htmlfile'] +))->setHelp('Upload an HTML/PHP file for the portal page here (leave blank to keep the current one). Make sure to include a form (POST to "$PORTAL_ACTION$") ' . + 'with a submit button (name="accept") and a hidden field with name="redirurl" and value="$PORTAL_REDIRURL$". ' . + 'Include the "auth_user" and "auth_pass" and/or "auth_voucher" input fields if authentication is enabled, otherwise it will always fail.' . '<br />' . + 'Example code for the form:' . '<br />' . + '<form method="post" action="$PORTAL_ACTION$"><br /> + <input name="auth_user" type="text"><br /> + <input name="auth_pass" type="password"><br /> + <input name="auth_voucher" type="text"><br /> + <input name="redirurl" type="hidden" value="$PORTAL_REDIRURL$"><br /> + <input name="accept" type="submit" value="Continue"><br /> + </form>')->addClass('btn btn-info btn-sm'); + +if ($pconfig['page']['htmltext']) { + $section->addInput(new Form_Button( + 'btnview', + 'View current page', + $href + ))->removeClass('btn-primary')->addClass('btn btn-default btn-xs'); + + $section->addInput(new Form_Button( + 'btndownload', + 'Download current page', + '?zone=' . $cpzone . '&act=gethtmlhtml' + ))->removeClass('btn-primary')->addClass('btn btn-info btn-xs'); + + $section->addInput(new Form_Button( + 'btndownload', + 'Restore default portal page', + '?zone=' . $cpzone . '&act=delhtmlhtml' + ))->removeClass('btn-primary')->addClass('btn btn-danger btn-xs'); +} + +$section->addInput(new Form_Input( + 'errfile', + 'Auth error page contents', + 'file', + $pconfig['errfile'] +))->setHelp('The contents of the HTML/PHP file that you upload here are displayed when an authentication error occurs. ' . + 'You may include "$PORTAL_MESSAGE$", which will be replaced by the error or reply messages from the RADIUS ' . + 'server, if any.')->addClass('btn btn-info btn-sm'); + +if ($pconfig['page']['errtext']) { + $section->addInput(new Form_Button( + 'btnview', + 'View current page', + '?zone=' . $cpzone . '&act=viewerrhtml' + ))->removeClass('btn-primary')->addClass('btn btn-default btn-xs'); + + $section->addInput(new Form_Button( + 'btndownload', + 'Download current page', + '?zone=' . $cpzone . '&act=geterrhtml' + ))->removeClass('btn-primary')->addClass('btn btn-info btn-xs'); + + $section->addInput(new Form_Button( + 'btndownload', + 'Restore default portal page', + '?zone=' . $cpzone . '&act=delerrhtml' + ))->removeClass('btn-primary')->addClass('btn btn-danger btn-xs'); +} + +$section->addInput(new Form_Input( + 'logoutfile', + 'Logout page contents', + 'file', + $pconfig['logoutfile'] +))->setHelp('The contents of the HTML/PHP file that you upload here are displayed on authentication success when the logout popup is enabled.')->addClass('btn btn-info btn-sm'); + +if ($pconfig['page']['logouttext']) { + $section->addInput(new Form_Button( + 'btnview', + 'View current page', + '?zone=' . $cpzone . '&act=viewlogouthtml' + ))->removeClass('btn-primary')->addClass('btn btn-default btn-xs'); + + $section->addInput(new Form_Button( + 'btndownload', + 'Download current page', + '?zone=' . $cpzone . '&act=getlogouthtml' + ))->removeClass('btn-primary')->addClass('btn btn-info btn-xs'); + + $section->addInput(new Form_Button( + 'btndownload', + 'Restore default portal page', + '?zone=' . $cpzone . '&act=dellogouthtml' + ))->removeClass('btn-primary')->addClass('btn btn-danger btn-xs'); +} +$section->addInput(new Form_Input( + 'zone', + null, + 'hidden', + $cpzone +)); + +$form->add($section); +print($form); + +print_info_box(gettext('Warning:' . '<br />' . 'Changing any settings on this page will disconnect all clients! ' . + 'Don\'t forget to enable the DHCP server on your captive portal interface! ' . + 'Make sure that the default/maximum DHCP lease time is higher than the timeout entered on this page. ' . + 'Also, the DNS forwarder needs to be enabled for DNS lookups by unauthenticated clients to work.')); + +?> + +<script type="text/javascript"> +//<![CDATA[ +events.push(function(){ + //---------- "Standard" show/hide functions --------------------------------------------------- + + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // Hides all elements of the specified class belonging to a multiselect. + function hideMultiClass(s_class, hide) { + if(hide) + $('.' + s_class).parent().parent().hide(); + else + $('.' + s_class).parent().parent().show(); + } + + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified checkbox lives so that the checkbox, its label and help text are hidden + function hideCheckbox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // ------- Show/hide sections based on checkbox settings -------------------------------------- + function hideSections(hide) { + hideClass('Authentication', hide); + hideRadius(); + hideHTTPS(); + hideClass('HTTPS', hide); + hideClass('HTML', hide); + hideGeneral(hide) + } + + function hideRadius() { + hide = (!$('#enable').prop('checked') || (!($('input[name="auth_method"]:checked').val() == 'radius'))); + + hideClass('Primary', hide); + hideClass('Secondary', hide); + hideClass('Accounting', hide); + hideClass('Radius', hide); + + disableInput('localauth_priv', !($('input[name="auth_method"]:checked').val() == 'local')); + } + + function hideHTTPS() { + hide = (!$('#httpslogin_enable').prop('checked') || !$('#enable').prop('checked')); + + hideInput('httpsname', hide); + hideInput('certref', hide); + hideCheckbox('nohttpsforwards', hide); + } + + function hideGeneral(hide) { + hideMultiClass('general', hide); + hideInput('maxprocperip', hide); + hideInput('idletimeout', hide); + hideInput('timeout', hide); + hideInput('freelogins_count', hide); + hideCheckbox('freelogins_updatetimeouts', hide); + hideCheckbox('logoutwin_enable', hide); + hideInput('preauthurl', hide); + hideInput('redirurl', hide); + hideInput('blockedmacsurl', hide); + hideCheckbox('noconcurrentlogins', hide); + hideCheckbox('nomacfilter', hide); + hideCheckbox('passthrumacadd', hide); + hideCheckbox('passthrumacaddusername', hide); + hideCheckbox('peruserbw', hide); + hideInput('bwdefaultdn', hide); + hideInput('bwdefaultup', hide); + } + + // ---------- Click checkbox handlers --------------------------------------------------------- + $("#enable").click(function() { + hideSections(!this.checked); + }); + + $('input[name="auth_method"]').on('change', function() { + hideRadius(); + }); + + + $("#httpslogin_enable").click(function() { + hideHTTPS(!this.checked); + }); + + // ---------- On itial page load -------------------------------------------------------------- + hideSections(!$('#enable').prop('checked')); + disableInput('localauth_priv', !($('input[name="auth_method"]:checked').val() == 'local')); +}); +//]]> +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_captiveportal_filemanager.php b/src/usr/local/www/services_captiveportal_filemanager.php new file mode 100644 index 0000000..afd1610 --- /dev/null +++ b/src/usr/local/www/services_captiveportal_filemanager.php @@ -0,0 +1,290 @@ +<?php +/* + services_captiveportal_filemanager.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2005-2006 Jonathan De Graeve (jonathan.de.graeve@imelda.be) + and Paul Taylor (paultaylor@winn-dixie.com). + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-services-captiveportal-filemanager +##|*NAME=Services: Captive portal: File Manager page +##|*DESCR=Allow access to the 'Services: Captive portal: File Manager' page. +##|*MATCH=services_captiveportal_filemanager.php* +##|-PRIV + +function cpelementscmp($a, $b) { + return strcasecmp($a['name'], $b['name']); +} + +function cpelements_sort() { + global $config, $cpzone; + + usort($config['captiveportal'][$cpzone]['element'], "cpelementscmp"); +} + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + +if (empty($cpzone)) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +$pgtitle = array(gettext("Services"), gettext("Captive portal"), $a_cp[$cpzone]['zone']); +$shortcut_section = "captiveportal"; + +if (!is_array($a_cp[$cpzone]['element'])) { + $a_cp[$cpzone]['element'] = array(); +} +$a_element =& $a_cp[$cpzone]['element']; + +// Calculate total size of all files +$total_size = 0; +foreach ($a_element as $element) { + $total_size += $element['size']; +} + +if ($_POST) { + unset($input_errors); + + if (is_uploaded_file($_FILES['new']['tmp_name'])) { + + if (!stristr($_FILES['new']['name'], "captiveportal-")) { + $name = "captiveportal-" . $_FILES['new']['name']; + } else { + $name = $_FILES['new']['name']; + } + $size = filesize($_FILES['new']['tmp_name']); + + // is there already a file with that name? + foreach ($a_element as $element) { + if ($element['name'] == $name) { + $input_errors[] = sprintf(gettext("A file with the name '%s' already exists."), $name); + break; + } + } + + // check total file size + if (($total_size + $size) > $g['captiveportal_element_sizelimit']) { + $input_errors[] = gettext("The total size of all files uploaded may not exceed ") . + format_bytes($g['captiveportal_element_sizelimit']) . "."; + } + + if (!$input_errors) { + $element = array(); + $element['name'] = $name; + $element['size'] = $size; + $element['content'] = base64_encode(file_get_contents($_FILES['new']['tmp_name'])); + + $a_element[] = $element; + cpelements_sort(); + + write_config(); + captiveportal_write_elements(); + header("Location: services_captiveportal_filemanager.php?zone={$cpzone}"); + exit; + } + } +} else if (($_GET['act'] == "del") && !empty($cpzone) && $a_element[$_GET['id']]) { + conf_mount_rw(); + @unlink("{$g['captiveportal_element_path']}/" . $a_element[$_GET['id']]['name']); + @unlink("{$g['captiveportal_path']}/" . $a_element[$_GET['id']]['name']); + conf_mount_ro(); + unset($a_element[$_GET['id']]); + write_config(); + header("Location: services_captiveportal_filemanager.php?zone={$cpzone}"); + exit; +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +$tab_array = array(); +$tab_array[] = array(gettext("Captive portal(s)"), false, "services_captiveportal.php?zone={$cpzone}"); +$tab_array[] = array(gettext("MAC"), false, "services_captiveportal_mac.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Allowed IP addresses"), false, "services_captiveportal_ip.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Allowed Hostnames"), false, "services_captiveportal_hostname.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Vouchers"), false, "services_captiveportal_vouchers.php?zone={$cpzone}"); +$tab_array[] = array(gettext("File Manager"), true, "services_captiveportal_filemanager.php?zone={$cpzone}"); +display_top_tabs($tab_array, true); + +require('classes/Form.class.php'); + +if ($_GET['act'] == 'add') { + + $form = new Form(new Form_Button( + 'Submit', + 'Upload' + )); + + $form->setMultipartEncoding(); + + $section = new Form_Section('Upload a new file'); + + $section->addInput(new Form_Input( + 'zone', + null, + 'hidden', + $cpzone + )); + + $section->addInput(new Form_Input( + 'new', + 'File', + 'file' + )); + + + $form->add($section); + print($form); +} + +if (is_array($a_cp[$cpzone]['element'])): +?> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Installed Files")?></h2></div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Name"); ?></th> + <th><?=gettext("Size"); ?></th> + <th> + <!-- Buttons --> + </th> + </tr> + </thead> + <tbody> +<?php + $i = 0; + foreach ($a_cp[$cpzone]['element'] as $element): +?> + <tr> + <td><?=htmlspecialchars($element['name'])?></td> + <td><?=format_bytes($element['size'])?></td> + <td> + <a href="services_captiveportal_filemanager.php?zone=<?=$cpzone?>&act=del&id=<?=$i?>" class="btn btn-xs btn-danger">Delete</a> + </td> + </tr> +<?php + $i++; + endforeach; + + if($total_size > 0) : +?> + <tr> + <th> + Total + </th> + <th> + <?=format_bytes($total_size);?> + </th> + <th></th> + </tr> +<?php endif; ?> + </tbody> + </table> + </div> + </div> + </div> +<?php +endif; + +?> + <nav class="action-buttons"> + <button id="btnnotes" class="btn btn-default">Show Notes</button> +<?php if (!$_GET['act'] == 'add'): ?> + <a href="services_captiveportal_filemanager.php?zone=<?=$cpzone?>&act=add" class="btn btn-success">Add</a> +<?php endif; ?> + </nav> +<?php +// The notes displayed on the page are large, the page content comparitively small. A "Note" button +// is provided so that you only see the notes if you ask for them +?> +<div class="help-block panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">Notes</h2></div> + <div class="panel-body"> + <?=gettext("Any files that you upload here with the filename prefix of captiveportal- will " . + "be made available in the root directory of the captive portal HTTP(S) server. " . + "You may reference them directly from your portal page HTML code using relative paths. " . + "Example: you've uploaded an image with the name 'captiveportal-test.jpg' using the " . + "file manager. Then you can include it in your portal page like this:")?><br /><br /> + <pre><img src="captiveportal-test.jpg" width=... height=...></pre><br /><br /> + <?=gettext("In addition, you can also upload .php files for execution. You can pass the filename " . + "to your custom page from the initial page by using text similar to:")?><br /><br /> + <pre><a href="/captiveportal-aup.php?zone=$PORTAL_ZONE$&redirurl=$PORTAL_REDIRURL$"><?=gettext("Acceptable usage policy"); ?></a></pre><br /><br /> + <?=sprintf(gettext("The total size limit for all files is %s."), format_bytes($g['captiveportal_element_sizelimit']))?> + </div> +</div> + +<script> +//<![CDATA[ +events.push(function(){ + + var hidenotes = true; + + // Hides all elements of the specified class. + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + hideClass('help-block', hidenotes); + + $(function () { + $('#btnnotes').on('click', function () { + hidenotes = !hidenotes; + hideClass('notes', hidenotes); + }); + }); +}); +</script> +<?php + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_captiveportal_hostname.php b/src/usr/local/www/services_captiveportal_hostname.php new file mode 100644 index 0000000..0256fc2 --- /dev/null +++ b/src/usr/local/www/services_captiveportal_hostname.php @@ -0,0 +1,178 @@ +<?php +/* + services_captiveportal_hostname.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2011 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2004 Dinesh Nair <dinesh@alphaque.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ipfw + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-services-captiveportal-allowedhostnames +##|*NAME=Services: Captive portal: Allowed Hostnames page +##|*DESCR=Allow access to the 'Services: Captive portal: Allowed Hostnames' page. +##|*MATCH=services_captiveportal_hostname.php* +##|-PRIV + +$directionicons = array('to' => '→', 'from' => '←', 'both' => '⇄'); + +$notestr = + gettext('Adding new hostnames will allow a DNS hostname access to/from the captive portal without being taken to the portal page.' . + 'This can be used for a web server serving images for the portal page, or a DNS server on another network, for example. ' . + 'By specifying <em>from</em> addresses, it may be used to always allow pass-through access from a client behind the captive portal.'); + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + +if (empty($cpzone) || empty($config['captiveportal'][$cpzone])) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +if (isset($cpzone) && !empty($cpzone) && isset($a_cp[$cpzone]['zoneid'])) { + $cpzoneid = $a_cp[$cpzone]['zoneid']; +} + +$pgtitle = array(gettext("Services"), gettext("Captive portal"), $a_cp[$cpzone]['zone']); +$shortcut_section = "captiveportal"; + +if ($_GET['act'] == "del" && !empty($cpzone) && isset($cpzoneid)) { + $a_allowedhostnames =& $a_cp[$cpzone]['allowedhostname']; + if ($a_allowedhostnames[$_GET['id']]) { + $ipent = $a_allowedhostnames[$_GET['id']]; + + if (isset($a_cp[$cpzone]['enable'])) { + if (is_ipaddr($ipent['hostname'])) { + $ip = $ipent['hostname']; + } else { + $ip = gethostbyname($ipent['hostname']); + } + $sn = (is_ipaddrv6($ip)) ? 128 : 32; + if (is_ipaddr($ip)) { + $ipfw = pfSense_ipfw_getTablestats($cpzoneid, IP_FW_TABLE_XLISTENTRY, 3, $ip); + if (is_array($ipfw)) { + captiveportal_free_dn_ruleno($ipfw['dnpipe']); + pfSense_pipe_action("pipe delete {$ipfw['dnpipe']}"); + pfSense_pipe_action("pipe delete " . ($ipfw['dnpipe']+1)); + } + + pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XDEL, 3, $ip, $sn); + pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XDEL, 4, $ip, $sn); + } + } + + unset($a_allowedhostnames[$_GET['id']]); + write_config(); + captiveportal_allowedhostname_configure(); + header("Location: services_captiveportal_hostname.php?zone={$cpzone}"); + exit; + } +} + +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg); + +$tab_array = array(); +$tab_array[] = array(gettext("Captive portal(s)"), false, "services_captiveportal.php?zone={$cpzone}"); +$tab_array[] = array(gettext("MAC"), false, "services_captiveportal_mac.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Allowed IP Addresses"), false, "services_captiveportal_ip.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Allowed Hostnames"), true, "services_captiveportal_hostname.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Vouchers"), false, "services_captiveportal_vouchers.php?zone={$cpzone}"); +$tab_array[] = array(gettext("File Manager"), false, "services_captiveportal_filemanager.php?zone={$cpzone}"); +display_top_tabs($tab_array, true); +?> +<div class="table-responsive"> + <table class="table table-hover table-striped table-condensed"> + <thead> + <tr> + <th><?=gettext("Hostname"); ?></th> + <th><?=gettext("Description"); ?></th> + <th><!-- Buttons --></th> + </tr> + </thead> + +<?php +if (is_array($a_cp[$cpzone]['allowedhostname'])): ?> + <tbody> +<?php +$i = 0; +foreach ($a_cp[$cpzone]['allowedhostname'] as $ip): ?> + <tr> + <td> + <?=$directionicons[$ip['dir']]?> <?=strtolower($ip['hostname'])?> + </td> + <td > + <?=htmlspecialchars($ip['descr'])?> + </td> + <td> + <a href="services_captiveportal_hostname_edit.php?zone=<?=$cpzone?>&id=<?=$i?>" class="btn btn-xs btn-info">Edit</a> + <a href="services_captiveportal_hostname.php?zone=<?=$cpzone?>&act=del&id=<?=$i?>" class="btn btn-xs btn-danger">Delete</a> + </td> + </tr> +<?php +$i++; +endforeach; ?> + <tbody> + </table> + <?=$directionicons['to'] . ' = ' . sprintf(gettext('All connections %sto%s the hostname are allowed'), '<u>','</u>') . ', '?> + <?=$directionicons['from'] . ' = ' . sprintf(gettext('All connections %sfrom%s the hostname are allowed'), '<u>','</u>') . ', '?> + <?=$directionicons['both'] . ' = ' . sprintf(gettext('All connections %sto or from%s are allowed'), '<u>','</u>')?> +<?php +else : +?> + </tbody> + </table> +<?php +endif; +?> + <nav class="action-buttons"> + <a href="services_captiveportal_hostname_edit.php?zone=<?=$cpzone?>&act=add" class="btn btn-success">Add</a> + </nav> +</div> +<?php +print_info_box($notestr); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_captiveportal_hostname_edit.php b/src/usr/local/www/services_captiveportal_hostname_edit.php new file mode 100644 index 0000000..2bd49ff --- /dev/null +++ b/src/usr/local/www/services_captiveportal_hostname_edit.php @@ -0,0 +1,247 @@ +<?php +/* + services_captiveportal_hostname_edit.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2011 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2004 Dinesh Nair <dinesh@alphaque.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ipfw + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-services-captiveportal-editallowedhostnames +##|*NAME=Services: Captive portal: Edit Allowed Hostnames page +##|*DESCR=Allow access to the 'Services: Captive portal: Edit Allowed Hostnames' page. +##|*MATCH=services_captiveportal_hostname_edit.php* +##|-PRIV + +function allowedhostnamescmp($a, $b) { + return strcmp($a['hostname'], $b['hostname']); +} + +function allowedhostnames_sort() { + global $g, $config, $cpzone; + usort($config['captiveportal'][$cpzone]['allowedhostname'], "allowedhostnamescmp"); +} + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); + +global $cpzone, $cpzoneid; + +$pgtitle = array(gettext("Services"), gettext("Captive portal"), gettext("Edit allowed Hostname")); +$shortcut_section = "captiveportal"; + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} +$cpzoneid = $config['captiveportal'][$cpzone]['zoneid']; + +if (empty($cpzone) || empty($config['captiveportal'][$cpzone])) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (!is_array($a_cp[$cpzone]['allowedhostname'])) { + $a_cp[$cpzone]['allowedhostname'] = array(); +} +$a_allowedhostnames = &$a_cp[$cpzone]['allowedhostname']; + +if (isset($id) && $a_allowedhostnames[$id]) { + $pconfig['zone'] = $a_allowedhostnames[$id]['zone']; + $pconfig['hostname'] = $a_allowedhostnames[$id]['hostname']; + $pconfig['sn'] = $a_allowedhostnames[$id]['sn']; + $pconfig['dir'] = $a_allowedhostnames[$id]['dir']; + $pconfig['bw_up'] = $a_allowedhostnames[$id]['bw_up']; + $pconfig['bw_down'] = $a_allowedhostnames[$id]['bw_down']; + $pconfig['descr'] = $a_allowedhostnames[$id]['descr']; +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "hostname"); + $reqdfieldsn = array(gettext("Allowed Hostname")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($_POST['hostname'] && !is_hostname($_POST['hostname']))) { + $input_errors[] = sprintf(gettext("A valid Hostname must be specified. [%s]"), $_POST['hostname']); + } + + if ($_POST['bw_up'] && !is_numeric($_POST['bw_up'])) { + $input_errors[] = gettext("Upload speed needs to be an integer"); + } + if ($_POST['bw_down'] && !is_numeric($_POST['bw_down'])) { + $input_errors[] = gettext("Download speed needs to be an integer"); + } + + foreach ($a_allowedhostnames as $ipent) { + if (isset($id) && ($a_allowedhostnames[$id]) && ($a_allowedhostnames[$id] === $ipent)) { + continue; + } + + if ($ipent['hostname'] == $_POST['hostname']) { + $input_errors[] = sprintf("[%s] %s.", $_POST['hostname'], gettext("already allowed")) ; + break ; + } + } + + if (!$input_errors) { + $ip = array(); + $ip['hostname'] = $_POST['hostname']; + $ip['sn'] = $_POST['sn']; + $ip['dir'] = $_POST['dir']; + $ip['descr'] = $_POST['descr']; + if ($_POST['bw_up']) { + $ip['bw_up'] = $_POST['bw_up']; + } + if ($_POST['bw_down']) { + $ip['bw_down'] = $_POST['bw_down']; + } + if (isset($id) && $a_allowedhostnames[$id]) { + $a_allowedhostnames[$id] = $ip; + } else { + $a_allowedhostnames[] = $ip; + } + + allowedhostnames_sort(); + + write_config(); + + $rules = captiveportal_allowedhostname_configure(); + @file_put_contents("{$g['tmp_path']}/hostname_rules", $rules); + mwexec("/sbin/ipfw -x {$cpzoneid} {$g['tmp_path']}/hostname_rules", true); + unset($rules); + + header("Location: services_captiveportal_hostname.php?zone={$cpzone}"); + exit; + } +} + +function build_dir_list() { + $dirs = array(gettext("Both"),gettext("From"),gettext("To")); + $dirlist = array(); + + foreach ($dirs as $dir) { + $dirlist[strtolower($dir)] = $dir; + } + + return($dirlist); +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(new Form_Button( + 'Submit', + gettext("Save") +)); + +$section = new Form_Section('Captive Portal Hostname settings'); + +$section->addInput(new Form_Select( + 'dir', + 'Direction', + strtolower($pconfig['dir']), + build_dir_list() +))->setHelp('Use "From" to always allow an Hostname through the captive portal (without authentication). ' . + 'Use "To" to allow access from all clients (even non-authenticated ones) behind the portal to this Hostname.'); + +$section->addInput(new Form_Input( + 'hostname', + 'Hostname', + 'text', + $pconfig['hostname'] +)); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$section->addInput(new Form_Input( + 'bw_up', + 'Bandwidth up', + 'text', + $pconfig['bw_up'] +))->setHelp('Enter a upload limit to be enforced on this Hostname in Kbit/s'); + +$section->addInput(new Form_Input( + 'bw_down', + 'Bandwidth down', + 'text', + $pconfig['bw_down'] +))->setHelp('Enter a download limit to be enforced on this Hostname in Kbit/s'); + +$section->addInput(new Form_Input( + 'zone', + null, + 'hidden', + $cpzone +)); + +if (isset($id) && $a_allowedhostnames[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_captiveportal_ip.php b/src/usr/local/www/services_captiveportal_ip.php new file mode 100644 index 0000000..dd449c4 --- /dev/null +++ b/src/usr/local/www/services_captiveportal_ip.php @@ -0,0 +1,171 @@ +<?php +/* + services_captiveportal_ip.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2004 Dinesh Nair <dinesh@alphaque.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ipfw + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-services-captiveportal-allowedips +##|*NAME=Services: Captive portal: Allowed IPs page +##|*DESCR=Allow access to the 'Services: Captive portal: Allowed IPs' page. +##|*MATCH=services_captiveportal_ip.php* +##|-PRIV + +$directionicons = array('to' => '→', 'from' => '←', 'both' => '⇄'); + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + +if (empty($cpzone) || empty($config['captiveportal'][$cpzone])) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +if (isset($cpzone) && !empty($cpzone) && isset($a_cp[$cpzone]['zoneid'])) { + $cpzoneid = $a_cp[$cpzone]['zoneid']; +} + +$pgtitle = array(gettext("Services"), gettext("Captive portal"), $a_cp[$cpzone]['zone']); +$shortcut_section = "captiveportal"; + +if ($_GET['act'] == "del" && !empty($cpzone) && isset($cpzoneid)) { + $a_allowedips =& $config['captiveportal'][$cpzone]['allowedip']; + + if ($a_allowedips[$_GET['id']]) { + $ipent = $a_allowedips[$_GET['id']]; + + if (isset($config['captiveportal'][$cpzone]['enable'])) { + $mask = (!empty($ipent['sn'])) ? $ipent['sn'] : 32; + + $ipfw = pfSense_ipfw_getTablestats($cpzoneid, IP_FW_TABLE_XLISTENTRY, 3, $ipent['ip']); + pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XDEL, 3, $ipent['ip'], $mask); + pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XDEL, 4, $ipent['ip'], $mask); + + if (is_array($ipfw)) { + captiveportal_free_dn_ruleno($ipfw['dnpipe']); + pfSense_pipe_action("pipe delete {$ipfw['dnpipe']}"); + pfSense_pipe_action("pipe delete " . ($ipfw['dnpipe']+1)); + } + } + + unset($a_allowedips[$_GET['id']]); + write_config(); + header("Location: services_captiveportal_ip.php?zone={$cpzone}"); + exit; + } +} + +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg); + +$tab_array = array(); +$tab_array[] = array(gettext("Captive portal(s)"), false, "services_captiveportal.php?zone={$cpzone}"); +$tab_array[] = array(gettext("MAC"), false, "services_captiveportal_mac.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Allowed IP Addresses"), true, "services_captiveportal_ip.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Allowed Hostnames"), false, "services_captiveportal_hostname.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Vouchers"), false, "services_captiveportal_vouchers.php?zone={$cpzone}"); +$tab_array[] = array(gettext("File Manager"), false, "services_captiveportal_filemanager.php?zone={$cpzone}"); +display_top_tabs($tab_array, true); + +?> +<div class="table-responsive"> + <table class="table table-hover table-striped table-condensed"> + <thead> + <tr> + <th><?=gettext("IP Addresses"); ?></th> + <th><?=gettext("Description"); ?></th> + <th><!-- Buttons --></th> + </tr> + </thead> + +<?php +if (is_array($a_cp[$cpzone]['allowedip'])): ?> + <tbody> +<?php + $i = 0; + foreach ($a_cp[$cpzone]['allowedip'] as $ip): ?> + <tr> + <td> + <?=$directionicons[$ip['dir']]?> <?=$ip['ip']?> + <?=($ip['sn'] != "32" && is_numeric($ip['sn'])) ? '/' . $ip['sn'] : ''?> + </td> + <td > + <?=htmlspecialchars($ip['descr'])?> + </td> + <td> + <a href="services_captiveportal_ip_edit.php?zone=<?=$cpzone?>&id=<?=$i?>" class="btn btn-xs btn-info">Edit</a> + <a href="services_captiveportal_ip.php?zone=<?=$cpzone?>&act=del&id=<?=$i?>" class="btn btn-xs btn-danger">Delete</a> + </td> + </tr> +<?php + $i++; + endforeach; ?> + <tbody> + </table> + + <?=$directionicons['to'] . ' = ' . sprintf(gettext('All connections %sto%s the address are allowed'), '<u>','</u>') . ', '?> + <?=$directionicons['from'] . ' = ' . sprintf(gettext('All connections %sfrom%s the address are allowed'), '<u>','</u>') . ', '?> + <?=$directionicons['both'] . ' = ' . sprintf(gettext('All connections %sto or from%s are allowed'), '<u>','</u>')?> +<?php +else : +?> + </tbody> + </table> +<?php +endif; +?> +</div> + +<nav class="action-buttons"> + <a href="services_captiveportal_ip_edit.php?zone=<?=$cpzone?>&act=add" class="btn btn-success">Add</a> +</nav> + +<?php +print_info_box(gettext('Adding allowed IP addresses will allow IP access to/from these addresses through the captive portal without being taken to the portal page. ' . + 'This can be used for a web server serving images for the portal page or a DNS server on another network, for example.')); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_captiveportal_ip_edit.php b/src/usr/local/www/services_captiveportal_ip_edit.php new file mode 100644 index 0000000..0db6df5 --- /dev/null +++ b/src/usr/local/www/services_captiveportal_ip_edit.php @@ -0,0 +1,263 @@ +<?php +/* + services_captiveportal_ip_edit.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2011 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2004 Dinesh Nair <dinesh@alphaque.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/ipfw + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-services-captiveportal-editallowedips +##|*NAME=Services: Captive portal: Edit Allowed IPs page +##|*DESCR=Allow access to the 'Services: Captive portal: Edit Allowed IPs' page. +##|*MATCH=services_captiveportal_ip_edit.php* +##|-PRIV + +function allowedipscmp($a, $b) { + return strcmp($a['ip'], $b['ip']); +} + +function allowedips_sort() { + global $g, $config, $cpzone; + + usort($config['captiveportal'][$cpzone]['allowedip'], "allowedipscmp"); +} + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); + +$pgtitle = array(gettext("Services"), gettext("Captive portal"), gettext("Edit allowed IP address")); +$shortcut_section = "captiveportal"; + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + +if (empty($cpzone) || empty($config['captiveportal'][$cpzone])) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (!is_array($config['captiveportal'][$cpzone]['allowedip'])) { + $config['captiveportal'][$cpzone]['allowedip'] = array(); +} +$a_allowedips =& $config['captiveportal'][$cpzone]['allowedip']; + +if (isset($id) && $a_allowedips[$id]) { + $pconfig['ip'] = $a_allowedips[$id]['ip']; + $pconfig['sn'] = $a_allowedips[$id]['sn']; + $pconfig['bw_up'] = $a_allowedips[$id]['bw_up']; + $pconfig['bw_down'] = $a_allowedips[$id]['bw_down']; + $pconfig['descr'] = $a_allowedips[$id]['descr']; +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "ip sn"); + $reqdfieldsn = array(gettext("Allowed IP address"), gettext("Subnet mask")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ($_POST['ip'] && !is_ipaddr($_POST['ip'])) { + $input_errors[] = sprintf(gettext("A valid IP address must be specified. [%s]"), $_POST['ip']); + } + + if ($_POST['sn'] && (!is_numeric($_POST['sn']) || ($_POST['sn'] < 1) || ($_POST['sn'] > 32))) { + $input_errors[] = gettext("A valid subnet mask must be specified"); + } + + if ($_POST['bw_up'] && !is_numeric($_POST['bw_up'])) { + $input_errors[] = gettext("Upload speed needs to be an integer"); + } + + if ($_POST['bw_down'] && !is_numeric($_POST['bw_down'])) { + $input_errors[] = gettext("Download speed needs to be an integer"); + } + + foreach ($a_allowedips as $ipent) { + if (isset($id) && ($a_allowedips[$id]) && ($a_allowedips[$id] === $ipent)) { + continue; + } + + if ($ipent['ip'] == $_POST['ip']) { + $input_errors[] = sprintf("[%s] %s.", $_POST['ip'], gettext("already allowed")) ; + break ; + } + } + + if (!$input_errors) { + $ip = array(); + $ip['ip'] = $_POST['ip']; + $ip['sn'] = $_POST['sn']; + $ip['descr'] = $_POST['descr']; + if ($_POST['bw_up']) { + $ip['bw_up'] = $_POST['bw_up']; + } + if ($_POST['bw_down']) { + $ip['bw_down'] = $_POST['bw_down']; + } + if (isset($id) && $a_allowedips[$id]) { + $oldip = $a_allowedips[$id]['ip']; + if (!empty($a_allowedips[$id]['sn'])) { + $oldmask = $a_allowedips[$id]['sn']; + } else { + $oldmask = 32; + } + $a_allowedips[$id] = $ip; + } else { + $a_allowedips[] = $ip; + } + + allowedips_sort(); + + write_config(); + + if (isset($a_cp[$cpzone]['enable']) && is_module_loaded("ipfw.ko")) { + $rules = ""; + $cpzoneid = $a_cp[$cpzone]['zoneid']; + unset($ipfw); + if (isset($oldip) && isset($oldmask)) { + $ipfw = pfSense_ipfw_getTablestats($cpzoneid, IP_FW_TABLE_XLISTENTRY, 3, $oldip); + $rules .= "table 3 delete {$oldip}/{$oldmask}\n"; + $rules .= "table 4 delete {$oldip}/{$oldmask}\n"; + if (is_array($ipfw)) { + $rules .= "pipe delete {$ipfw['dnpipe']}\n"; + $rules .= "pipe delete " . ($ipfw['dnpipe']+1 . "\n"); + } + } + + $rules .= captiveportal_allowedip_configure_entry($ip); + if (is_array($ipfw)) { + captiveportal_free_dn_ruleno($ipfw['dnpipe']); + } + + $uniqid = uniqid("{$cpzone}_allowed"); + @file_put_contents("{$g['tmp_path']}/{$uniqid}_tmp", $rules); + mwexec("/sbin/ipfw -x {$cpzoneid} -q {$g['tmp_path']}/{$uniqid}_tmp"); + @unlink("{$g['tmp_path']}/{$uniqid}_tmp"); + } + + header("Location: services_captiveportal_ip.php?zone={$cpzone}"); + exit; + } +} + +function build_dir_list() { + $dirs = array(gettext("Both"),gettext("From"),gettext("To")); + $dirlist = array(); + + foreach ($dirs as $dir) { + $dirlist[strtolower($dir)] = $dir; + } + + return($dirlist); +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Edit Captive Portal IP rule'); + +$section->addInput(new Form_IpAddress( + 'ip', + 'IP Address', + $pconfig['ip'] +))->addMask(sn, $pconfig['sn'], 32); + + +$section->addInput(new Form_Select( + 'dir', + 'Direction', + strtolower($pconfig['dir']), + build_dir_list() +))->setHelp('Use "From" to always allow aaccess to an address through the captive portal (without authentication). ' . + 'Use "To" to allow access from all clients (even non-authenticated ones) behind the portal to this Hostname.'); + +$section->addInput(new Form_Input( + 'bw_up', + 'Bandwidth up', + 'text', + $pconfig['bw_up'] +))->setHelp('Enter an upload limit to be enforced on this address in Kbit/s'); + +$section->addInput(new Form_Input( + 'bw_down', + 'Bandwidth down', + 'text', + $pconfig['bw_down'] +))->setHelp('Enter a download limit to be enforced on this address in Kbit/s'); + +$section->addInput(new Form_Input( + 'zone', + null, + 'hidden', + $cpzone +)); + +if (isset($id) && $a_allowedips[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_captiveportal_mac.php b/src/usr/local/www/services_captiveportal_mac.php new file mode 100644 index 0000000..fdf82d3 --- /dev/null +++ b/src/usr/local/www/services_captiveportal_mac.php @@ -0,0 +1,231 @@ +<?php +/* + services_captiveportal_mac.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2004 Dinesh Nair <dinesh@alphaque.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-services-captiveportal-macaddresses +##|*NAME=Services: Captive portal: Mac Addresses page +##|*DESCR=Allow access to the 'Services: Captive portal: Mac Addresses' page. +##|*MATCH=services_captiveportal_mac.php* +##|-PRIV + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); + +global $cpzone; +global $cpzoneid; + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + + +if (empty($cpzone) || empty($config['captiveportal'][$cpzone])) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +$pgtitle = array(gettext("Services"), gettext("Captive portal"), $a_cp[$cpzone]['zone']); +$shortcut_section = "captiveportal"; + +$actsmbl = array('pass' => '<font color="green" size="4">✔</font> Pass', + 'block' => '<font color="red" size="4">✘</font> Block'); + +if ($_POST) { + $pconfig = $_POST; + + if ($_POST['apply']) { + $retval = 0; + + if (is_array($a_cp[$cpzone]['passthrumac'])) { + $cpzoneid = $a_cp[$cpzone]['cpzoneid']; + $rules = captiveportal_passthrumac_configure(); + if (!empty($rules)) { + @file_put_contents("{$g['tmp_path']}/passthrumac_gui", $rules); + mwexec("/sbin/ipfw -x {$cpzoneid} {$g['tmp_path']}/passthrumac_gui"); + @unlink("{$g['tmp_path']}/passthrumac_gui"); + } + $savemsg = get_std_save_message($retval); + if ($retval == 0) { + clear_subsystem_dirty('passthrumac'); + } + } + } + + if ($_POST['postafterlogin']) { + if (!is_array($a_passthrumacs)) { + echo gettext("No entry exists yet!") ."\n"; + exit; + } + + if (empty($_POST['zone'])) { + echo gettext("Please set the zone on which the operation should be allowed"); + exit; + } + if (!is_array($a_cp[$cpzone]['passthrumac'])) { + $a_cp[$cpzone]['passthrumac'] = array(); + } + $a_passthrumacs =& $a_cp[$cpzone]['passthrumac']; + + if ($_POST['username']) { + $mac = captiveportal_passthrumac_findbyname($_POST['username']); + if (!empty($mac)) { + $_POST['delmac'] = $mac['mac']; + } else { + echo gettext("No entry exists for this username:") . " " . $_POST['username'] . "\n"; + } + } + + if ($_POST['delmac']) { + $found = false; + foreach ($a_passthrumacs as $idx => $macent) { + if ($macent['mac'] == $_POST['delmac']) { + $found = true; + break; + } + } + if ($found == true) { + $cpzoneid = $a_cp[$cpzone]['zoneid']; + $rules = captiveportal_passthrumac_delete_entry($a_passthrumacs[$idx]); + $uniqid = uniqid("{$cpzone}_mac"); + file_put_contents("{$g['tmp_path']}/{$uniqid}_tmp", $rules); + mwexec("/sbin/ipfw -x {$cpzoneid} -q {$g['tmp_path']}/{$uniqid}_tmp"); + @unlink("{$g['tmp_path']}/{$uniqid}_tmp"); + unset($a_passthrumacs[$idx]); + write_config(); + echo gettext("The entry was successfully deleted") . "\n"; + } else { + echo gettext("No entry exists for this mac address:") . " " . $_POST['delmac'] . "\n"; + } + } + exit; + } +} + +if ($_GET['act'] == "del") { + $a_passthrumacs =& $a_cp[$cpzone]['passthrumac']; + + if ($a_passthrumacs[$_GET['id']]) { + $cpzoneid = $a_cp[$cpzone]['zoneid']; + $rules = captiveportal_passthrumac_delete_entry($a_passthrumacs[$_GET['id']]); + $uniqid = uniqid("{$cpzone}_mac"); + file_put_contents("{$g['tmp_path']}/{$uniqid}_tmp", $rules); + mwexec("/sbin/ipfw -x {$cpzoneid} -q {$g['tmp_path']}/{$uniqid}_tmp"); + @unlink("{$g['tmp_path']}/{$uniqid}_tmp"); + unset($a_passthrumacs[$_GET['id']]); + write_config(); + header("Location: services_captiveportal_mac.php?zone={$cpzone}"); + exit; + } +} + +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('passthrumac')) + print_info_box_np(gettext("The captive portal MAC address configuration has been changed.<br />You must apply the changes in order for them to take effect.")); + +$tab_array = array(); +$tab_array[] = array(gettext("Captive portal(s)"), false, "services_captiveportal.php?zone={$cpzone}"); +$tab_array[] = array(gettext("MAC"), true, "services_captiveportal_mac.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Allowed IP addresses"), false, "services_captiveportal_ip.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Allowed Hostnames"), false, "services_captiveportal_hostname.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Vouchers"), false, "services_captiveportal_vouchers.php?zone={$cpzone}"); +$tab_array[] = array(gettext("File Manager"), false, "services_captiveportal_filemanager.php?zone={$cpzone}"); +display_top_tabs($tab_array, true); +?> +<div class="table-responsive"> + <table class="table table-hover table-striped table-condensed"> + <thead> + <tr> + <th><?=gettext('Action')?></th> + <th><?=gettext("MAC address")?></th> + <th><?=gettext("Description")?></th> + <th><!-- Buttons --></th> + </tr> + </thead> + +<?php +if (is_array($a_cp[$cpzone]['passthrumac'])): ?> + <tbody> +<?php +$i = 0; +foreach ($a_cp[$cpzone]['passthrumac'] as $mac): ?> + <tr> + <td> + <?=$actsmbl[$mac['action']]?> + </td> + <td> + <?=$mac['mac']?> + </td> + <td > + <?=htmlspecialchars($mac['descr'])?> + </td> + <td> + <a href="services_captiveportal_mac_edit.php?zone=<?=$cpzone?>&id=<?=$i?>" class="btn btn-xs btn-info">Edit</a> + <a href="services_captiveportal_mac.php?zone=<?=$cpzone?>&act=del&id=<?=$i?>" class="btn btn-xs btn-danger">Delete</a> + </td> + </tr> +<?php +$i++; +endforeach; ?> + <tbody> + </table> +<?php +else : +?> + </tbody> + </table> +<?php +endif; +?> + <nav class="action-buttons"> + <a href="services_captiveportal_ip_edit.php?zone=<?=$cpzone?>&act=add" class="btn btn-success">Add</a> + </nav> +</div> + +<?php +print_info_box(gettext('Adding MAC addresses as "pass" MACs allows them access through the captive portal automatically without being taken to the portal page.')); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_captiveportal_mac_edit.php b/src/usr/local/www/services_captiveportal_mac_edit.php new file mode 100644 index 0000000..4f28531 --- /dev/null +++ b/src/usr/local/www/services_captiveportal_mac_edit.php @@ -0,0 +1,287 @@ +<?php +/* + services_captiveportal_mac_edit.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2004 Dinesh Nair <dinesh@alphaque.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-services-captiveportal-editmacaddresses +##|*NAME=Services: Captive portal: Edit MAC Addresses page +##|*DESCR=Allow access to the 'Services: Captive portal: Edit MAC Addresses' page. +##|*MATCH=services_captiveportal_mac_edit.php* +##|-PRIV + +function passthrumacscmp($a, $b) { + return strcmp($a['mac'], $b['mac']); +} + +function passthrumacs_sort() { + global $config, $cpzone; + + usort($config['captiveportal'][$cpzone]['passthrumac'], "passthrumacscmp"); +} + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); + +global $cpzone; +global $cpzoneid; + +$pgtitle = array(gettext("Services"), gettext("Captive portal"), gettext("Edit MAC address rules")); +$shortcut_section = "captiveportal"; + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + +if (empty($cpzone) || empty($config['captiveportal'][$cpzone])) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (!is_array($a_cp[$cpzone]['passthrumac'])) { + $a_cp[$cpzone]['passthrumac'] = array(); +} +$a_passthrumacs = &$a_cp[$cpzone]['passthrumac']; + +if (isset($id) && $a_passthrumacs[$id]) { + $pconfig['action'] = $a_passthrumacs[$id]['action']; + $pconfig['mac'] = $a_passthrumacs[$id]['mac']; + $pconfig['bw_up'] = $a_passthrumacs[$id]['bw_up']; + $pconfig['bw_down'] = $a_passthrumacs[$id]['bw_down']; + $pconfig['descr'] = $a_passthrumacs[$id]['descr']; + $pconfig['username'] = $a_passthrumacs[$id]['username']; +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "action mac"); + $reqdfieldsn = array(gettext("Action"), gettext("MAC address")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + $_POST['mac'] = strtolower(str_replace("-", ":", $_POST['mac'])); + + if ($_POST['mac']) { + if (is_macaddr($_POST['mac'])) { + $iflist = get_interface_list(); + foreach ($iflist as $if) { + if ($_POST['mac'] == strtolower($if['mac'])) { + $input_errors[] = sprintf(gettext("The MAC address %s belongs to a local interface, you cannot use it here."), $_POST['mac']); + break; + } + } + } else { + $input_errors[] = sprintf("%s. [%s]", gettext("A valid MAC address must be specified"), $_POST['mac']); + } + } + if ($_POST['bw_up'] && !is_numeric($_POST['bw_up'])) { + $input_errors[] = gettext("Upload speed needs to be an integer"); + } + if ($_POST['bw_down'] && !is_numeric($_POST['bw_down'])) { + $input_errors[] = gettext("Download speed needs to be an integer"); + } + + foreach ($a_passthrumacs as $macent) { + if (isset($id) && ($a_passthrumacs[$id]) && ($a_passthrumacs[$id] === $macent)) { + continue; + } + + if ($macent['mac'] == $_POST['mac']) { + $input_errors[] = sprintf("[%s] %s.", $_POST['mac'], gettext("already exists")); + break; + } + } + + if (!$input_errors) { + $mac = array(); + $mac['action'] = $_POST['action']; + $mac['mac'] = $_POST['mac']; + if ($_POST['bw_up']) { + $mac['bw_up'] = $_POST['bw_up']; + } + if ($_POST['bw_down']) { + $mac['bw_down'] = $_POST['bw_down']; + } + if ($_POST['username']) { + $mac['username'] = $_POST['username']; + } + + $mac['descr'] = $_POST['descr']; + + if (isset($id) && $a_passthrumacs[$id]) { + $oldmac = $a_passthrumacs[$id]; + $a_passthrumacs[$id] = $mac; + } else { + $oldmac = $mac; + $a_passthrumacs[] = $mac; + } + passthrumacs_sort(); + + write_config(); + + if (isset($config['captiveportal'][$cpzone]['enable'])) { + $cpzoneid = $config['captiveportal'][$cpzone]['zoneid']; + $rules = captiveportal_passthrumac_delete_entry($oldmac); + $rules .= captiveportal_passthrumac_configure_entry($mac); + $uniqid = uniqid("{$cpzone}_macedit"); + file_put_contents("{$g['tmp_path']}/{$uniqid}_tmp", $rules); + mwexec("/sbin/ipfw -x {$cpzoneid} -q {$g['tmp_path']}/{$uniqid}_tmp"); + @unlink("{$g['tmp_path']}/{$uniqid}_tmp"); + unset($cpzoneid); + } + + header("Location: services_captiveportal_mac.php?zone={$cpzone}"); + exit; + } +} + +// Get the MAC address +$ip = $_SERVER['REMOTE_ADDR']; +$mymac = `/usr/sbin/arp -an | grep '('{$ip}')' | head -n 1 | cut -d" " -f4`; +$mymac = str_replace("\n","",$mymac); + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Edit MAC address rules'); + +$section->addInput(new Form_Select( + 'action', + 'Action', + strtolower($pconfig['action']), + array('pass' => 'Pass', 'block' => 'Block') +))->setHelp('Choose what to do with packets coming from this MAC address.'); + +$macaddress = new Form_Input( + 'mac', + 'MAC Address', + 'text', + $pconfig['mac'], + ['placeholder' => 'xx:xx:xx:xx:xx:xx'] +); + +$btnmymac = new Form_Button( + 'btnmymac', + 'Copy My MAC' + ); + +$btnmymac->removeClass('btn-primary')->addClass('btn-success btn-sm'); + +$group = new Form_Group('MAC controls'); +$group->add($macaddress); +$group->add($btnmymac); +$group->setHelp('MAC address (6 hex octets separated by colons)'); +$section->add($group); + +$section->addInput(new Form_Input( + 'bw_up', + 'Bandwidth up', + 'text', + $pconfig['bw_up'] +))->setHelp('Enter an upload limit to be enforced on this MAC in Kbit/s'); + +$section->addInput(new Form_Input( + 'bw_down', + 'Bandwidth down', + 'text', + $pconfig['bw_down'] +))->setHelp('Enter a download limit to be enforced on this MAC in Kbit/s'); + +$section->addInput(new Form_Input( + 'zone', + null, + 'hidden', + $cpzone +)); + +if (isset($id) && $a_passthrumacs[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +if (isset($pconfig['username']) && $pconfig['username']) { + $section->addInput(new Form_Input( + 'username', + null, + 'hidden', + $pconfig['username'] + )); +} + +$form->add($section); +print($form); +?> + +<script> +//<![CDATA[ +events.push(function(){ + // Make the ‘Copy My MAC’ button a plain button, not a submit button + $("#btnmymac").prop('type','button'); + + // On click, copy the hidden 'mymac' text to the 'mac' input + $("#btnmymac").click(function() { + $('#mac').val('<?=$mymac?>'); + }); +}); +//]]> +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_captiveportal_vouchers.php b/src/usr/local/www/services_captiveportal_vouchers.php new file mode 100644 index 0000000..b80d3a0 --- /dev/null +++ b/src/usr/local/www/services_captiveportal_vouchers.php @@ -0,0 +1,656 @@ +<?php +/* + services_captiveportal_vouchers.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2007 Marcel Wiget <mwiget@mac.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/local/bin/voucher /usr/bin/openssl + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-services-captiveportal-vouchers +##|*NAME=Services: Captive portal Vouchers page +##|*DESCR=Allow access to the 'Services: Captive portal Vouchers' page. +##|*MATCH=services_captiveportal_vouchers.php* +##|-PRIV + +if ($_POST['postafterlogin']) { + $nocsrf= true; +} + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); +require_once("voucher.inc"); + +$cpzone = $_GET['zone']; + +if (isset($_POST['zone'])) + $cpzone = $_POST['zone']; + +if (empty($cpzone)) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if ($_REQUEST['generatekey']) { + exec("/usr/bin/openssl genrsa 64 > /tmp/key64.private"); + exec("/usr/bin/openssl rsa -pubout < /tmp/key64.private > /tmp/key64.public"); + $privatekey = str_replace("\n", "\\n", file_get_contents("/tmp/key64.private")); + $publickey = str_replace("\n", "\\n", file_get_contents("/tmp/key64.public")); + exec("rm /tmp/key64.private /tmp/key64.public"); + print json_encode(['public' => $publickey, 'private' => $privatekey]); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +if (!is_array($config['voucher'])) { + $config['voucher'] = array(); +} + +if (empty($a_cp[$cpzone])) { + log_error("Submission on captiveportal page with unknown zone parameter: " . htmlspecialchars($cpzone)); + header("Location: services_captiveportal_zones.php"); + exit; +} + +$pgtitle = array(gettext("Services"), gettext("Captive portal"), gettext("Vouchers"), $a_cp[$cpzone]['zone']); +$shortcut_section = "captiveportal-vouchers"; + +if (!is_array($config['voucher'][$cpzone]['roll'])) { + $config['voucher'][$cpzone]['roll'] = array(); +} +if (!isset($config['voucher'][$cpzone]['charset'])) { + $config['voucher'][$cpzone]['charset'] = '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'; +} +if (!isset($config['voucher'][$cpzone]['rollbits'])) { + $config['voucher'][$cpzone]['rollbits'] = 16; +} +if (!isset($config['voucher'][$cpzone]['ticketbits'])) { + $config['voucher'][$cpzone]['ticketbits'] = 10; +} +if (!isset($config['voucher'][$cpzone]['checksumbits'])) { + $config['voucher'][$cpzone]['checksumbits'] = 5; +} +if (!isset($config['voucher'][$cpzone]['magic'])) { + $config['voucher'][$cpzone]['magic'] = rand(); // anything slightly random will do +} +if (!isset($config['voucher'][$cpzone]['exponent'])) { + while (true) { + while (($exponent = rand()) % 30000 < 5000) { + continue; + } + $exponent = ($exponent * 2) + 1; // Make it odd number + if ($exponent <= 65537) { + break; + } + } + + $config['voucher'][$cpzone]['exponent'] = $exponent; + unset($exponent); +} + +if (!isset($config['voucher'][$cpzone]['publickey'])) { + /* generate a random 64 bit RSA key pair using the voucher binary */ + $fd = popen("/usr/local/bin/voucher -g 64 -e " . $config['voucher'][$cpzone]['exponent'], "r"); + if ($fd !== false) { + $output = fread($fd, 16384); + pclose($fd); + list($privkey, $pubkey) = explode("\0", $output); + $config['voucher'][$cpzone]['publickey'] = base64_encode($pubkey); + $config['voucher'][$cpzone]['privatekey'] = base64_encode($privkey); + } +} + +// Check for invalid or expired vouchers +if (!isset($config['voucher'][$cpzone]['descrmsgnoaccess'])) { + $config['voucher'][$cpzone]['descrmsgnoaccess'] = gettext("Voucher invalid"); +} +if (!isset($config['voucher'][$cpzone]['descrmsgexpired'])) { + $config['voucher'][$cpzone]['descrmsgexpired'] = gettext("Voucher expired"); +} + +$a_roll = &$config['voucher'][$cpzone]['roll']; + +if ($_GET['act'] == "del") { + $id = $_GET['id']; + if ($a_roll[$id]) { + $roll = $a_roll[$id]['number']; + $voucherlck = lock("voucher{$cpzone}"); + unset($a_roll[$id]); + voucher_unlink_db($roll); + unlock($voucherlck); + write_config(); + } + header("Location: services_captiveportal_vouchers.php?zone={$cpzone}"); + exit; +} else if ($_GET['act'] == "csv") { + /* print all vouchers of the selected roll */ + $privkey = base64_decode($config['voucher'][$cpzone]['privatekey']); + if (strstr($privkey, "BEGIN RSA PRIVATE KEY")) { + $fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.private", "w"); + if (!$fd) { + $input_errors[] = gettext("Cannot write private key file") . ".\n"; + } else { + chmod("{$g['varetc_path']}/voucher_{$cpzone}.private", 0600); + fwrite($fd, $privkey); + fclose($fd); + $a_voucher = &$config['voucher'][$cpzone]['roll']; + $id = $_GET['id']; + if (isset($id) && $a_voucher[$id]) { + $number = $a_voucher[$id]['number']; + $count = $a_voucher[$id]['count']; + header("Content-Type: application/octet-stream"); + header("Content-Disposition: attachment; filename=vouchers_{$cpzone}_roll{$number}.csv"); + if (file_exists("{$g['varetc_path']}/voucher_{$cpzone}.cfg")) { + system("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher_{$cpzone}.cfg -p {$g['varetc_path']}/voucher_{$cpzone}.private $number $count"); + } + @unlink("{$g['varetc_path']}/voucher_{$cpzone}.private"); + } else { + header("Location: services_captiveportal_vouchers.php?zone={$cpzone}"); + } + exit; + } + } else { + $input_errors[] = gettext("Need private RSA key to print vouchers") . "\n"; + } +} + +$pconfig['enable'] = isset($config['voucher'][$cpzone]['enable']); +$pconfig['charset'] = $config['voucher'][$cpzone]['charset']; +$pconfig['rollbits'] = $config['voucher'][$cpzone]['rollbits']; +$pconfig['ticketbits'] = $config['voucher'][$cpzone]['ticketbits']; +$pconfig['checksumbits'] = $config['voucher'][$cpzone]['checksumbits']; +$pconfig['magic'] = $config['voucher'][$cpzone]['magic']; +$pconfig['exponent'] = $config['voucher'][$cpzone]['exponent']; +$pconfig['publickey'] = base64_decode($config['voucher'][$cpzone]['publickey']); +$pconfig['privatekey'] = base64_decode($config['voucher'][$cpzone]['privatekey']); +$pconfig['msgnoaccess'] = $config['voucher'][$cpzone]['descrmsgnoaccess']; +$pconfig['msgexpired'] = $config['voucher'][$cpzone]['descrmsgexpired']; +$pconfig['vouchersyncdbip'] = $config['voucher'][$cpzone]['vouchersyncdbip']; +$pconfig['vouchersyncport'] = $config['voucher'][$cpzone]['vouchersyncport']; +$pconfig['vouchersyncpass'] = $config['voucher'][$cpzone]['vouchersyncpass']; +$pconfig['vouchersyncusername'] = $config['voucher'][$cpzone]['vouchersyncusername']; + +if ($_POST) { + unset($input_errors); + + if ($_POST['postafterlogin']) { + voucher_expire($_POST['voucher_expire']); + exit; + } + + $pconfig = $_POST; + + /* input validation */ + if ($_POST['enable'] == "yes") { + if (!$_POST['vouchersyncusername']) { + $reqdfields = explode(" ", "charset rollbits ticketbits checksumbits publickey magic"); + $reqdfieldsn = array(gettext("charset"), gettext("rollbits"), gettext("ticketbits"), gettext("checksumbits"), gettext("publickey"), gettext("magic")); + } else { + $reqdfields = explode(" ", "vouchersyncdbip vouchersyncport vouchersyncpass vouchersyncusername"); + $reqdfieldsn = array(gettext("Synchronize Voucher Database IP"), gettext("Sync port"), gettext("Sync password"), gettext("Sync username")); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + } + + if (!$_POST['vouchersyncusername']) { + // Check for form errors + if ($_POST['charset'] && (strlen($_POST['charset'] < 2))) { + $input_errors[] = gettext("Need at least 2 characters to create vouchers."); + } + if ($_POST['charset'] && (strpos($_POST['charset'], "\"") > 0)) { + $input_errors[] = gettext("Double quotes aren't allowed."); + } + if ($_POST['charset'] && (strpos($_POST['charset'], ",") > 0)) { + $input_errors[] = "',' " . gettext("aren't allowed."); + } + if ($_POST['rollbits'] && (!is_numeric($_POST['rollbits']) || ($_POST['rollbits'] < 1) || ($_POST['rollbits'] > 31))) { + $input_errors[] = gettext("# of Bits to store Roll Id needs to be between 1..31."); + } + if ($_POST['ticketbits'] && (!is_numeric($_POST['ticketbits']) || ($_POST['ticketbits'] < 1) || ($_POST['ticketbits'] > 16))) { + $input_errors[] = gettext("# of Bits to store Ticket Id needs to be between 1..16."); + } + if ($_POST['checksumbits'] && (!is_numeric($_POST['checksumbits']) || ($_POST['checksumbits'] < 1) || ($_POST['checksumbits'] > 31))) { + $input_errors[] = gettext("# of Bits to store checksum needs to be between 1..31."); + } + if ($_POST['publickey'] && (!strstr($_POST['publickey'], "BEGIN PUBLIC KEY"))) { + $input_errors[] = gettext("This doesn't look like an RSA Public key."); + } + if ($_POST['privatekey'] && (!strstr($_POST['privatekey'], "BEGIN RSA PRIVATE KEY"))) { + $input_errors[] = gettext("This doesn't look like an RSA Private key."); + } + if ($_POST['vouchersyncdbip'] && (is_ipaddr_configured($_POST['vouchersyncdbip']))) { + $input_errors[] = gettext("You cannot sync the voucher database to this host (itself)."); + } + } + + if (!$input_errors) { + if (empty($config['voucher'][$cpzone])) { + $newvoucher = array(); + } else { + $newvoucher = $config['voucher'][$cpzone]; + } + if ($_POST['enable'] == "yes") { + $newvoucher['enable'] = true; + } else { + unset($newvoucher['enable']); + } + if (empty($_POST['vouchersyncusername'])) { + unset($newvoucher['vouchersyncdbip']); + unset($newvoucher['vouchersyncport']); + unset($newvoucher['vouchersyncusername']); + unset($newvoucher['vouchersyncpass']); + $newvoucher['charset'] = $_POST['charset']; + $newvoucher['rollbits'] = $_POST['rollbits']; + $newvoucher['ticketbits'] = $_POST['ticketbits']; + $newvoucher['checksumbits'] = $_POST['checksumbits']; + $newvoucher['magic'] = $_POST['magic']; + $newvoucher['exponent'] = $_POST['exponent']; + $newvoucher['publickey'] = base64_encode($_POST['publickey']); + $newvoucher['privatekey'] = base64_encode($_POST['privatekey']); + $newvoucher['descrmsgnoaccess'] = $_POST['msgnoaccess']; + $newvoucher['descrmsgexpired'] = $_POST['msgexpired']; + $config['voucher'][$cpzone] = $newvoucher; + write_config(); + voucher_configure_zone(); + } else { + $newvoucher['vouchersyncdbip'] = $_POST['vouchersyncdbip']; + $newvoucher['vouchersyncport'] = $_POST['vouchersyncport']; + $newvoucher['vouchersyncusername'] = $_POST['vouchersyncusername']; + $newvoucher['vouchersyncpass'] = $_POST['vouchersyncpass']; + if ($newvoucher['vouchersyncpass'] && $newvoucher['vouchersyncusername'] && + $newvoucher['vouchersyncport'] && $newvoucher['vouchersyncdbip']) { + // Synchronize the voucher DB from the master node + require_once("xmlrpc.inc"); + + $protocol = "http"; + if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) && + $config['system']['webgui']['protocol'] == "https") { + $protocol = "https"; + } + if ($protocol == "https" || $newvoucher['vouchersyncport'] == "443") { + $url = "https://{$newvoucher['vouchersyncdbip']}"; + } else { + $url = "http://{$newvoucher['vouchersyncdbip']}"; + } + + $execcmd = <<<EOF + \$toreturn = array(); + \$toreturn['voucher'] = \$config['voucher']['$cpzone']; + unset(\$toreturn['vouchersyncport'], \$toreturn['vouchersyncpass'], \$toreturn['vouchersyncusername'], \$toreturn['vouchersyncdbip']); + +EOF; + + /* assemble xmlrpc payload */ + $params = array( + XML_RPC_encode($newvoucher['vouchersyncpass']), + XML_RPC_encode($execcmd) + ); + $port = $newvoucher['vouchersyncport']; + log_error("voucher XMLRPC sync data {$url}:{$port}."); + $msg = new XML_RPC_Message('pfsense.exec_php', $params); + $cli = new XML_RPC_Client('/xmlrpc.php', $url, $port); + $cli->setCredentials($newvoucher['vouchersyncusername'], $newvoucher['vouchersyncpass']); + $resp = $cli->send($msg, "250"); + if (!is_object($resp)) { + $error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php)."; + log_error($error); + file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", ""); + $input_errors[] = $error; + } elseif ($resp->faultCode()) { + $cli->setDebug(1); + $resp = $cli->send($msg, "250"); + $error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString(); + log_error($error); + file_notice("CaptivePortalVoucherSync", $error, "Error code received", ""); + $input_errors[] = $error; + } else { + log_error("The Captive Portal voucher database has been synchronized with {$url}:{$port} (pfsense.exec_php)."); + } + if (!$input_errors) { + $toreturn = XML_RPC_Decode($resp->value()); + if (!is_array($toreturn)) { + if ($toreturn == "Authentication failed") { + $input_errors[] = "Could not synchronize the voucher database: Authentication Failed."; + } + } else { + // If we received back the voucher roll and other information then store it. + if ($toreturn['voucher']['roll']) { + $newvoucher['roll'] = $toreturn['voucher']['roll']; + } + if ($toreturn['voucher']['rollbits']) { + $newvoucher['rollbits'] = $toreturn['voucher']['rollbits']; + } + if ($toreturn['voucher']['ticketbits']) { + $newvoucher['ticketbits'] = $toreturn['voucher']['ticketbits']; + } + if ($toreturn['voucher']['checksumbits']) { + $newvoucher['checksumbits'] = $toreturn['voucher']['checksumbits']; + } + if ($toreturn['voucher']['magic']) { + $newvoucher['magic'] = $toreturn['voucher']['magic']; + } + if ($toreturn['voucher']['exponent']) { + $newvoucher['exponent'] = $toreturn['voucher']['exponent']; + } + if ($toreturn['voucher']['publickey']) { + $newvoucher['publickey'] = $toreturn['voucher']['publickey']; + } + if ($toreturn['voucher']['privatekey']) { + $newvoucher['privatekey'] = $toreturn['voucher']['privatekey']; + } + if ($toreturn['voucher']['descrmsgnoaccess']) { + $newvoucher['descrmsgnoaccess'] = $toreturn['voucher']['descrmsgnoaccess']; + } + if ($toreturn['voucher']['descrmsgexpired']) { + $newvoucher['descrmsgexpired'] = $toreturn['voucher']['descrmsgexpired']; + } + $savemsg = gettext("Voucher database has been synchronized from {$url}:{$port}"); + + $config['voucher'][$cpzone] = $newvoucher; + write_config(); + voucher_configure_zone(true); + } + } + } + } + if (!$input_errors) { + header("Location: services_captiveportal_vouchers.php?zone={$cpzone}"); + exit; + } + } +} +$closehead = false; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg. 'success'); + +$tab_array = array(); +$tab_array[] = array(gettext("Captive portal(s)"), false, "services_captiveportal.php?zone={$cpzone}"); +$tab_array[] = array(gettext("MAC"), false, "services_captiveportal_mac.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Allowed IP addresses"), false, "services_captiveportal_ip.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Allowed Hostnames"), false, "services_captiveportal_hostname.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Vouchers"), true, "services_captiveportal_vouchers.php?zone={$cpzone}"); +$tab_array[] = array(gettext("File Manager"), false, "services_captiveportal_filemanager.php?zone={$cpzone}"); +display_top_tabs($tab_array, true); + +// We draw a simple table first, then present the controls to work with it +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">Synchronized from <?=$pconfig['vouchersyncdbip']?></h2></div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Roll")?> #</th> + <th><?=gettext("Minutes/Ticket")?></th> + <th># <?=gettext("of Tickets")?></th> + <th><?=gettext("Comment")?></th> + <th><?=gettext("Action")?></th> + </tr> + </thead> + <tbody> +<?php +$i = 0; +foreach($a_roll as $rollent): +?> + <tr> + <td> + <?=htmlspecialchars($rollent['number']); ?> + </td> + <td> + <?=htmlspecialchars($rollent['minutes'])?> + </td> + <td> + <?=htmlspecialchars($rollent['count'])?> + </td> + <td> + <?=htmlspecialchars($rollent['descr']); ?> + </td> + <td> + <!-- These buttons are hidden/shown on checking hte 'enable' checkbox --> + <a href="services_captiveportal_vouchers_edit.php?zone=<?=$cpzone?>&id=<?=$i; ?>" class="btn btn-info btn-xs"><?=gettext("Edit")?></a> + <a href="services_captiveportal_vouchers.php?zone=<?=$cpzone?>&act=del&id=<?=$i; ?>" class="btn btn-danger btn-xs"><?=gettext("Delete")?></a> + <a href="services_captiveportal_vouchers.php?zone=<?=$cpzone?>&act=csv&id=<?=$i; ?>" class="btn btn-success btn-xs" data-toggle="tooltip" title="Generate vouchers for this roll to a .csv file""><?=gettext("Generate")?></a> + </td> + </tr> +<?php + $i++; +endforeach; +?> + </tbody> + </table> + </div> + </div> +</div> +<?php +if ($pconfig['enable']) : ?> + <nav class="action-buttons"> + <a href="services_captiveportal_vouchers_edit.php?zone=<?$cpzone?>" class="btn btn-success"><?=gettext("Add Voucher")?></a> + </nav> +<?php +endif; + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Create, generate and activate Rolls with Vouchers'); + +$section->addInput(new Form_Checkbox( + 'enable', + 'Enable', + 'Enable the creation, generation and activation of rolls with vouchers', + $pconfig['enable'] + )); + +$form->add($section); + +$section = new Form_Section('Create, generate and activate Rolls with Vouchers'); +$section->addClass('rolledit'); + +$section->addInput(new Form_TextArea( + 'publickey', + 'Voucher Public Key', + $pconfig['publickey'] +))->setHelp('Paste an RSA public key (64 Bit or smaller) in PEM format here. This key is used to decrypt vouchers.'); + +$section->addInput(new Form_TextArea( + 'privatekey', + 'Voucher Private Key', + $pconfig['privatekey'] +))->setHelp('Paste an RSA private key (64 Bit or smaller) in PEM format here. This key is only used to generate encrypted vouchers and doesn\'t need to be available if the vouchers have been generated offline.'); + +$section->addInput(new Form_Input( + 'charset', + 'Character set', + 'text', + $pconfig['charset'] +))->setHelp('Tickets are generated with the specified character set. It should contain printable characters (numbers, lower case and upper case letters) that are hard to confuse with others. Avoid e.g. 0/O and l/1.'); + +$section->addInput(new Form_Input( + 'rollbits', + '# of Roll bits', + 'text', + $pconfig['rollbits'] +))->setHelp('Reserves a range in each voucher to store the Roll # it belongs to. Allowed range: 1..31. Sum of Roll+Ticket+Checksum bits must be one Bit less than the RSA key size.'); + +$section->addInput(new Form_Input( + 'ticketbits', + '# of Ticket bits', + 'text', + $pconfig['ticketbits'] +))->setHelp('Reserves a range in each voucher to store the Ticket# it belongs to. Allowed range: 1..16. ' . + 'Using 16 bits allows a roll to have up to 65535 vouchers. ' . + 'A bit array, stored in RAM and in the config, is used to mark if a voucher has been used. A bit array for 65535 vouchers requires 8 KB of storage. '); + +$section->addInput(new Form_Input( + 'checksumbits', + '# of Checksum bits', + 'text', + $pconfig['checksumbits'] +))->setHelp('Reserves a range in each voucher to store a simple checksum over Roll # and Ticket#. Allowed range is 0..31.'); + +$section->addInput(new Form_Input( + 'magic', + 'Magic number', + 'text', + $pconfig['magic'] +))->setHelp('Magic number stored in every voucher. Verified during voucher check. ' . + 'Size depends on how many bits are left by Roll+Ticket+Checksum bits. If all bits are used, no magic number will be used and checked.'); + +$section->addInput(new Form_Input( + 'msgnoaccess', + 'Invalid voucher message', + 'text', + $pconfig['msgnoaccess'] +))->setHelp('Error message displayed for invalid vouchers on captive portal error page ($PORTAL_MESSAGE$).'); + + +$section->addInput(new Form_Input( + 'msgexpired', + 'Expired voucher message', + 'text', + $pconfig['msgexpired'] +))->setHelp('Error message displayed for expired vouchers on captive portal error page ($PORTAL_MESSAGE$).'); + +$form->add($section); + +$section = new Form_Section('Voucher database synchronization'); +$section->addClass('rolledit'); + +$section->addInput(new Form_IpAddress( + 'vouchersyncdbip', + 'Synchronize Voucher Database IP', + $pconfig['vouchersyncdbip'] +))->setHelp('IP address of master nodes webConfigurator to synchronize voucher database and used vouchers from.' . '<br />' . + 'NOTE: this should be setup on the slave nodes and not the primary node!'); + +$section->addInput(new Form_Input( + 'vouchersyncport', + 'Voucher sync port', + 'text', + $pconfig['vouchersyncport'] +))->setHelp('The port of the master voucher node\'s webConfigurator. Example: 443 '); + +$section->addInput(new Form_Input( + 'vouchersyncusername', + 'Voucher sync username', + 'text', + $pconfig['vouchersyncusername'] +))->setHelp('This is the username of the master voucher nodes webConfigurator.'); + +$section->addInput(new Form_Input( + 'vouchersyncpass', + 'Voucher sync password', + 'password', + $pconfig['vouchersyncuserpass'] +))->setHelp('This is the password of the master voucher nodes webConfigurator.'); + +$section->addInput(new Form_Input( + 'zone', + null, + 'hidden', + $cpzone +)); + +$section->addInput(new Form_Input( + 'exponent', + null, + 'hidden', + $pconfig['exponent'] +)); + +$form->add($section); +print($form); +?> +<div class="rolledit"> +<?php + print_info_box(gettext('Changing any Voucher parameter (apart from managing the list of Rolls) on this page will render existing vouchers useless if they were generated with different settings. ' . + 'Specifying the Voucher Database Synchronization options will not record any other value from the other options. They will be retrieved/synced from the master.')); +?> +</div> + +<script> +events.push(function(){ + + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + function setShowHide (show) { + hideClass('rolledit', !show); + + if(show) + $('td:nth-child(5),th:nth-child(5)').show(); + else + $('td:nth-child(5),th:nth-child(5)').hide(); + } + + // Show/hide on checkbox change + $('#enable').click(function() { + setShowHide($('#enable').is(":checked")); + }) + + // Set initial state + setShowHide($('#enable').is(":checked")); + + var generateButton = $('<a class="btn btn-xs btn-default">Generate new keys</a>'); + generateButton.on('click', function(){ + $.ajax({ + type: 'get', + url: 'services_captiveportal_vouchers.php?generatekey=true', + dataType: 'json', + success: function(data){ + $('#publickey').val(data.public.replace(/\\n/g, '\n')); + $('#privatekey').val(data.private.replace(/\\n/g, '\n')); + } + }); + }); + generateButton.appendTo($('#publickey + .help-block')[0]); +}); + +</script> +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_captiveportal_vouchers_edit.php b/src/usr/local/www/services_captiveportal_vouchers_edit.php new file mode 100644 index 0000000..cc05aee --- /dev/null +++ b/src/usr/local/www/services_captiveportal_vouchers_edit.php @@ -0,0 +1,240 @@ +<?php +/* + services_captiveportal_vouchers_edit.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2007 Marcel Wiget <mwiget@mac.com>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-services-captiveportal-voucher-edit +##|*NAME=Services: Captive portal Voucher Rolls page +##|*DESCR=Allow access to the 'Services: Captive portal Edit Voucher Rolls' page. +##|*MATCH=services_captiveportal_vouchers_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); +require_once("voucher.inc"); + +$pgtitle = array(gettext("Services"), gettext("Captive portal"), gettext("Edit Voucher Rolls")); +$shortcut_section = "captiveportal-vouchers"; + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + +if (empty($cpzone) || empty($config['captiveportal'][$cpzone])) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +if (!is_array($config['voucher'])) { + $config['voucher'] = array(); +} + +if (!is_array($config['voucher'][$cpzone]['roll'])) { + $config['voucher'][$cpzone]['roll'] = array(); +} + +$a_roll = &$config['voucher'][$cpzone]['roll']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_roll[$id]) { + $pconfig['zone'] = $a_roll[$id]['zone']; + $pconfig['number'] = $a_roll[$id]['number']; + $pconfig['count'] = $a_roll[$id]['count']; + $pconfig['minutes'] = $a_roll[$id]['minutes']; + $pconfig['descr'] = $a_roll[$id]['descr']; +} + +$maxnumber = (1<<$config['voucher'][$cpzone]['rollbits']) -1; // Highest Roll# +$maxcount = (1<<$config['voucher'][$cpzone]['ticketbits']) -1; // Highest Ticket# + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "number count minutes"); + $reqdfieldsn = array(gettext("Number"), gettext("Count"), gettext("minutes")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + // Look for duplicate roll # + foreach ($a_roll as $re) { + if ($re['number'] == $_POST['number']) { + $input_errors[] = sprintf(gettext("Roll number %s already exists."), $_POST['number']); + break; + } + } + + if (!is_numeric($_POST['number']) || $_POST['number'] >= $maxnumber) { + $input_errors[] = sprintf(gettext("Roll number must be numeric and less than %s"), $maxnumber); + } + + if (!is_numeric($_POST['count']) || $_POST['count'] < 1 || $_POST['count'] > $maxcount) { + $input_errors[] = sprintf(gettext("A roll has at least one voucher and less than %s."), $maxcount); + } + + if (!is_numeric($_POST['minutes']) || $_POST['minutes'] < 1) { + $input_errors[] = gettext("Each voucher must be good for at least 1 minute."); + } + + if (!$input_errors) { + + if (isset($id) && $a_roll[$id]) { + $rollent = $a_roll[$id]; + } + + $rollent['zone'] = $_POST['zone']; + $rollent['number'] = $_POST['number']; + $rollent['minutes'] = $_POST['minutes']; + $rollent['descr'] = $_POST['descr']; + + /* New Roll or modified voucher count: create bitmask */ + $voucherlck = lock("voucher{$cpzone}"); + + if ($_POST['count'] != $rollent['count']) { + $rollent['count'] = $_POST['count']; + $len = ($rollent['count']>>3) + 1; // count / 8 +1 + $rollent['used'] = base64_encode(str_repeat("\000",$len)); // 4 bitmask + $rollent['active'] = array(); + voucher_write_used_db($rollent['number'], $rollent['used']); + voucher_write_active_db($rollent['number'], array()); // create empty DB + voucher_log(LOG_INFO,sprintf(gettext('All %1$s vouchers from Roll %2$s marked unused'), $rollent['count'], $rollent['number'])); + } else { + // existing roll has been modified but without changing the count + // read active and used DB from ramdisk and store it in XML config + $rollent['used'] = base64_encode(voucher_read_used_db($rollent['number'])); + $activent = array(); + $db = array(); + $active_vouchers = voucher_read_active_db($rollent['number'], $rollent['minutes']); + foreach($active_vouchers as $voucher => $line) { + list($timestamp, $minutes) = explode(",", $line); + $activent['voucher'] = $voucher; + $activent['timestamp'] = $timestamp; + $activent['minutes'] = $minutes; + $db[] = $activent; + } + $rollent['active'] = $db; + } + + unlock($voucherlck); + + if (isset($id) && $a_roll[$id]) + $a_roll[$id] = $rollent; + else + $a_roll[] = $rollent; + + write_config(); + + header("Location: services_captiveportal_vouchers.php?zone={$cpzone}"); + exit; + } +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Voucher rolls'); + +$section->addInput(new Form_Input( + 'number', + 'Roll #', + 'text', + $pconfig['number'] +))->setHelp('Enter the Roll# (0..%d) found on top of the generated/printed vouchers', [$maxnumber]); + +$section->addInput(new Form_Input( + 'minutes', + 'Minutes per ticket', + 'text', + $pconfig['minutes'] +))->setHelp('Defines the time in minutes that a user is allowed access. The clock starts ticking the first time a voucher is used for authentication.'); + +$section->addInput(new Form_Input( + 'count', + 'Count', + 'text', + $pconfig['count'] +))->setHelp('Enter the number of vouchers (1..%d) found on top of the generated/printed vouchers. WARNING: Changing this number for an existing Roll will mark all vouchers as unused again', [$maxcount]); + +$section->addInput(new Form_Input( + 'descr', + 'Comment', + 'text', + $pconfig['descr'] +))->setHelp('Can be used to further identify this roll. Ignored by the system.'); + +$section->addInput(new Form_Input( + 'zone', + null, + 'hidden', + $cpzone +)); + +if (isset($id) && $a_roll[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $pconfig['id'] + )); +} + + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_captiveportal_zones.php b/src/usr/local/www/services_captiveportal_zones.php new file mode 100644 index 0000000..6d956b5 --- /dev/null +++ b/src/usr/local/www/services_captiveportal_zones.php @@ -0,0 +1,154 @@ +<?php +/* + services_captiveportal_zones.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-services-captiveportal-zones +##|*NAME=Services: Captive portal Zones page +##|*DESCR=Allow access to the 'Services: Captive portal Zones' page. +##|*MATCH=services_captiveportal_zones.php* +##|-PRIV + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); + +global $cpzone; +global $cpzoneid; + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp = &$config['captiveportal']; + +if ($_GET['act'] == "del" && !empty($_GET['zone'])) { + $cpzone = htmlspecialchars($_GET['zone']); + if ($a_cp[$cpzone]) { + $cpzoneid = $a_cp[$cpzone]['zoneid']; + unset($a_cp[$cpzone]['enable']); + captiveportal_configure_zone($a_cp[$cpzone]); + unset($a_cp[$cpzone]); + if (isset($config['voucher'][$cpzone])) { + unset($config['voucher'][$cpzone]); + } + write_config(); + } + header("Location: services_captiveportal_zones.php"); + exit; +} + +$pgtitle = array(gettext("Captive Portal"), gettext("Zones")); +$shortcut_section = "captiveportal"; +include("head.inc"); + +?> + +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<form action="services_captiveportal_zones.php" method="post"> +<?php if ($savemsg) print_info_box($savemsg); ?> +<?php if (is_subsystem_dirty('captiveportal')): ?><p> +<?php print_info_box_np(gettext("The CaptivePortal entry list has been changed") . ".<br />" . gettext("You must apply the changes in order for them to take effect."));?> +<?php endif; ?> + +<table class="tabcont" width="100%" border="0" cellpadding="0" cellspacing="0" summary="captive portal"> + <tr> + <td width="15%" class="listhdrr"><?=gettext("Zone");?></td> + <td width="30%" class="listhdrr"><?=gettext("Interfaces");?></td> + <td width="10%" class="listhdrr"><?=gettext("Number of users");?></td> + <td width="40%" class="listhdrr"><?=gettext("Description");?></td> + <td width="5%" class="list"> + <table border="0" cellspacing="0" cellpadding="1" summary="icons"> + <tr> + <td valign="middle" width="17"> </td> + <td valign="middle"> + <a href="services_captiveportal_zones_edit.php"><img src="/themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" width="17" height="17" border="0" title="<?=gettext("add a new captiveportal instance");?>" alt="add" /></a> + </td> + </tr> + </table> + </td> + </tr> +<?php + foreach ($a_cp as $cpzone => $cpitem): + if (!is_array($cpitem)) { + continue; + } +?> + <tr> + <td class="listlr" ondblclick="document.location='services_captiveportal.php?zone=<?=$cpzone;?>';"> + <?=htmlspecialchars($cpitem['zone']);?> + </td> + <td class="listlr" ondblclick="document.location='services_captiveportal.php?zone=<?=$cpzone;?>';"> +<?php + $cpifaces = explode(",", $cpitem['interface']); + foreach ($cpifaces as $cpiface) { + echo convert_friendly_interface_to_friendly_descr($cpiface) . " "; + } +?> + </td> + <td class="listr" ondblclick="document.location='services_captiveportal.php?zone=<?=$cpzone;?>';"> + <?=count(captiveportal_read_db());?> + </td> + <td class="listbg" ondblclick="document.location='services_captiveportal.php?zone=<?=$cpzone;?>';"> + <?=htmlspecialchars($cpitem['descr']);?> + </td> + <td valign="middle" class="list nowrap"> + <table border="0" cellspacing="0" cellpadding="1" summary="icons"> + <tr> + <td valign="middle"><a href="services_captiveportal.php?zone=<?=$cpzone?>"><img src="/themes/<?= $g['theme']; ?>/images/icons/icon_e.gif" width="17" height="17" border="0" title="<?=gettext("edit captiveportal instance"); ?>" alt="edit" /></a></td> + <td> + <a href="services_captiveportal_zones.php?act=del&zone=<?=$cpzone;?>" onclick="return confirm('<?=gettext("Do you really want to delete this entry?");?>')"><img src="/themes/<?= $g['theme']; ?>/images/icons/icon_x.gif" width="17" height="17" border="0" title="<?=gettext("delete captiveportal instance");?>" alt="delete" /></a> + </td> + </tr> + </table> + </td> + </tr> +<?php + endforeach; +?> + <tr> + <td class="list" colspan="4"></td> + <td class="list"> + <table border="0" cellspacing="0" cellpadding="1" summary="add"> + <tr> + <td valign="middle" width="17"> </td> + <td valign="middle"> + <a href="services_captiveportal_zones_edit.php"><img src="/themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" width="17" height="17" border="0" title="<?=gettext("add a new captiveportal instance");?>" alt="add" /></a> + </td> + </tr> + </table> + </td> + </tr> +</table> +</form> +<?php include("fend.inc"); ?> +</body> +</html> diff --git a/src/usr/local/www/services_captiveportal_zones_edit.php b/src/usr/local/www/services_captiveportal_zones_edit.php new file mode 100644 index 0000000..340b684 --- /dev/null +++ b/src/usr/local/www/services_captiveportal_zones_edit.php @@ -0,0 +1,116 @@ +<?php +/* + services_captiveportal_zones_edit.php + Copyright (C) 2011 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-services-captiveportal-editzones +##|*NAME=Services: Captive portal: Edit Zones page +##|*DESCR=Allow access to the 'Services: Captive portal: Edit Zones' page. +##|*MATCH=services_captiveportal_zones_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); + +$pgtitle = array(gettext("Services"), gettext("Captive portal"), gettext("Edit Zones")); +$shortcut_section = "captiveportal"; + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "zone"); + $reqdfieldsn = array(gettext("Zone name")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (preg_match('/[^A-Za-z0-9_]/', $_POST['zone'])) { + $input_errors[] = gettext("The zone name can only contain letters, digits, and underscores ( _ )."); + } + + foreach ($a_cp as $cpkey => $cpent) { + if ($cpent['zone'] == $_POST['zone']) { + $input_errors[] = sprintf("[%s] %s.", $_POST['zone'], gettext("already exists")); + break; + } + } + + if (!$input_errors) { + $cpzone = strtolower($_POST['zone']); + $a_cp[$cpzone] = array(); + $a_cp[$cpzone]['zone'] = str_replace(" ", "", $_POST['zone']); + $a_cp[$cpzone]['descr'] = $_POST['descr']; + $a_cp[$cpzone]['localauth_priv'] = true; + write_config(); + + header("Location: services_captiveportal.php?zone={$cpzone}"); + exit; + } +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(new Form_Button( + 'submit', + 'Continue' +)); + +$section = new Form_Section('Edit Captive Portal Zones'); + +$section->addInput(new Form_Input( + 'zone', + 'Zone name' +))->setPattern('[0-9A-Za-z_]+')->setHelp('Zone name. Can only contain letters, digits, and underscores (_).'); + +$section->addInput(new Form_Input( + 'descr', + 'Zone description' +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$form->add($section); + +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_dhcp.php b/src/usr/local/www/services_dhcp.php new file mode 100644 index 0000000..3061812 --- /dev/null +++ b/src/usr/local/www/services_dhcp.php @@ -0,0 +1,1442 @@ +<?php +/* $Id$ */ +/* + services_dhcp.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /bin/rm + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-services-dhcpserver +##|*NAME=Services: DHCP server page +##|*DESCR=Allow access to the 'Services: DHCP server' page. +##|*MATCH=services_dhcp.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("filter.inc"); + +if (!$g['services_dhcp_server_enable']) { + header("Location: /"); + exit; +} + +$if = $_GET['if']; +if (!empty($_POST['if'])) { + $if = $_POST['if']; +} + +/* if OLSRD is enabled, allow WAN to house DHCP. */ +if ($config['installedpackages']['olsrd']) { + foreach ($config['installedpackages']['olsrd']['config'] as $olsrd) { + if ($olsrd['enable']) { + $is_olsr_enabled = true; + break; + } + } +} + +$iflist = get_configured_interface_with_descr(); + +/* set the starting interface */ +if (!$if || !isset($iflist[$if])) { + foreach ($iflist as $ifent => $ifname) { + $oc = $config['interfaces'][$ifent]; + if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) || + (!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) { + continue; + } + $if = $ifent; + break; + } +} + +$act = $_GET['act']; +if (!empty($_POST['act'])) { + $act = $_POST['act']; +} + +$a_pools = array(); + +if (is_array($config['dhcpd'][$if])) { + $pool = $_GET['pool']; + if (is_numeric($_POST['pool'])) { + $pool = $_POST['pool']; + } + + // If we have a pool but no interface name, that's not valid. Redirect away. + if (is_numeric($pool) && empty($if)) { + header("Location: services_dhcp.php"); + exit; + } + + if (!is_array($config['dhcpd'][$if]['pool'])) { + $config['dhcpd'][$if]['pool'] = array(); + } + $a_pools = &$config['dhcpd'][$if]['pool']; + + if (is_numeric($pool) && $a_pools[$pool]) { + $dhcpdconf = &$a_pools[$pool]; + } elseif ($act == "newpool") { + $dhcpdconf = array(); + } else { + $dhcpdconf = &$config['dhcpd'][$if]; + } +} +if (is_array($dhcpdconf)) { + // Global Options + if (!is_numeric($pool) && !($act == "newpool")) { + $pconfig['enable'] = isset($dhcpdconf['enable']); + $pconfig['staticarp'] = isset($dhcpdconf['staticarp']); + // No reason to specify this per-pool, per the dhcpd.conf man page it needs to be in every + // pool and should be specified in every pool both nodes share, so we'll treat it as global + $pconfig['failover_peerip'] = $dhcpdconf['failover_peerip']; + + // dhcpleaseinlocaltime is global to all interfaces. So if it is selected on any interface, + // then show it true/checked. + foreach ($config['dhcpd'] as $dhcpdifitem) { + $dhcpleaseinlocaltime = $dhcpdifitem['dhcpleaseinlocaltime']; + if ($dhcpleaseinlocaltime) { + break; + } + } + + $pconfig['dhcpleaseinlocaltime'] = $dhcpleaseinlocaltime; + + if (!is_array($dhcpdconf['staticmap'])) { + $dhcpdconf['staticmap'] = array(); + } + $a_maps = &$dhcpdconf['staticmap']; + } else { + // Options that exist only in pools + $pconfig['descr'] = $dhcpdconf['descr']; + } + + // Options that can be global or per-pool. + if (is_array($dhcpdconf['range'])) { + $pconfig['range_from'] = $dhcpdconf['range']['from']; + $pconfig['range_to'] = $dhcpdconf['range']['to']; + } + $pconfig['deftime'] = $dhcpdconf['defaultleasetime']; + $pconfig['maxtime'] = $dhcpdconf['maxleasetime']; + $pconfig['gateway'] = $dhcpdconf['gateway']; + $pconfig['domain'] = $dhcpdconf['domain']; + $pconfig['domainsearchlist'] = $dhcpdconf['domainsearchlist']; + list($pconfig['wins1'], $pconfig['wins2']) = $dhcpdconf['winsserver']; + list($pconfig['dns1'], $pconfig['dns2'], $pconfig['dns3'], $pconfig['dns4']) = $dhcpdconf['dnsserver']; + $pconfig['denyunknown'] = isset($dhcpdconf['denyunknown']); + $pconfig['ddnsdomain'] = $dhcpdconf['ddnsdomain']; + $pconfig['ddnsdomainprimary'] = $dhcpdconf['ddnsdomainprimary']; + $pconfig['ddnsdomainkeyname'] = $dhcpdconf['ddnsdomainkeyname']; + $pconfig['ddnsdomainkey'] = $dhcpdconf['ddnsdomainkey']; + $pconfig['ddnsupdate'] = isset($dhcpdconf['ddnsupdate']); + $pconfig['mac_allow'] = $dhcpdconf['mac_allow']; + $pconfig['mac_deny'] = $dhcpdconf['mac_deny']; + list($pconfig['ntp1'], $pconfig['ntp2']) = $dhcpdconf['ntpserver']; + $pconfig['tftp'] = $dhcpdconf['tftp']; + $pconfig['ldap'] = $dhcpdconf['ldap']; + $pconfig['netboot'] = isset($dhcpdconf['netboot']); + $pconfig['nextserver'] = $dhcpdconf['nextserver']; + $pconfig['filename'] = $dhcpdconf['filename']; + $pconfig['filename32'] = $dhcpdconf['filename32']; + $pconfig['filename64'] = $dhcpdconf['filename64']; + $pconfig['rootpath'] = $dhcpdconf['rootpath']; + $pconfig['netmask'] = $dhcpdconf['netmask']; + $pconfig['numberoptions'] = $dhcpdconf['numberoptions']; +} + +$ifcfgip = $config['interfaces'][$if]['ipaddr']; +$ifcfgsn = $config['interfaces'][$if]['subnet']; + +function validate_partial_mac_list($maclist) { + $macs = explode(',', $maclist); + + // Loop through and look for invalid MACs. + foreach ($macs as $mac) { + if (!is_macaddr($mac, true)) { + return false; + } + } + return true; +} + +if (isset($_POST['submit'])) { + + unset($input_errors); + + $pconfig = $_POST; + + $numberoptions = array(); + for ($x = 0; $x < 99; $x++) { + if (isset($_POST["number{$x}"]) && ctype_digit($_POST["number{$x}"])) { + $numbervalue = array(); + $numbervalue['number'] = htmlspecialchars($_POST["number{$x}"]); + $numbervalue['type'] = htmlspecialchars($_POST["itemtype{$x}"]); + $numbervalue['value'] = str_replace('"', '"', htmlspecialchars($_POST["value{$x}"])); + $numberoptions['item'][] = $numbervalue; + } + } + // Reload the new pconfig variable that the form uses. + $pconfig['numberoptions'] = $numberoptions; + + /* input validation */ + if ($_POST['enable'] || is_numeric($pool) || $act == "newpool") { + $reqdfields = explode(" ", "range_from range_to"); + $reqdfieldsn = array(gettext("Range begin"), gettext("Range end")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($_POST['range_from'] && !is_ipaddrv4($_POST['range_from']))) { + $input_errors[] = gettext("A valid range must be specified."); + } + if (($_POST['range_to'] && !is_ipaddrv4($_POST['range_to']))) { + $input_errors[] = gettext("A valid range must be specified."); + } + if (($_POST['gateway'] && $_POST['gateway'] != "none" && !is_ipaddrv4($_POST['gateway']))) { + $input_errors[] = gettext("A valid IP address must be specified for the gateway."); + } + if (($_POST['wins1'] && !is_ipaddrv4($_POST['wins1'])) || ($_POST['wins2'] && !is_ipaddrv4($_POST['wins2']))) { + $input_errors[] = gettext("A valid IP address must be specified for the primary/secondary WINS servers."); + } + $parent_ip = get_interface_ip($_POST['if']); + if (is_ipaddrv4($parent_ip) && $_POST['gateway'] && $_POST['gateway'] != "none") { + $parent_sn = get_interface_subnet($_POST['if']); + if (!ip_in_subnet($_POST['gateway'], gen_subnet($parent_ip, $parent_sn) . "/" . $parent_sn) && !ip_in_interface_alias_subnet($_POST['if'], $_POST['gateway'])) { + $input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet."), $_POST['gateway']); + } + } + if (($_POST['dns1'] && !is_ipaddrv4($_POST['dns1'])) || ($_POST['dns2'] && !is_ipaddrv4($_POST['dns2'])) || ($_POST['dns3'] && !is_ipaddrv4($_POST['dns3'])) || ($_POST['dns4'] && !is_ipaddrv4($_POST['dns4']))) { + $input_errors[] = gettext("A valid IP address must be specified for each of the DNS servers."); + } + + if ($_POST['deftime'] && (!is_numeric($_POST['deftime']) || ($_POST['deftime'] < 60))) { + $input_errors[] = gettext("The default lease time must be at least 60 seconds."); + } + + if (isset($config['captiveportal']) && is_array($config['captiveportal'])) { + $deftime = 7200; // Default value if it's empty + if (is_numeric($_POST['deftime'])) { + $deftime = $_POST['deftime']; + } + + foreach ($config['captiveportal'] as $cpZone => $cpdata) { + if (!isset($cpdata['enable'])) { + continue; + } + if (!isset($cpdata['timeout']) || !is_numeric($cpdata['timeout'])) { + continue; + } + $cp_ifs = explode(',', $cpdata['interface']); + if (!in_array($if, $cp_ifs)) { + continue; + } + if ($cpdata['timeout'] > $deftime) { + $input_errors[] = sprintf(gettext( + "The Captive Portal zone '%s' has Hard Timeout parameter set to a value bigger than Default lease time (%s)."), $cpZone, $deftime); + } + } + } + + if ($_POST['maxtime'] && (!is_numeric($_POST['maxtime']) || ($_POST['maxtime'] < 60) || ($_POST['maxtime'] <= $_POST['deftime']))) { + $input_errors[] = gettext("The maximum lease time must be at least 60 seconds and higher than the default lease time."); + } + if (($_POST['ddnsdomain'] && !is_domain($_POST['ddnsdomain']))) { + $input_errors[] = gettext("A valid domain name must be specified for the dynamic DNS registration."); + } + if (($_POST['ddnsdomain'] && !is_ipaddrv4($_POST['ddnsdomainprimary']))) { + $input_errors[] = gettext("A valid primary domain name server IP address must be specified for the dynamic domain name."); + } + if (($_POST['ddnsdomainkey'] && !$_POST['ddnsdomainkeyname']) || + ($_POST['ddnsdomainkeyname'] && !$_POST['ddnsdomainkey'])) { + $input_errors[] = gettext("You must specify both a valid domain key and key name."); + } + if ($_POST['domainsearchlist']) { + $domain_array = preg_split("/[ ;]+/", $_POST['domainsearchlist']); + foreach ($domain_array as $curdomain) { + if (!is_domain($curdomain)) { + $input_errors[] = gettext("A valid domain search list must be specified."); + break; + } + } + } + + // Validate MACs + if (!empty($_POST['mac_allow']) && !validate_partial_mac_list($_POST['mac_allow'])) { + $input_errors[] = gettext("If you specify a mac allow list, it must contain only valid partial MAC addresses."); + } + if (!empty($_POST['mac_deny']) && !validate_partial_mac_list($_POST['mac_deny'])) { + $input_errors[] = gettext("If you specify a mac deny list, it must contain only valid partial MAC addresses."); + } + + if (($_POST['ntp1'] && !is_ipaddrv4($_POST['ntp1'])) || ($_POST['ntp2'] && !is_ipaddrv4($_POST['ntp2']))) { + $input_errors[] = gettext("A valid IP address must be specified for the primary/secondary NTP servers."); + } + if (($_POST['domain'] && !is_domain($_POST['domain']))) { + $input_errors[] = gettext("A valid domain name must be specified for the DNS domain."); + } + if ($_POST['tftp'] && !is_ipaddrv4($_POST['tftp']) && !is_domain($_POST['tftp']) && !is_URL($_POST['tftp'])) { + $input_errors[] = gettext("A valid IP address or hostname must be specified for the TFTP server."); + } + if (($_POST['nextserver'] && !is_ipaddrv4($_POST['nextserver']))) { + $input_errors[] = gettext("A valid IP address must be specified for the network boot server."); + } + + if (gen_subnet($ifcfgip, $ifcfgsn) == $_POST['range_from']) { + $input_errors[] = gettext("You cannot use the network address in the starting subnet range."); + } + if (gen_subnet_max($ifcfgip, $ifcfgsn) == $_POST['range_to']) { + $input_errors[] = gettext("You cannot use the broadcast address in the ending subnet range."); + } + + // Disallow a range that includes the virtualip + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['interface'] == $if) { + if ($vip['subnet'] && is_inrange_v4($vip['subnet'], $_POST['range_from'], $_POST['range_to'])) { + $input_errors[] = sprintf(gettext("The subnet range cannot overlap with virtual IP address %s."), $vip['subnet']); + } + } + } + } + + $noip = false; + if (is_array($a_maps)) { + foreach ($a_maps as $map) { + if (empty($map['ipaddr'])) { + $noip = true; + } + } + } + if ($_POST['staticarp'] && $noip) { + $input_errors[] = "Cannot enable static ARP when you have static map entries without IP addresses. Ensure all static maps have IP addresses and try again."; + } + + if (is_array($pconfig['numberoptions']['item'])) { + foreach ($pconfig['numberoptions']['item'] as $numberoption) { + if ($numberoption['type'] == 'text' && strstr($numberoption['value'], '"')) { + $input_errors[] = gettext("Text type cannot include quotation marks."); + } else if ($numberoption['type'] == 'string' && !preg_match('/^"[^"]*"$/', $numberoption['value']) && !preg_match('/^[0-9a-f]{2}(?:\:[0-9a-f]{2})*$/i', $numberoption['value'])) { + $input_errors[] = gettext("String type must be enclosed in quotes like \"this\" or must be a series of octets specified in hexadecimal, separated by colons, like 01:23:45:67:89:ab:cd:ef"); + } else if ($numberoption['type'] == 'boolean' && $numberoption['value'] != 'true' && $numberoption['value'] != 'false' && $numberoption['value'] != 'on' && $numberoption['value'] != 'off') { + $input_errors[] = gettext("Boolean type must be true, false, on, or off."); + } else if ($numberoption['type'] == 'unsigned integer 8' && (!is_numeric($numberoption['value']) || $numberoption['value'] < 0 || $numberoption['value'] > 255)) { + $input_errors[] = gettext("Unsigned 8-bit integer type must be a number in the range 0 to 255."); + } else if ($numberoption['type'] == 'unsigned integer 16' && (!is_numeric($numberoption['value']) || $numberoption['value'] < 0 || $numberoption['value'] > 65535)) { + $input_errors[] = gettext("Unsigned 16-bit integer type must be a number in the range 0 to 65535."); + } else if ($numberoption['type'] == 'unsigned integer 32' && (!is_numeric($numberoption['value']) || $numberoption['value'] < 0 || $numberoption['value'] > 4294967295)) { + $input_errors[] = gettext("Unsigned 32-bit integer type must be a number in the range 0 to 4294967295."); + } else if ($numberoption['type'] == 'signed integer 8' && (!is_numeric($numberoption['value']) || $numberoption['value'] < -128 || $numberoption['value'] > 127)) { + $input_errors[] = gettext("Signed 8-bit integer type must be a number in the range -128 to 127."); + } else if ($numberoption['type'] == 'signed integer 16' && (!is_numeric($numberoption['value']) || $numberoption['value'] < -32768 || $numberoption['value'] > 32767)) { + $input_errors[] = gettext("Signed 16-bit integer type must be a number in the range -32768 to 32767."); + } else if ($numberoption['type'] == 'signed integer 32' && (!is_numeric($numberoption['value']) || $numberoption['value'] < -2147483648 || $numberoption['value'] > 2147483647)) { + $input_errors[] = gettext("Signed 32-bit integer type must be a number in the range -2147483648 to 2147483647."); + } else if ($numberoption['type'] == 'ip-address' && !is_ipaddrv4($numberoption['value']) && !is_hostname($numberoption['value'])) { + $input_errors[] = gettext("IP address or host type must be an IP address or host name."); + } + } + } + + if (!$input_errors) { + /* make sure the range lies within the current subnet */ + $subnet_start = ip2ulong(long2ip32(ip2long($ifcfgip) & gen_subnet_mask_long($ifcfgsn))); + $subnet_end = ip2ulong(long2ip32(ip2long($ifcfgip) | (~gen_subnet_mask_long($ifcfgsn)))); + + if ((ip2ulong($_POST['range_from']) < $subnet_start) || (ip2ulong($_POST['range_from']) > $subnet_end) || + (ip2ulong($_POST['range_to']) < $subnet_start) || (ip2ulong($_POST['range_to']) > $subnet_end)) { + $input_errors[] = gettext("The specified range lies outside of the current subnet."); + } + + if (ip2ulong($_POST['range_from']) > ip2ulong($_POST['range_to'])) { + $input_errors[] = gettext("The range is invalid (first element higher than second element)."); + } + + if (is_numeric($pool) || ($act == "newpool")) { + $rfrom = $config['dhcpd'][$if]['range']['from']; + $rto = $config['dhcpd'][$if]['range']['to']; + + if (is_inrange_v4($_POST['range_from'], $rfrom, $rto) || is_inrange_v4($_POST['range_to'], $rfrom, $rto)) { + $input_errors[] = gettext("The specified range must not be within the DHCP range for this interface."); + } + } + + foreach ($a_pools as $id => $p) { + if (is_numeric($pool) && ($id == $pool)) { + continue; + } + + if (is_inrange_v4($_POST['range_from'], $p['range']['from'], $p['range']['to']) || + is_inrange_v4($_POST['range_to'], $p['range']['from'], $p['range']['to'])) { + $input_errors[] = gettext("The specified range must not be within the range configured on a DHCP pool for this interface."); + break; + } + } + + /* make sure that the DHCP Relay isn't enabled on this interface */ + if (isset($config['dhcrelay']['enable']) && (stristr($config['dhcrelay']['interface'], $if) !== false)) { + $input_errors[] = sprintf(gettext("You must disable the DHCP relay on the %s interface before enabling the DHCP server."), $iflist[$if]); + } + + $dynsubnet_start = ip2ulong($_POST['range_from']); + $dynsubnet_end = ip2ulong($_POST['range_to']); + if (is_array($a_maps)) { + foreach ($a_maps as $map) { + if (empty($map['ipaddr'])) { + continue; + } + if ((ip2ulong($map['ipaddr']) > $dynsubnet_start) && + (ip2ulong($map['ipaddr']) < $dynsubnet_end)) { + $input_errors[] = sprintf(gettext("The DHCP range cannot overlap any static DHCP mappings.")); + break; + } + } + } + } + } + + if (!$input_errors) { + if (!is_numeric($pool)) { + if ($act == "newpool") { + $dhcpdconf = array(); + } else { + if (!is_array($config['dhcpd'][$if])) { + $config['dhcpd'][$if] = array(); + } + $dhcpdconf = $config['dhcpd'][$if]; + } + } else { + if (is_array($a_pools[$pool])) { + $dhcpdconf = $a_pools[$pool]; + } else { + // Someone specified a pool but it doesn't exist. Punt. + header("Location: services_dhcp.php"); + exit; + } + } + if (!is_array($dhcpdconf['range'])) { + $dhcpdconf['range'] = array(); + } + + $dhcpd_enable_changed = false; + + // Global Options + if (!is_numeric($pool) && !($act == "newpool")) { + $old_dhcpd_enable = isset($dhcpdconf['enable']); + $new_dhcpd_enable = ($_POST['enable']) ? true : false; + if ($old_dhcpd_enable != $new_dhcpd_enable) { + /* DHCP has been enabled or disabled. The pf ruleset will need to be rebuilt to allow or disallow DHCP. */ + $dhcpd_enable_changed = true; + } + $dhcpdconf['enable'] = $new_dhcpd_enable; + $dhcpdconf['staticarp'] = ($_POST['staticarp']) ? true : false; + $previous = $dhcpdconf['failover_peerip']; + if ($previous <> $_POST['failover_peerip']) { + mwexec("/bin/rm -rf /var/dhcpd/var/db/*"); + } + $dhcpdconf['failover_peerip'] = $_POST['failover_peerip']; + // dhcpleaseinlocaltime is global to all interfaces. So update the setting on all interfaces. + foreach ($config['dhcpd'] as &$dhcpdifitem) { + $dhcpdifitem['dhcpleaseinlocaltime'] = $_POST['dhcpleaseinlocaltime']; + } + } else { + // Options that exist only in pools + $dhcpdconf['descr'] = $_POST['descr']; + } + + // Options that can be global or per-pool. + $dhcpdconf['range']['from'] = $_POST['range_from']; + $dhcpdconf['range']['to'] = $_POST['range_to']; + $dhcpdconf['defaultleasetime'] = $_POST['deftime']; + $dhcpdconf['maxleasetime'] = $_POST['maxtime']; + $dhcpdconf['netmask'] = $_POST['netmask']; + + unset($dhcpdconf['winsserver']); + if ($_POST['wins1']) { + $dhcpdconf['winsserver'][] = $_POST['wins1']; + } + if ($_POST['wins2']) { + $dhcpdconf['winsserver'][] = $_POST['wins2']; + } + + unset($dhcpdconf['dnsserver']); + if ($_POST['dns1']) { + $dhcpdconf['dnsserver'][] = $_POST['dns1']; + } + if ($_POST['dns2']) { + $dhcpdconf['dnsserver'][] = $_POST['dns2']; + } + if ($_POST['dns3']) { + $dhcpdconf['dnsserver'][] = $_POST['dns3']; + } + if ($_POST['dns4']) { + $dhcpdconf['dnsserver'][] = $_POST['dns4']; + } + + $dhcpdconf['gateway'] = $_POST['gateway']; + $dhcpdconf['domain'] = $_POST['domain']; + $dhcpdconf['domainsearchlist'] = $_POST['domainsearchlist']; + $dhcpdconf['denyunknown'] = ($_POST['denyunknown']) ? true : false; + $dhcpdconf['ddnsdomain'] = $_POST['ddnsdomain']; + $dhcpdconf['ddnsdomainprimary'] = $_POST['ddnsdomainprimary']; + $dhcpdconf['ddnsdomainkeyname'] = $_POST['ddnsdomainkeyname']; + $dhcpdconf['ddnsdomainkey'] = $_POST['ddnsdomainkey']; + $dhcpdconf['ddnsupdate'] = ($_POST['ddnsupdate']) ? true : false; + $dhcpdconf['mac_allow'] = $_POST['mac_allow']; + $dhcpdconf['mac_deny'] = $_POST['mac_deny']; + + unset($dhcpdconf['ntpserver']); + if ($_POST['ntp1']) { + $dhcpdconf['ntpserver'][] = $_POST['ntp1']; + } + if ($_POST['ntp2']) { + $dhcpdconf['ntpserver'][] = $_POST['ntp2']; + } + + $dhcpdconf['tftp'] = $_POST['tftp']; + $dhcpdconf['ldap'] = $_POST['ldap']; + $dhcpdconf['netboot'] = ($_POST['netboot']) ? true : false; + $dhcpdconf['nextserver'] = $_POST['nextserver']; + $dhcpdconf['filename'] = $_POST['filename']; + $dhcpdconf['filename32'] = $_POST['filename32']; + $dhcpdconf['filename64'] = $_POST['filename64']; + $dhcpdconf['rootpath'] = $_POST['rootpath']; + + // Handle the custom options rowhelper + if (isset($dhcpdconf['numberoptions']['item'])) { + unset($dhcpdconf['numberoptions']['item']); + } + + $dhcpdconf['numberoptions'] = $numberoptions; + + if (is_numeric($pool) && is_array($a_pools[$pool])) { + $a_pools[$pool] = $dhcpdconf; + } elseif ($act == "newpool") { + $a_pools[] = $dhcpdconf; + } else { + $config['dhcpd'][$if] = $dhcpdconf; + } + + write_config(); + } +} + +if ((isset($_POST['submit']) || isset($_POST['apply'])) && (!$input_errors)) { + $retval = 0; + $retvaldhcp = 0; + $retvaldns = 0; + /* dnsmasq_configure calls dhcpd_configure */ + /* no need to restart dhcpd twice */ + if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic'])) { + $retvaldns = services_dnsmasq_configure(); + if ($retvaldns == 0) { + clear_subsystem_dirty('hosts'); + clear_subsystem_dirty('staticmaps'); + } + } else if (isset($config['unbound']['enable']) && isset($config['unbound']['regdhcpstatic'])) { + $retvaldns = services_unbound_configure(); + if ($retvaldns == 0) { + clear_subsystem_dirty('unbound'); + clear_subsystem_dirty('hosts'); + clear_subsystem_dirty('staticmaps'); + } + } else { + $retvaldhcp = services_dhcpd_configure(); + if ($retvaldhcp == 0) { + clear_subsystem_dirty('staticmaps'); + } + } + if ($dhcpd_enable_changed) { + $retvalfc = filter_configure(); + } + + if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) { + $retval = 1; + } + $savemsg = get_std_save_message($retval); +} + +if ($act == "delpool") { + if ($a_pools[$_GET['id']]) { + unset($a_pools[$_GET['id']]); + write_config(); + header("Location: services_dhcp.php?if={$if}"); + exit; + } +} + +if ($act == "del") { + if ($a_maps[$_GET['id']]) { + unset($a_maps[$_GET['id']]); + write_config(); + if (isset($config['dhcpd'][$if]['enable'])) { + mark_subsystem_dirty('staticmaps'); + if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic'])) { + mark_subsystem_dirty('hosts'); + } + } + header("Location: services_dhcp.php?if={$if}"); + exit; + } +} + +$closehead = false; +$pgtitle = array(gettext("Services"), gettext("DHCP server")); +$shortcut_section = "dhcp"; + +include("head.inc"); + +?> + +<script type="text/javascript" src="/javascript/row_helper.js"> +</script> + +<script type="text/javascript"> +//<![CDATA[ + function itemtype_field(fieldname, fieldsize, n) { + return '<select name="' + fieldname + n + '" class="formselect" id="' + fieldname + n + '"><?php + $customitemtypes = array('text' => gettext('Text'), 'string' => gettext('String'), 'boolean' => gettext('Boolean'), + 'unsigned integer 8' => gettext('Unsigned 8-bit integer'), 'unsigned integer 16' => gettext('Unsigned 16-bit integer'), 'unsigned integer 32' => gettext('Unsigned 32-bit integer'), + 'signed integer 8' => gettext('Signed 8-bit integer'), 'signed integer 16' => gettext('Signed 16-bit integer'), 'signed integer 32' => gettext('Signed 32-bit integer'), 'ip-address' => gettext('IP address or host')); + foreach ($customitemtypes as $typename => $typedescr) { + echo "<option value=\"{$typename}\">{$typedescr}<\/option>"; + } + ?><\/select>'; + } + + rowname[0] = "number"; + rowtype[0] = "textbox"; + rowsize[0] = "10"; + rowname[1] = "itemtype"; + rowtype[1] = itemtype_field; + rowname[2] = "value"; + rowtype[2] = "textbox"; + rowsize[2] = "40"; +//]]> +</script> + +<script type="text/javascript"> +//<![CDATA[ + function enable_change(enable_over) { + var endis; + <?php if (is_numeric($pool) || ($act == "newpool")): ?> + enable_over = true; + <?php endif; ?> + endis = !(document.iform.enable.checked || enable_over); + <?php if (is_numeric($pool) || ($act == "newpool")): ?> + document.iform.descr.disabled = endis; + <?php endif; ?> + document.iform.range_from.disabled = endis; + document.iform.range_to.disabled = endis; + document.iform.wins1.disabled = endis; + document.iform.wins2.disabled = endis; + document.iform.dns1.disabled = endis; + document.iform.dns2.disabled = endis; + document.iform.dns3.disabled = endis; + document.iform.dns4.disabled = endis; + document.iform.deftime.disabled = endis; + document.iform.maxtime.disabled = endis; + document.iform.gateway.disabled = endis; + document.iform.failover_peerip.disabled = endis; + document.iform.domain.disabled = endis; + document.iform.domainsearchlist.disabled = endis; + document.iform.staticarp.disabled = endis; + document.iform.dhcpleaseinlocaltime.disabled = endis; + document.iform.ddnsdomain.disabled = endis; + document.iform.ddnsdomainprimary.disabled = endis; + document.iform.ddnsdomainkeyname.disabled = endis; + document.iform.ddnsdomainkey.disabled = endis; + document.iform.ddnsupdate.disabled = endis; + document.iform.mac_allow.disabled = endis; + document.iform.mac_deny.disabled = endis; + document.iform.ntp1.disabled = endis; + document.iform.ntp2.disabled = endis; + document.iform.tftp.disabled = endis; + document.iform.ldap.disabled = endis; + document.iform.netboot.disabled = endis; + document.iform.nextserver.disabled = endis; + document.iform.filename.disabled = endis; + document.iform.filename32.disabled = endis; + document.iform.filename64.disabled = endis; + document.iform.rootpath.disabled = endis; + document.iform.denyunknown.disabled = endis; + } + + function show_shownumbervalue() { + document.getElementById("shownumbervaluebox").innerHTML=''; + aodiv = document.getElementById('shownumbervalue'); + aodiv.style.display = "block"; + } + + function show_ddns_config() { + document.getElementById("showddnsbox").innerHTML=''; + aodiv = document.getElementById('showddns'); + aodiv.style.display = "block"; + } + + function show_maccontrol_config() { + document.getElementById("showmaccontrolbox").innerHTML=''; + aodiv = document.getElementById('showmaccontrol'); + aodiv.style.display = "block"; + } + + function show_ntp_config() { + document.getElementById("showntpbox").innerHTML=''; + aodiv = document.getElementById('showntp'); + aodiv.style.display = "block"; + } + + function show_tftp_config() { + document.getElementById("showtftpbox").innerHTML=''; + aodiv = document.getElementById('showtftp'); + aodiv.style.display = "block"; + } + + function show_ldap_config() { + document.getElementById("showldapbox").innerHTML=''; + aodiv = document.getElementById('showldap'); + aodiv.style.display = "block"; + } + + function show_netboot_config() { + document.getElementById("shownetbootbox").innerHTML=''; + aodiv = document.getElementById('shownetboot'); + aodiv.style.display = "block"; + } +//]]> +</script> +</head> + +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<form action="services_dhcp.php" method="post" name="iform" id="iform"> +<?php if ($input_errors) print_input_errors($input_errors); ?> +<?php if ($savemsg) print_info_box($savemsg); ?> +<?php + if (isset($config['dhcrelay']['enable'])) { + echo gettext("DHCP Relay is currently enabled. Cannot enable the DHCP Server service while the DHCP Relay is enabled on any interface."); + include("fend.inc"); + echo "</body>"; + echo "</html>"; + exit; + } +?> +<?php if (is_subsystem_dirty('staticmaps')): ?><br/> +<?php print_info_box_np(gettext("The static mapping configuration has been changed") . ".<br />" . gettext("You must apply the changes in order for them to take effect."));?><br /> +<?php endif; ?> +<table width="100%" border="0" cellpadding="0" cellspacing="0" summary="dhcp server"> + <tr> + <td> +<?php + /* active tabs */ + $tab_array = array(); + $tabscounter = 0; + $i = 0; + foreach ($iflist as $ifent => $ifname) { + $oc = $config['interfaces'][$ifent]; + if ((is_array($config['dhcpd'][$ifent]) && !isset($config['dhcpd'][$ifent]['enable']) && (!is_ipaddrv4($oc['ipaddr']))) || + (!is_array($config['dhcpd'][$ifent]) && (!is_ipaddrv4($oc['ipaddr'])))) { + continue; + } + if ($ifent == $if) { + $active = true; + } else { + $active = false; + } + $tab_array[] = array($ifname, $active, "services_dhcp.php?if={$ifent}"); + $tabscounter++; + } + if ($tabscounter == 0) { + echo "<b>" . gettext("The DHCP Server can only be enabled on interfaces configured with a static IPv4 address. This system has none.") . "<br/><br/>"; + echo "</td></tr></table></form>"; + include("fend.inc"); + echo "</body>"; + echo "</html>"; + exit; + } + display_top_tabs($tab_array); +?> + </td> + </tr> + <tr> + <td> + <div id="mainarea"> + <table class="tabcont" width="100%" border="0" cellpadding="6" cellspacing="0" summary="main area"> + <?php if (!is_numeric($pool) && !($act == "newpool")): ?> + <tr> + <td width="22%" valign="top" class="vtable"> </td> + <td width="78%" class="vtable"> + <input name="enable" type="checkbox" value="yes" <?php if ($pconfig['enable']) echo "checked=\"checked\""; ?> onclick="enable_change(false)" /> + <strong><?php printf(gettext("Enable DHCP server on %s interface"), htmlspecialchars($iflist[$if]));?></strong> + </td> + </tr> + <?php else: ?> + <tr> + <td colspan="2" class="listtopic"><?php echo gettext("Editing Pool-Specific Options. To return to the Interface, click its tab above."); ?></td> + </tr> + <?php endif; ?> + <tr> + <td width="22%" valign="top" class="vtable"> </td> + <td width="78%" class="vtable"> + <input name="denyunknown" id="denyunknown" type="checkbox" value="yes" <?php if ($pconfig['denyunknown']) echo "checked=\"checked\""; ?> /> + <strong><?=gettext("Deny unknown clients");?></strong><br /> + <?=gettext("If this is checked, only the clients defined below will get DHCP leases from this server. ");?> + </td> + </tr> + <?php if (is_numeric($pool) || ($act == "newpool")): ?> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Pool Description");?></td> + <td width="78%" class="vtable"> + <input name="descr" type="text" class="formfld unknown" id="descr" size="20" value="<?=htmlspecialchars($pconfig['descr']);?>" /> + </td> + </tr> + <?php endif; ?> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Subnet");?></td> + <td width="78%" class="vtable"> + <?=gen_subnet($ifcfgip, $ifcfgsn);?> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Subnet mask");?></td> + <td width="78%" class="vtable"> + <?=gen_subnet_mask($ifcfgsn);?> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Available range");?></td> + <td width="78%" class="vtable"> + <?php + $range_from = ip2long(long2ip32(ip2long($ifcfgip) & gen_subnet_mask_long($ifcfgsn))); + $range_from++; + echo long2ip32($range_from); + ?> + - + <?php + $range_to = ip2long(long2ip32(ip2long($ifcfgip) | (~gen_subnet_mask_long($ifcfgsn)))); + $range_to--; + echo long2ip32($range_to); + if (is_numeric($pool) || ($act == "newpool")): + ?> + <br />In-use DHCP Pool Ranges: + <?php + if (is_array($config['dhcpd'][$if]['range'])): + ?> + <br /> + <?php + echo $config['dhcpd'][$if]['range']['from']; + ?> + - + <?php + echo $config['dhcpd'][$if]['range']['to']; + endif; + ?> + <?php + foreach ($a_pools as $p): + if (is_array($p['range'])): + ?> + <br /> + <?php + echo $p['range']['from']; + ?> + - + <?php + echo $p['range']['to']; + endif; + endforeach; + endif; + ?> + </td> + </tr> +<?php if ($is_olsr_enabled): ?> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Subnet Mask");?></td> + <td width="78%" class="vtable"> + <select name="netmask" class="formselect" id="netmask"> + <?php + for ($i = 32; $i > 0; $i--) { + if ($i <> 31) { + echo "<option value=\"{$i}\" "; + if ($i == $pconfig['netmask']) { + echo "selected=\"selected\""; + } + echo ">" . $i . "</option>"; + } + } + ?> + </select> + </td> + </tr> +<?php endif; ?> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Range");?></td> + <td width="78%" class="vtable"> + <input name="range_from" type="text" class="formfld unknown" id="range_from" size="20" value="<?=htmlspecialchars($pconfig['range_from']);?>" /> + <?=gettext("to"); ?> <input name="range_to" type="text" class="formfld unknown" id="range_to" size="20" value="<?=htmlspecialchars($pconfig['range_to']);?>" /> + </td> + </tr> +<?php + if (!is_numeric($pool) && !($act == "newpool")): +?> + <tr> + <td width="22%" valign="top" class="vncell"> + <?=gettext("Additional Pools");?> + </td> + <td width="78%" class="vtable"> + <?php echo gettext("If you need additional pools of addresses inside of this subnet outside the above Range, they may be specified here."); ?> + <table class="tabcont" width="100%" border="0" cellpadding="0" cellspacing="0" summary="subnet"> + <tr> + <td width="35%" class="listhdrr"><?=gettext("Pool Start");?></td> + <td width="35%" class="listhdrr"><?=gettext("Pool End");?></td> + <td width="20%" class="listhdrr"><?=gettext("Description");?></td> + <td width="10%" class="list"> + <table border="0" cellspacing="0" cellpadding="1" summary="pool"> + <tr> + <td valign="middle" width="17"></td> + <td valign="middle"> + <a href="services_dhcp.php?if=<?=htmlspecialchars($if);?>&act=newpool"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" width="17" height="17" border="0" alt="plus" /></a> + </td> + </tr> + </table> + </td> + </tr> +<?php + if (is_array($a_pools)): + $i = 0; + foreach ($a_pools as $poolent): + if (!empty($poolent['range']['from']) && !empty($poolent['range']['to'])): +?> + <tr> + <td class="listlr" ondblclick="document.location='services_dhcp.php?if=<?=htmlspecialchars($if);?>&pool=<?=$i;?>';"> + <?=htmlspecialchars($poolent['range']['from']);?> + </td> + <td class="listr" ondblclick="document.location='services_dhcp.php?if=<?=htmlspecialchars($if);?>&pool=<?=$i;?>';"> + <?=htmlspecialchars($poolent['range']['to']);?> + </td> + <td class="listr" ondblclick="document.location='services_dhcp.php?if=<?=htmlspecialchars($if);?>&pool=<?=$i;?>';"> + <?=htmlspecialchars($poolent['descr']);?> + </td> + <td valign="middle" class="list nowrap"> + <table border="0" cellspacing="0" cellpadding="1" summary="icons"> + <tr> + <td valign="middle"> + <a href="services_dhcp.php?if=<?=htmlspecialchars($if);?>&pool=<?=$i;?>"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_e.gif" width="17" height="17" border="0" alt="edit" /></a> + </td> + <td valign="middle"> + <a href="services_dhcp.php?if=<?=htmlspecialchars($if);?>&act=delpool&id=<?=$i;?>" onclick="return confirm('<?=gettext("Do you really want to delete this pool?");?>')"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_x.gif" width="17" height="17" border="0" alt="delete" /></a> + </td> + </tr> + </table> + </td> + </tr> +<?php + endif; + $i++; + endforeach; + endif; +?> + <tr> + <td class="list" colspan="3"></td> + <td class="list"> + <table border="0" cellspacing="0" cellpadding="1" summary="add"> + <tr> + <td valign="middle" width="17"></td> + <td valign="middle"> + <a href="services_dhcp.php?if=<?=htmlspecialchars($if);?>&act=newpool"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" width="17" height="17" border="0" alt="add" /></a> + </td> + </tr> + </table> + </td> + </tr> + </table> + </td> + </tr> +<?php + endif; +?> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("WINS servers");?></td> + <td width="78%" class="vtable"> + <input name="wins1" type="text" class="formfld unknown" id="wins1" size="20" value="<?=htmlspecialchars($pconfig['wins1']);?>" /><br /> + <input name="wins2" type="text" class="formfld unknown" id="wins2" size="20" value="<?=htmlspecialchars($pconfig['wins2']);?>" /> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("DNS servers");?></td> + <td width="78%" class="vtable"> + <input name="dns1" type="text" class="formfld unknown" id="dns1" size="20" value="<?=htmlspecialchars($pconfig['dns1']);?>" /><br /> + <input name="dns2" type="text" class="formfld unknown" id="dns2" size="20" value="<?=htmlspecialchars($pconfig['dns2']);?>" /><br /> + <input name="dns3" type="text" class="formfld unknown" id="dns3" size="20" value="<?=htmlspecialchars($pconfig['dns3']);?>" /><br /> + <input name="dns4" type="text" class="formfld unknown" id="dns4" size="20" value="<?=htmlspecialchars($pconfig['dns4']);?>" /><br /> + <?=gettext("NOTE: leave blank to use the system default DNS servers - this interface's IP if DNS Forwarder or Resolver is enabled, otherwise the servers configured on the General page.");?> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Gateway");?></td> + <td width="78%" class="vtable"> + <input name="gateway" type="text" class="formfld host" id="gateway" size="20" value="<?=htmlspecialchars($pconfig['gateway']);?>" /><br /> + <?=gettext("The default is to use the IP on this interface of the firewall as the gateway. Specify an alternate gateway here if this is not the correct gateway for your network. Type \"none\" for no gateway assignment.");?> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Domain name");?></td> + <td width="78%" class="vtable"> + <input name="domain" type="text" class="formfld unknown" id="domain" size="20" value="<?=htmlspecialchars($pconfig['domain']);?>" /><br /> + <?=gettext("The default is to use the domain name of this system as the default domain name provided by DHCP. You may specify an alternate domain name here.");?> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Domain search list");?></td> + <td width="78%" class="vtable"> + <input name="domainsearchlist" type="text" class="formfld unknown" id="domainsearchlist" size="20" value="<?=htmlspecialchars($pconfig['domainsearchlist']);?>" /><br /> + <?=gettext("The DHCP server can optionally provide a domain search list. Use the semicolon character as separator ");?> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Default lease time");?></td> + <td width="78%" class="vtable"> + <input name="deftime" type="text" class="formfld unknown" id="deftime" size="10" value="<?=htmlspecialchars($pconfig['deftime']);?>" /> + <?=gettext("seconds");?><br /> + <?=gettext("This is used for clients that do not ask for a specific expiration time."); ?><br /> + <?=gettext("The default is 7200 seconds.");?> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Maximum lease time");?></td> + <td width="78%" class="vtable"> + <input name="maxtime" type="text" class="formfld unknown" id="maxtime" size="10" value="<?=htmlspecialchars($pconfig['maxtime']);?>" /> + <?=gettext("seconds");?><br /> + <?=gettext("This is the maximum lease time for clients that ask for a specific expiration time."); ?><br /> + <?=gettext("The default is 86400 seconds.");?> + </td> + </tr> +<?php + if (!is_numeric($pool) && !($act == "newpool")): +?> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Failover peer IP:");?></td> + <td width="78%" class="vtable"> + <input name="failover_peerip" type="text" class="formfld host" id="failover_peerip" size="20" value="<?=htmlspecialchars($pconfig['failover_peerip']);?>" /><br /> + <?=gettext("Leave blank to disable. Enter the interface IP address of the other machine. Machines must be using CARP. Interface's advskew determines whether the DHCPd process is Primary or Secondary. Ensure one machine's advskew<20 (and the other is >20).");?> + </td> + </tr> +<?php + endif; + + if (!is_numeric($pool) && !($act == "newpool")): +?> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Static ARP");?></td> + <td width="78%" class="vtable"> + <table summary="static arp"> + <tr> + <td> + <input style="vertical-align:middle" type="checkbox" value="yes" name="staticarp" id="staticarp" <?php if ($pconfig['staticarp']) echo " checked=\"checked\""; ?> /> + </td> + <td><b><?=gettext("Enable Static ARP entries");?></b></td> + </tr> + <tr> + <td> </td> + <td> + <span class="red"> + <strong><?=gettext("Note:");?></strong> + </span> + <?=gettext("This option persists even if DHCP server is disabled. Only the machines listed below will be able to communicate with the firewall on this NIC.");?> + </td> + </tr> + </table> + </td> + </tr> +<?php + endif; + + if (!is_numeric($pool) && !($act == "newpool")): ?> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Time format change"); ?></td> + <td width="78%" class="vtable"> + <table summary="time format"> + <tr> + <td> + <input name="dhcpleaseinlocaltime" type="checkbox" id="dhcpleaseinlocaltime" value="yes" <?php if ($pconfig['dhcpleaseinlocaltime']) echo "checked=\"checked\""; ?> /> + </td> + <td> + <strong> + <?=gettext("Change DHCP display lease time from UTC to local time."); ?> + </strong> + </td> + </tr> + <tr> + <td> </td> + <td> + <span class="red"> + <strong><?=gettext("Note:");?></strong> + </span> + <?=gettext("By default DHCP leases are displayed in UTC time. By checking this box DHCP lease time will be displayed in local time and set to time zone selected. This will be used for all DHCP interfaces lease time."); ?> + </td> + </tr> + </table> + </td> + </tr> +<?php + endif; +?> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Dynamic DNS");?></td> + <td width="78%" class="vtable"> + <div id="showddnsbox" <?php if ($pconfig['ddnsupdate'] || !empty($pconfig['ddnsdomain']) || !empty($pconfig['ddnsdomainprimary']) || !empty($pconfig['ddnsdomainkeyname']) || !empty($pconfig['ddnsdomainkey'])) echo "style='display:none'"; ?>> + <input type="button" onclick="show_ddns_config()" value="<?=gettext("Advanced");?>" /> - <?=gettext("Show Dynamic DNS");?> + </div> + <div id="showddns" <?php if (!$pconfig['ddnsupdate'] && empty($pconfig['ddnsdomain']) && empty($pconfig['ddnsdomainprimary']) && empty($pconfig['ddnsdomainkeyname']) && empty($pconfig['ddnsdomainkey'])) echo "style='display:none'"; ?>> + <input style="vertical-align:middle" type="checkbox" value="yes" name="ddnsupdate" id="ddnsupdate" <?php if ($pconfig['ddnsupdate']) echo " checked=\"checked\""; ?> /> + <b><?=gettext("Enable registration of DHCP client names in DNS.");?></b><br /> + <br/> + <input name="ddnsdomain" type="text" class="formfld unknown" id="ddnsdomain" size="20" value="<?=htmlspecialchars($pconfig['ddnsdomain']);?>" /><br /> + <?=gettext("Note: Leave blank to disable dynamic DNS registration.");?><br /> + <?=gettext("Enter the dynamic DNS domain which will be used to register client names in the DNS server.");?><br /> + <input name="ddnsdomainprimary" type="text" class="formfld unknown" id="ddnsdomainprimary" size="20" value="<?=htmlspecialchars($pconfig['ddnsdomainprimary']);?>" /><br /> + <?=gettext("Enter the primary domain name server IP address for the dynamic domain name.");?><br /> + <input name="ddnsdomainkeyname" type="text" class="formfld unknown" id="ddnsdomainkeyname" size="20" value="<?=htmlspecialchars($pconfig['ddnsdomainkeyname']);?>" /><br /> + <?=gettext("Enter the dynamic DNS domain key name which will be used to register client names in the DNS server.");?><br /> + <input name="ddnsdomainkey" type="text" class="formfld unknown" id="ddnsdomainkey" size="20" value="<?=htmlspecialchars($pconfig['ddnsdomainkey']);?>" /><br /> + <?=gettext("Enter the dynamic DNS domain key secret which will be used to register client names in the DNS server.");?> + </div> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("MAC Address Control");?></td> + <td width="78%" class="vtable"> + <div id="showmaccontrolbox" <?php if (!empty($pconfig['mac_allow']) || !empty($pconfig['mac_deny'])) echo "style='display:none'"; ?>> + <input type="button" onclick="show_maccontrol_config()" value="<?=gettext("Advanced");?>" /> - <?=gettext("Show MAC Address Control");?> + </div> + <div id="showmaccontrol" <?php if (empty($pconfig['mac_allow']) && empty($pconfig['mac_deny'])) echo "style='display:none'"; ?>> + <input name="mac_allow" type="text" class="formfld unknown" id="mac_allow" size="20" value="<?=htmlspecialchars($pconfig['mac_allow']);?>" /><br /> + <?=gettext("Enter a list of partial MAC addresses to allow, comma separated, no spaces, such as ");?>00:00:00,01:E5:FF<br /> + <input name="mac_deny" type="text" class="formfld unknown" id="mac_deny" size="20" value="<?=htmlspecialchars($pconfig['mac_deny']);?>" /><br /> + <?=gettext("Enter a list of partial MAC addresses to deny access, comma separated, no spaces, such as ");?>00:00:00,01:E5:FF + </div> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("NTP servers");?></td> + <td width="78%" class="vtable"> + <div id="showntpbox" <?php if (!empty($pconfig['ntp1']) || !empty($pconfig['ntp2'])) echo "style='display:none'"; ?>> + <input type="button" onclick="show_ntp_config()" value="<?=gettext("Advanced");?>" /> - <?=gettext("Show NTP configuration");?> + </div> + <div id="showntp" <?php if (empty($pconfig['ntp1']) && empty($pconfig['ntp2'])) echo "style='display:none'"; ?>> + <input name="ntp1" type="text" class="formfld unknown" id="ntp1" size="20" value="<?=htmlspecialchars($pconfig['ntp1']);?>" /><br /> + <input name="ntp2" type="text" class="formfld unknown" id="ntp2" size="20" value="<?=htmlspecialchars($pconfig['ntp2']);?>" /> + </div> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("TFTP server");?></td> + <td width="78%" class="vtable"> + <div id="showtftpbox" <?php if (!empty($pconfig['tftp'])) echo "style='display:none'"; ?>> + <input type="button" onclick="show_tftp_config()" value="<?=gettext("Advanced");?>" /> - <?=gettext("Show TFTP configuration");?> + </div> + <div id="showtftp" <?php if (empty($pconfig['tftp'])) echo "style='display:none'"; ?>> + <input name="tftp" type="text" class="formfld unknown" id="tftp" size="50" value="<?=htmlspecialchars($pconfig['tftp']);?>" /><br /> + <?=gettext("Leave blank to disable. Enter a full hostname or IP for the TFTP server.");?> + </div> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("LDAP URI");?></td> + <td width="78%" class="vtable"> + <div id="showldapbox" <?php if (!empty($pconfig['ldap'])) echo "style='display:none'"; ?>> + <input type="button" onclick="show_ldap_config()" value="<?=gettext("Advanced");?>" /> - <?=gettext("Show LDAP configuration");?> + </div> + <div id="showldap" <?php if (empty($pconfig['ldap'])) echo "style='display:none'"; ?>> + <input name="ldap" type="text" class="formfld unknown" id="ldap" size="80" value="<?=htmlspecialchars($pconfig['ldap']);?>" /><br /> + <?=gettext("Leave blank to disable. Enter a full URI for the LDAP server in the form ldap://ldap.example.com/dc=example,dc=com");?> + </div> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Enable network booting");?></td> + <td width="78%" class="vtable"> + <div id="shownetbootbox" <?php if ($pconfig['netboot'] || !empty($pconfig['nextserver']) || !empty($pconfig['filename']) || !empty($pconfig['filename32']) || !empty($pconfig['filename64']) || !empty($pconfig['rootpath'])) echo "style='display:none'"; ?>> + <input type="button" onclick="show_netboot_config()" value="<?=gettext("Advanced");?>" /> - <?=gettext("Show Network booting");?> + </div> + <div id="shownetboot" <?php if (!$pconfig['netboot'] && empty($pconfig['nextserver']) && empty($pconfig['filename']) && empty($pconfig['filename32']) && empty($pconfig['filename64']) && empty($pconfig['rootpath'])) echo "style='display:none'"; ?>> + <input style="vertical-align:middle" type="checkbox" value="yes" name="netboot" id="netboot" <?php if ($pconfig['netboot']) echo " checked=\"checked\""; ?> /> + <b><?=gettext("Enables network booting.");?></b> + <br/> + <table border="0" cellspacing="0" cellpadding="2" summary="network booting"> + <tr> + <td> + <?=gettext("Enter the IP of the"); ?> <b><?=gettext("next-server"); ?></b> + </td> + <td> + <input name="nextserver" type="text" class="formfld unknown" id="nextserver" size="20" value="<?=htmlspecialchars($pconfig['nextserver']);?>" /><br /> + </td> + </tr> + <tr> + <td> + <?=gettext("and the default bios filename");?> + </td> + <td> + <input name="filename" type="text" class="formfld unknown" id="filename" size="20" value="<?=htmlspecialchars($pconfig['filename']);?>" /><br /> + </td> + </tr> + <tr> + <td> + <?=gettext("and the UEFI 32bit filename ");?> + </td> + <td> + <input name="filename32" type="text" class="formfld unknown" id="filename32" size="20" value="<?=htmlspecialchars($pconfig['filename32']);?>" /><br /> + </td> + </tr> + <tr> + <td> + <?=gettext("and the UEFI 64bit filename ");?> + </td> + <td> + <input name="filename64" type="text" class="formfld unknown" id="filename64" size="20" value="<?=htmlspecialchars($pconfig['filename64']);?>" /><br /> + </td> + </tr> + </table> + <?=gettext("Note: You need both a filename and a boot server configured for this to work!");?> + <?=gettext("You will need all three filenames and a boot server configured for UEFI to work!");?> + <?=gettext("Enter the"); ?> <b><?=gettext("root-path"); ?></b>-<?=gettext("string");?> + <input name="rootpath" type="text" class="formfld unknown" id="rootpath" size="90" value="<?=htmlspecialchars($pconfig['rootpath']);?>" /><br /> + <?=gettext("Note: string-format: iscsi:(servername):(protocol):(port):(LUN):targetname");?> + </div> + </td> + </tr> +<?php + if (!is_numeric($pool) && !($act == "newpool")): +?> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Additional BOOTP/DHCP Options");?></td> + <td width="78%" class="vtable"> + <div id="shownumbervaluebox" <?php if (!empty($pconfig['numberoptions'])) echo "style='display:none'"; ?>> + <input type="button" onclick="show_shownumbervalue()" value="<?=gettext("Advanced");?>" /> - <?=gettext("Show Additional BOOTP/DHCP Options");?> + </div> + <div id="shownumbervalue" <?php if (empty($pconfig['numberoptions'])) echo "style='display:none'"; ?>> + <table id="maintable" summary="bootp-dhcp options"> + <tbody> + <tr> + <td colspan="3"> + <div style="padding:5px; margin-top: 16px; margin-bottom: 16px; border:1px dashed #000066; background-color: #ffffff; color: #000000; font-size: 8pt;" id="itemhelp"> + <?=gettext("Enter the DHCP option number and the value for each item you would like to include in the DHCP lease information. For a list of available options please visit this"); ?> <a href="http://www.iana.org/assignments/bootp-dhcp-parameters/" target="_blank"><?=gettext("URL"); ?></a> + </div> + </td> + </tr> + <tr> + <td><div id="onecolumn"><?=gettext("Number");?></div></td> + <td><div id="twocolumn"><?=gettext("Type");?></div></td> + <td><div id="threecolumn"><?=gettext("Value");?></div></td> + </tr> +<?php + $counter = 0; + if ($pconfig['numberoptions']): + foreach ($pconfig['numberoptions']['item'] as $item): + $number = $item['number']; + $itemtype = $item['type']; + $value = $item['value']; +?> + <tr> + <td> + <input autocomplete="off" name="number<?php echo $counter; ?>" type="text" class="formfld unknown" id="number<?php echo $counter; ?>" size="10" value="<?=htmlspecialchars($number);?>" /> + </td> + <td> + <select name="itemtype<?php echo $counter; ?>" class="formselect" id="itemtype<?php echo $counter; ?>"> +<?php + foreach ($customitemtypes as $typename => $typedescr) { + echo "<option value=\"{$typename}\" "; + if ($itemtype == $typename) { + echo "selected=\"selected\""; + } + echo ">" . $typedescr . "</option>"; + } +?> + </select> + </td> + <td> + <input autocomplete="off" name="value<?php echo $counter; ?>" type="text" class="formfld unknown" id="value<?php echo $counter; ?>" size="40" value="<?=htmlspecialchars($value);?>" /> + </td> + <td> + <a onclick="removeRow(this); return false;" href="#"><img border="0" src="/themes/<?echo $g['theme'];?>/images/icons/icon_x.gif" alt="delete" /></a> + </td> + </tr> +<?php + $counter++; + endforeach; + endif; // numberoptions +?> + </tbody> + </table> + <a onclick="javascript:addRowTo('maintable', 'formfldalias'); return false;" href="#"> + <img border="0" src="/themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" alt="" title="<?=gettext("add another entry");?>" /> + </a> + <script type="text/javascript"> + //<![CDATA[ + field_counter_js = 3; + rows = 1; + totalrows = <?php echo $counter; ?>; + loaded = <?php echo $counter; ?>; + //]]> + </script> + </div> + </td> + </tr> +<?php + endif; +?> + <tr> + <td width="22%" valign="top"> </td> + <td width="78%"> + <?php if ($act == "newpool"): ?> + <input type="hidden" name="act" value="newpool" /> + <?php endif; ?> + <?php if (is_numeric($pool)): ?> + <input type="hidden" name="pool" value="<?php echo $pool; ?>" /> + <?php endif; ?> + <input name="if" type="hidden" value="<?=htmlspecialchars($if);?>" /> + <input name="submit" type="submit" class="formbtn" value="<?=gettext("Save");?>" onclick="enable_change(true)" /> + </td> + </tr> + <tr> + <td width="22%" valign="top"> </td> + <td width="78%"> + <p> + <span class="vexpl"> + <span class="red"><strong><?=gettext("Note:");?> + <br /> + </strong> + </span> + <?=gettext("The DNS servers entered in"); ?> + <a href="system.php"><?=gettext("System: General setup"); ?></a> + <?=gettext("(or the"); ?> <a href="services_dnsmasq.php"><?=gettext("DNS forwarder"); ?></a>, <?=gettext("if enabled)"); ?> + </span> + <span class="vexpl"> + <?=gettext("will be assigned to clients by the DHCP server."); ?> + <br /> + <br /> + <?=gettext("The DHCP lease table can be viewed on the"); ?> + <a href="status_dhcp_leases.php"><?=gettext("Status: DHCP leases"); ?></a> + <?=gettext("page."); ?> + <br /> + </span> + </p> + </td> + </tr> + </table> +<?php + if (!is_numeric($pool) && !($act == "newpool")): +?> + <table class="tabcont" width="100%" border="0" cellpadding="0" cellspacing="0" summary="static mappings"> + <tr> + <td colspan="5" valign="top" class="listtopic"><?=gettext("DHCP Static Mappings for this interface.");?></td> + <td> </td> + </tr> + <tr> + <td width="7%" class="listhdrr"><?=gettext("Static ARP");?></td> + <td width="18%" class="listhdrr"><?=gettext("MAC address");?></td> + <td width="15%" class="listhdrr"><?=gettext("IP address");?></td> + <td width="20%" class="listhdrr"><?=gettext("Hostname");?></td> + <td width="30%" class="listhdr"><?=gettext("Description");?></td> + <td width="10%" class="list"> + <table border="0" cellspacing="0" cellpadding="1" summary="add"> + <tr> + <td valign="middle" width="17"></td> + <td valign="middle"> + <a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if);?>"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" width="17" height="17" border="0" alt="add" /></a> + </td> + </tr> + </table> + </td> + </tr> +<?php + if (is_array($a_maps)): + $i = 0; + foreach ($a_maps as $mapent): +?> + <tr> + <td align="center" class="listlr" ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if);?>&id=<?=$i;?>';"> + <?php if (isset($mapent['arp_table_static_entry'])): ?> + <img src="./themes/<?= $g['theme']; ?>/images/icons/icon_alert.gif" alt="ARP Table Static Entry" width="17" height="17" border="0" alt="alert" /> + <?php endif; ?> + </td> + <td class="listlr" ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if);?>&id=<?=$i;?>';"> + <?=htmlspecialchars($mapent['mac']);?> + </td> + <td class="listr" ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if);?>&id=<?=$i;?>';"> + <?=htmlspecialchars($mapent['ipaddr']);?> + </td> + <td class="listr" ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if);?>&id=<?=$i;?>';"> + <?=htmlspecialchars($mapent['hostname']);?> + </td> + <td class="listbg" ondblclick="document.location='services_dhcp_edit.php?if=<?=htmlspecialchars($if);?>&id=<?=$i;?>';"> + <?=htmlspecialchars($mapent['descr']);?> + </td> + <td valign="middle" class="list nowrap"> + <table border="0" cellspacing="0" cellpadding="1" summary="icons"> + <tr> + <td valign="middle"><a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if);?>&id=<?=$i;?>"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_e.gif" width="17" height="17" border="0" alt="edit" /></a></td> + <td valign="middle"><a href="services_dhcp.php?if=<?=htmlspecialchars($if);?>&act=del&id=<?=$i;?>" onclick="return confirm('<?=gettext("Do you really want to delete this mapping?");?>')"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_x.gif" width="17" height="17" border="0" alt="delete" /></a></td> + </tr> + </table> + </td> + </tr> +<?php + $i++; + endforeach; + endif; +?> + <tr> + <td class="list" colspan="5"></td> + <td class="list"> + <table border="0" cellspacing="0" cellpadding="1" summary="add"> + <tr> + <td valign="middle" width="17"></td> + <td valign="middle"><a href="services_dhcp_edit.php?if=<?=htmlspecialchars($if);?>"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" width="17" height="17" border="0" alt="add" /></a></td> + </tr> + </table> + </td> + </tr> + </table> +<?php + endif; +?> + </div> + </td> + </tr> +</table> +</form> +<script type="text/javascript"> +//<![CDATA[ +enable_change(false); +//]]> +</script> +<?php include("fend.inc"); ?> +</body> +</html> diff --git a/src/usr/local/www/services_dhcp_edit.php b/src/usr/local/www/services_dhcp_edit.php new file mode 100644 index 0000000..1ab0987 --- /dev/null +++ b/src/usr/local/www/services_dhcp_edit.php @@ -0,0 +1,739 @@ +<?php +/* $Id$ */ +/* + services_dhcp_edit.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/sbin/arp + pfSense_MODULE: dhcpserver +*/ + +##|+PRIV +##|*IDENT=page-services-dhcpserver-editstaticmapping +##|*NAME=Services: DHCP Server : Edit static mapping page +##|*DESCR=Allow access to the 'Services: DHCP Server : Edit static mapping' page. +##|*MATCH=services_dhcp_edit.php* +##|-PRIV + +function staticmapcmp($a, $b) { + return ipcmp($a['ipaddr'], $b['ipaddr']); +} + +function staticmaps_sort($ifgui) { + global $g, $config; + + usort($config['dhcpd'][$ifgui]['staticmap'], "staticmapcmp"); +} + +require_once('globals.inc'); + +if (!$g['services_dhcp_server_enable']) { + header("Location: /"); + exit; +} + +require("guiconfig.inc"); + +$if = $_GET['if']; + +if ($_POST['if']) + $if = $_POST['if']; + +if (!$if) { + header("Location: services_dhcp.php"); + exit; +} + +if (!is_array($config['dhcpd'])) { + $config['dhcpd'] = array(); +} +if (!is_array($config['dhcpd'][$if])) { + $config['dhcpd'][$if] = array(); +} +if (!is_array($config['dhcpd'][$if]['staticmap'])) { + $config['dhcpd'][$if]['staticmap'] = array(); +} + +if (!is_array($config['dhcpd'][$if]['pool'])) { + $config['dhcpd'][$if]['pool'] = array(); +} +$a_pools = &$config['dhcpd'][$if]['pool']; + +$static_arp_enabled=isset($config['dhcpd'][$if]['staticarp']); +$netboot_enabled=isset($config['dhcpd'][$if]['netboot']); +$a_maps = &$config['dhcpd'][$if]['staticmap']; +$ifcfgip = get_interface_ip($if); +$ifcfgsn = get_interface_subnet($if); +$ifcfgdescr = convert_friendly_interface_to_friendly_descr($if); + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_maps[$id]) { + $pconfig['mac'] = $a_maps[$id]['mac']; + $pconfig['cid'] = $a_maps[$id]['cid']; + $pconfig['hostname'] = $a_maps[$id]['hostname']; + $pconfig['ipaddr'] = $a_maps[$id]['ipaddr']; + $pconfig['filename'] = $a_maps[$id]['filename']; + $pconfig['rootpath'] = $a_maps[$id]['rootpath']; + $pconfig['descr'] = $a_maps[$id]['descr']; + $pconfig['arp_table_static_entry'] = isset($a_maps[$id]['arp_table_static_entry']); + $pconfig['deftime'] = $a_maps[$id]['defaultleasetime']; + $pconfig['maxtime'] = $a_maps[$id]['maxleasetime']; + $pconfig['gateway'] = $a_maps[$id]['gateway']; + $pconfig['domain'] = $a_maps[$id]['domain']; + $pconfig['domainsearchlist'] = $a_maps[$id]['domainsearchlist']; + list($pconfig['wins1'], $pconfig['wins2']) = $a_maps[$id]['winsserver']; + list($pconfig['dns1'], $pconfig['dns2'], $pconfig['dns3'], $pconfig['dns4']) = $a_maps[$id]['dnsserver']; + $pconfig['ddnsdomain'] = $a_maps[$id]['ddnsdomain']; + $pconfig['ddnsdomainprimary'] = $a_maps[$id]['ddnsdomainprimary']; + $pconfig['ddnsdomainkeyname'] = $a_maps[$id]['ddnsdomainkeyname']; + $pconfig['ddnsdomainkey'] = $a_maps[$id]['ddnsdomainkey']; + $pconfig['ddnsupdate'] = isset($a_maps[$id]['ddnsupdate']); + list($pconfig['ntp1'], $pconfig['ntp2']) = $a_maps[$id]['ntpserver']; + $pconfig['tftp'] = $a_maps[$id]['tftp']; +} else { + $pconfig['mac'] = $_GET['mac']; + $pconfig['cid'] = $_GET['cid']; + $pconfig['hostname'] = $_GET['hostname']; + $pconfig['filename'] = $_GET['filename']; + $pconfig['rootpath'] = $_GET['rootpath']; + $pconfig['descr'] = $_GET['descr']; + $pconfig['arp_table_static_entry'] = $_GET['arp_table_static_entry']; + $pconfig['deftime'] = $_GET['defaultleasetime']; + $pconfig['maxtime'] = $_GET['maxleasetime']; + $pconfig['gateway'] = $_GET['gateway']; + $pconfig['domain'] = $_GET['domain']; + $pconfig['domainsearchlist'] = $_GET['domainsearchlist']; + $pconfig['wins1'] = $_GET['wins1']; + $pconfig['wins2'] = $_GET['wins2']; + $pconfig['dns1'] = $_GET['dns1']; + $pconfig['dns2'] = $_GET['dns2']; + $pconfig['dns3'] = $_GET['dns3']; + $pconfig['dns4'] = $_GET['dns4']; + $pconfig['ddnsdomain'] = $_GET['ddnsdomain']; + $pconfig['ddnsdomainprimary'] = $_GET['ddnsdomainprimary']; + $pconfig['ddnsdomainkeyname'] = $_GET['ddnsdomainkeyname']; + $pconfig['ddnsdomainkey'] = $_GET['ddnsdomainkey']; + $pconfig['ddnsupdate'] = isset($_GET['ddnsupdate']); + $pconfig['ntp1'] = $_GET['ntp1']; + $pconfig['ntp2'] = $_GET['ntp2']; + $pconfig['tftp'] = $_GET['tftp']; +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = array(); + $reqdfieldsn = array(); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + /* either MAC or Client-ID must be specified */ + if (empty($_POST['mac']) && empty($_POST['cid'])) { + $input_errors[] = gettext("Either MAC address or Client identifier must be specified"); + } + + /* normalize MAC addresses - lowercase and convert Windows-ized hyphenated MACs to colon delimited */ + $_POST['mac'] = strtolower(str_replace("-", ":", $_POST['mac'])); + + if ($_POST['hostname']) { + preg_match("/\-\$/", $_POST['hostname'], $matches); + if ($matches) { + $input_errors[] = gettext("The hostname cannot end with a hyphen according to RFC952"); + } + if (!is_hostname($_POST['hostname'])) { + $input_errors[] = gettext("The hostname can only contain the characters A-Z, 0-9 and '-'."); + } else { + if (!is_unqualified_hostname($_POST['hostname'])) { + $input_errors[] = gettext("A valid hostname is specified, but the domain name part should be omitted"); + } + } + } + + if (($_POST['ipaddr'] && !is_ipaddr($_POST['ipaddr']))) { + $input_errors[] = gettext("A valid IP address must be specified."); + } + + if (($_POST['mac'] && !is_macaddr($_POST['mac']))) { + $input_errors[] = gettext("A valid MAC address must be specified."); + } + if ($static_arp_enabled && !$_POST['ipaddr']) { + $input_errors[] = gettext("Static ARP is enabled. You must specify an IP address."); + } + + /* check for overlaps */ + foreach ($a_maps as $mapent) { + if (isset($id) && ($a_maps[$id]) && ($a_maps[$id] === $mapent)) { + continue; + } + /* The fully qualified hostname (hostname + '.' + domainname) must be unique. + * The unqualified hostname does not have to be unique as long as the fully + * qualified hostname is unique. */ + $existingFqn = "{$mapent['hostname']}.{$mapent['domain']}"; + $candidateFqn = "{$_POST['hostname']}.{$_POST['domain']}"; + if ((($existingFqn == $candidateFqn) && $mapent['hostname']) || + (($mapent['mac'] == $_POST['mac']) && $mapent['mac']) || + (($mapent['ipaddr'] == $_POST['ipaddr']) && $mapent['ipaddr']) || + (($mapent['cid'] == $_POST['cid']) && $mapent['cid'])) { + $input_errors[] = gettext("This fully qualified hostname (Hostname + Domainname), IP, MAC address or Client identifier already exists."); + break; + } + } + + /* make sure it's not within the dynamic subnet */ + if ($_POST['ipaddr']) { + $dynsubnet_start = ip2ulong($config['dhcpd'][$if]['range']['from']); + $dynsubnet_end = ip2ulong($config['dhcpd'][$if]['range']['to']); + if ((ip2ulong($_POST['ipaddr']) >= $dynsubnet_start) && + (ip2ulong($_POST['ipaddr']) <= $dynsubnet_end)) { + $input_errors[] = sprintf(gettext("The IP address must not be within the DHCP range for this interface.")); + } + + foreach ($a_pools as $pidx => $p) { + if (is_inrange_v4($_POST['ipaddr'], $p['range']['from'], $p['range']['to'])) { + $input_errors[] = gettext("The IP address must not be within the range configured on a DHCP pool for this interface."); + break; + } + } + + $lansubnet_start = ip2ulong(long2ip32(ip2long($ifcfgip) & gen_subnet_mask_long($ifcfgsn))); + $lansubnet_end = ip2ulong(long2ip32(ip2long($ifcfgip) | (~gen_subnet_mask_long($ifcfgsn)))); + if ((ip2ulong($_POST['ipaddr']) < $lansubnet_start) || + (ip2ulong($_POST['ipaddr']) > $lansubnet_end)) { + $input_errors[] = sprintf(gettext("The IP address must lie in the %s subnet."), $ifcfgdescr); + } + } + + if (($_POST['gateway'] && !is_ipaddrv4($_POST['gateway']))) { + $input_errors[] = gettext("A valid IP address must be specified for the gateway."); + } + if (($_POST['wins1'] && !is_ipaddrv4($_POST['wins1'])) || ($_POST['wins2'] && !is_ipaddrv4($_POST['wins2']))) { + $input_errors[] = gettext("A valid IP address must be specified for the primary/secondary WINS servers."); + } + + $parent_ip = get_interface_ip($POST['if']); + if (is_ipaddrv4($parent_ip) && $_POST['gateway']) { + $parent_sn = get_interface_subnet($_POST['if']); + if (!ip_in_subnet($_POST['gateway'], gen_subnet($parent_ip, $parent_sn) . "/" . $parent_sn) && !ip_in_interface_alias_subnet($_POST['if'], $_POST['gateway'])) { + $input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet."), $_POST['gateway']); + } + } + if (($_POST['dns1'] && !is_ipaddrv4($_POST['dns1'])) || + ($_POST['dns2'] && !is_ipaddrv4($_POST['dns2'])) || + ($_POST['dns3'] && !is_ipaddrv4($_POST['dns3'])) || + ($_POST['dns4'] && !is_ipaddrv4($_POST['dns4']))) { + $input_errors[] = gettext("A valid IP address must be specified for each of the DNS servers."); + } + + if ($_POST['deftime'] && (!is_numeric($_POST['deftime']) || ($_POST['deftime'] < 60))) { + $input_errors[] = gettext("The default lease time must be at least 60 seconds."); + } + if ($_POST['maxtime'] && (!is_numeric($_POST['maxtime']) || ($_POST['maxtime'] < 60) || ($_POST['maxtime'] <= $_POST['deftime']))) { + $input_errors[] = gettext("The maximum lease time must be at least 60 seconds and higher than the default lease time."); + } + if (($_POST['ddnsdomain'] && !is_domain($_POST['ddnsdomain']))) { + $input_errors[] = gettext("A valid domain name must be specified for the dynamic DNS registration."); + } + if (($_POST['ddnsdomain'] && !is_ipaddrv4($_POST['ddnsdomainprimary']))) { + $input_errors[] = gettext("A valid primary domain name server IP address must be specified for the dynamic domain name."); + } + if (($_POST['ddnsdomainkey'] && !$_POST['ddnsdomainkeyname']) || + ($_POST['ddnsdomainkeyname'] && !$_POST['ddnsdomainkey'])) { + $input_errors[] = gettext("You must specify both a valid domain key and key name."); + } + if ($_POST['domainsearchlist']) { + $domain_array=preg_split("/[ ;]+/", $_POST['domainsearchlist']); + foreach ($domain_array as $curdomain) { + if (!is_domain($curdomain)) { + $input_errors[] = gettext("A valid domain search list must be specified."); + break; + } + } + } + + if (($_POST['ntp1'] && !is_ipaddrv4($_POST['ntp1'])) || ($_POST['ntp2'] && !is_ipaddrv4($_POST['ntp2']))) { + $input_errors[] = gettext("A valid IP address must be specified for the primary/secondary NTP servers."); + } + if ($_POST['tftp'] && !is_ipaddrv4($_POST['tftp']) && !is_domain($_POST['tftp']) && !is_URL($_POST['tftp'])) { + $input_errors[] = gettext("A valid IP address or hostname must be specified for the TFTP server."); + } + if (($_POST['nextserver'] && !is_ipaddrv4($_POST['nextserver']))) { + $input_errors[] = gettext("A valid IP address must be specified for the network boot server."); + } + + if (!$input_errors) { + $mapent = array(); + $mapent['mac'] = $_POST['mac']; + $mapent['cid'] = $_POST['cid']; + $mapent['ipaddr'] = $_POST['ipaddr']; + $mapent['hostname'] = $_POST['hostname']; + $mapent['descr'] = $_POST['descr']; + $mapent['arp_table_static_entry'] = ($_POST['arp_table_static_entry']) ? true : false; + $mapent['filename'] = $_POST['filename']; + $mapent['rootpath'] = $_POST['rootpath']; + $mapent['defaultleasetime'] = $_POST['deftime']; + $mapent['maxleasetime'] = $_POST['maxtime']; + + unset($mapent['winsserver']); + if ($_POST['wins1']) { + $mapent['winsserver'][] = $_POST['wins1']; + } + if ($_POST['wins2']) { + $mapent['winsserver'][] = $_POST['wins2']; + } + + unset($mapent['dnsserver']); + if ($_POST['dns1']) { + $mapent['dnsserver'][] = $_POST['dns1']; + } + if ($_POST['dns2']) { + $mapent['dnsserver'][] = $_POST['dns2']; + } + if ($_POST['dns3']) { + $mapent['dnsserver'][] = $_POST['dns3']; + } + if ($_POST['dns4']) { + $mapent['dnsserver'][] = $_POST['dns4']; + } + + $mapent['gateway'] = $_POST['gateway']; + $mapent['domain'] = $_POST['domain']; + $mapent['domainsearchlist'] = $_POST['domainsearchlist']; + $mapent['ddnsdomain'] = $_POST['ddnsdomain']; + $mapent['ddnsdomainprimary'] = $_POST['ddnsdomainprimary']; + $mapent['ddnsdomainkeyname'] = $_POST['ddnsdomainkeyname']; + $mapent['ddnsdomainkey'] = $_POST['ddnsdomainkey']; + $mapent['ddnsupdate'] = ($_POST['ddnsupdate']) ? true : false; + + unset($mapent['ntpserver']); + if ($_POST['ntp1']) { + $mapent['ntpserver'][] = $_POST['ntp1']; + } + if ($_POST['ntp2']) { + $mapent['ntpserver'][] = $_POST['ntp2']; + } + + $mapent['tftp'] = $_POST['tftp']; + $mapent['ldap'] = $_POST['ldap']; + + if (isset($id) && $a_maps[$id]) { + $a_maps[$id] = $mapent; + } else { + $a_maps[] = $mapent; + } + staticmaps_sort($if); + + write_config(); + + if (isset($config['dhcpd'][$if]['enable'])) { + mark_subsystem_dirty('staticmaps'); + if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic'])) { + mark_subsystem_dirty('hosts'); + } + if (isset($config['unbound']['enable']) && isset($config['unbound']['regdhcpstatic'])) { + mark_subsystem_dirty('unbound'); + } + } + + header("Location: services_dhcp.php?if={$if}"); + exit; + } +} + +// Get our MAC address +$ip = $_SERVER['REMOTE_ADDR']; +$mymac = `/usr/sbin/arp -an | grep '('{$ip}')' | cut -d" " -f4`; +$mymac = str_replace("\n","",$mymac); + +$closehead = false; +$pgtitle = array(gettext("Services"), gettext("DHCP"), gettext("Edit static mapping")); +$shortcut_section = "dhcp"; + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section(sprintf("Static DHCP Mapping on %s", $ifcfgdescr)); + +$macaddress = new Form_Input( + 'mac', + 'MAC Address', + 'text', + $pconfig['mac'], + ['placeholder' => 'xx:xx:xx:xx:xx:xx'] +); + +$btnmymac = new Form_Button( + 'btnmymac', + 'Copy My MAC' + ); + +$btnmymac->removeClass('btn-primary')->addClass('btn-success btn-sm'); + +$group = new Form_Group('MAC controls'); +$group->add($macaddress); +$group->add($btnmymac); +$group->setHelp('MAC address (6 hex octets separated by colons)'); +$section->add($group); + +$section->addInput(new Form_Input( + 'cid', + 'Client Identifier', + 'text', + $pconfig['cid'] +)); + +$section->addInput(new Form_IpAddress( + 'ipaddr', + 'IP Address', + $pconfig['ipaddr'] +))->setHelp('If an IPv4 address is entered, the address must be outside of the pool.' . '<br />' . + 'If no IPv4 address is given, one will be dynamically allocated from the pool.'); + +$section->addInput(new Form_Input( + 'hostnme', + 'Hostname', + 'text', + $pconfig['hostname'] +))->setHelp('Name of the host, without domain part.'); + +if($netboot_enabled) { + $section->addInput(new Form_Input( + 'filename', + 'Netboot filename', + 'text', + $pconfig['filename'] + ))->setHelp('Name of the file that should be loaded when this host boots off of the network, overrides setting on main page.'); + + $section->addInput(new Form_Input( + 'rootpath', + 'Root Path', + 'text', + $pconfig['rootpath'] + ))->setHelp('Enter the root-path-string, overrides setting on main page.'); +} + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['hostname'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$section->addInput(new Form_Checkbox( + 'arp_table_static_entry', + 'ARP Table Static Entry', + 'Create an ARP Table Static Entry for this MAC & IP Address pair.', + $pconfig['arp_table_static_entry'] +)); + +$group = new Form_Group('WINS Servers'); + +$group->add(new Form_Input( + 'wins1', + null, + 'text', + $pconfig['wins1'], + ['placeholder' => 'WINS 1'] +)); + +$group->add(new Form_Input( + 'win2', + null, + 'text', + $pconfig['win2'], + ['placeholder' => 'WINS 2'] +)); + +$section->add($group); +$group = new Form_Group('DNS Servers'); + +$group->add(new Form_Input( + 'dns1', + null, + 'text', + $pconfig['dns1'], + ['placeholder' => 'DNS1 1'] +)); + +$group->add(new Form_Input( + 'dns2', + null, + 'text', + $pconfig['dns2'], + ['placeholder' => 'DNS 2'] +)); + +$group->add(new Form_Input( + 'dns3', + null, + 'text', + $pconfig['dns3'], + ['placeholder' => 'DNS 3'] +)); + +$group->add(new Form_Input( + 'dns4', + null, + 'text', + $pconfig['dns4'], + ['placeholder' => 'DNS 4'] +)); + +$group->setHelp('NOTE: leave blank to use the system default DNS servers - this interface\'s IP if DNS forwarder is enabled, otherwise the servers configured on the General page.'); + +$section->add($group); + +$section->addInput(new Form_Input( + 'gateway', + 'Gateway', + 'text', + $pconfig['gateway'] +))->setHelp('The default is to use the IP on this interface of the firewall as the gateway. Specify an alternate gateway here if this is not the correct gateway for your network.'); + +$section->addInput(new Form_Input( + 'domain', + 'Domain name', + 'text', + $pconfig['domain'] +))->setHelp('The default is to use the domain name of this system as the default domain name provided by DHCP. You may specify an alternate domain name here. '); + +$section->addInput(new Form_Input( + 'domainsearchlist', + 'Domain search list', + 'text', + $pconfig['domainsearchlist'] +))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator'); + +$section->addInput(new Form_Input( + 'deftime', + 'Default lease time (Seconds)', + 'text', + $pconfig['deftime'] +))->setHelp('Used for clients that do not ask for a specific expiration time. The default is 7200 seconds.'); + +$section->addInput(new Form_Input( + 'maxtime', + 'Maximum lease time (Seconds)', + 'text', + $pconfig['maxtime'] +))->setHelp('This is the maximum lease time for clients that ask for a specific expiration time. The default is 86400 seconds.'); + +$btndyndns = new Form_Button( + 'btndyndns', + 'Advanced' +); + +$btndyndns->removeClass('btn-primary')->addClass('btn-success btn-sm'); + +$section->addInput(new Form_StaticText( + 'Dynamic DNS', + $btndyndns . ' ' . 'Show dynamic DNS settings' +)); + +$section->addInput(new Form_Checkbox( + 'ddnsupdate', + 'DHCP Registration', + 'Enable registration of DHCP client names in DNS.', + $pconfig['ddnsupdate'] +)); + +$section->addInput(new Form_Input( + 'ddnsdomain', + 'DDNS Domain', + 'text', + $pconfig['ddnsdomain'] +))->setHelp('Leave blank to disable dynamic DNS registration. Enter the dynamic DNS domain which will be used to register client names in the DNS server.'); + +$section->addInput(new Form_IpAddress( + 'ddnsdomainprimary', + 'DDNS Server IP', + $pconfig['ddnsdomainprimary'] +))->setHelp('Enter the primary domain name server IP address for the dynamic domain name.'); + +$section->addInput(new Form_Input( + 'ddnsdomainkeyname', + 'DDNS Domain Key name', + 'text', + $pconfig['ddnsdomainkeyname'] +))->setHelp('Enter the dynamic DNS domain key name which will be used to register client names in the DNS server.'); + +$section->addInput(new Form_Input( + 'ddnsdomainkey', + 'DDNS Domain Key secret', + 'text', + $pconfig['ddnsdomainkey'] +))->setHelp('Enter the dynamic DNS domain key secret which will be used to register client names in the DNS server.'); + +$btnntp = new Form_Button( + 'btnntp', + 'Advanced' +); + +$btnntp->removeClass('btn-primary')->addClass('btn-success btn-sm'); + +$section->addInput(new Form_StaticText( + 'NTP servers', + $btnntp . ' ' . 'Show NTP Configuration' +)); + +$group = new Form_Group('NTP Servers'); + +$group->add(new Form_Input( + 'ntp1', + 'NTP Server 1', + 'text', + $pconfig['ntp1'], + ['placeholder' => 'NTP 1'] +)); + +$group->add(new Form_Input( + 'ntp2', + 'NTP Server 1', + 'text', + $pconfig['ntp2'], + ['placeholder' => 'NTP 2'] +)); + +$group->addClass('ntpclass'); + +$section->add($group); + +$btntftp = new Form_Button( + 'btntftp', + 'Advanced' +); + +$btntftp->removeClass('btn-primary')->addClass('btn-success btn-sm'); + +$section->addInput(new Form_StaticText( + 'TFTP servers', + $btntftp . ' ' . 'Show TFTP Configuration' +)); + +$section->addInput(new Form_Input( + 'tftp', + 'TFTP Server', + 'text', + $pconfig['tftp'] +))->setHelp('Leave blank to disable. Enter a full hostname or IP for the TFTP server.'); + +$form->add($section); +print($form); +?> + +<script> +//<![CDATA[ +events.push(function(){ + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified icheckbox lives so that the input, its label and help text are hidden + // Checkboxes live inside <label></label> tags so we need another parent level + function hideCheckBox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + function hideDDNS(hide) { + hideCheckBox('ddnsupdate', hide); + hideInput('ddnsdomain', hide); + hideInput('ddnsdomainprimary', hide); + hideInput('ddnsdomainkeyname', hide); + hideInput('ddnsdomainkey', hide); + } + + // Make the ‘Copy My MAC’ button a plain button, not a submit button + $("#btnmymac").prop('type','button'); + + // On click, copy the hidden 'mymac' text to the 'mac' input + $("#btnmymac").click(function() { + $('#mac').val('<?=$mymac?>'); + }); + + // Make the ‘tftp’ button a plain button, not a submit button + $("#btntftp").prop('type','button'); + + // Show tftp controls + $("#btntftp").click(function() { + hideInput('tftp', false); + }); + + // Make the ‘ntp’ button a plain button, not a submit button + $("#btnntp").prop('type','button'); + + // Show ntp controls + $("#btnntp").click(function() { + hideClass('ntpclass', false); + }); + + // Make the ‘ddns’ button a plain button, not a submit button + $("#btndyndns").prop('type','button'); + + // Show ddns controls + $("#btndyndns").click(function() { + hideDDNS(false); + }); + + // On initial load + hideDDNS(true); + hideClass('ntpclass', true); + hideInput('tftp', true); +}); +//]]> +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_dhcp_relay.php b/src/usr/local/www/services_dhcp_relay.php new file mode 100644 index 0000000..8559f4b --- /dev/null +++ b/src/usr/local/www/services_dhcp_relay.php @@ -0,0 +1,229 @@ +<?php +/* + services_dhcp_relay.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 22003-2004 Justin Ellison <justin@techadvise.com> + * Copyright (c) 22010 Ermal Luçi + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: dhcprelay +*/ + +##|+PRIV +##|*IDENT=page-services-dhcprelay +##|*NAME=Services: DHCP Relay page +##|*DESCR=Allow access to the 'Services: DHCP Relay' page. +##|*MATCH=services_dhcp_relay.php* +##|-PRIV + +require("guiconfig.inc"); +require('classes/Form.class.php'); + +$pconfig['enable'] = isset($config['dhcrelay']['enable']); + +if (empty($config['dhcrelay']['interface'])) { + $pconfig['interface'] = array(); +} else { + $pconfig['interface'] = explode(",", $config['dhcrelay']['interface']); +} + +$pconfig['agentoption'] = isset($config['dhcrelay']['agentoption']); + +$iflist = array_intersect_key( + get_configured_interface_with_descr(), + array_flip( + array_filter( + array_keys(get_configured_interface_with_descr()), + function($if) { + return is_ipaddr(get_interface_ip($if)); + } + ) + ) +); + +/* set the enabled flag which will tell us if DHCP server is enabled + * on any interface. We will use this to disable dhcp-relay since + * the two are not compatible with each other. + */ +$dhcpd_enabled = false; +if (is_array($config['dhcpd'])) { + foreach ($config['dhcpd'] as $dhcpif => $dhcp) { + if (isset($dhcp['enable']) && isset($config['interfaces'][$dhcpif]['enable'])) { + $dhcpd_enabled = true; + break; + } + } +} + +if ($_POST) { + unset($input_errors); + + $pconfig = $_POST; + + /* input validation */ + if ($_POST['enable']) { + $reqdfields = explode(" ", "server interface"); + $reqdfieldsn = array(gettext("Destination Server"), gettext("Interface")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + $svrlist = ''; + + if ($_POST['server']) { + foreach($_POST['server'] as $checksrv => $srv) { + if (!is_ipaddr($srv[0])) + $input_errors[] = gettext("A valid Destination Server IP address must be specified."); + + if(!empty($srv[0])) { // Filter out any empties + if(!empty($svrlist)) + $svrlist .= ','; + + $svrlist .= $srv[0]; + } + } + } + } + + // Now $svrlist is a comma separated list of servers ready to save to the config system + $pconfig['server'] = $svrlist; + + if (!$input_errors) { + $config['dhcrelay']['enable'] = $_POST['enable'] ? true : false; + $config['dhcrelay']['interface'] = implode(",", $_POST['interface']); + $config['dhcrelay']['agentoption'] = $_POST['agentoption'] ? true : false; + $config['dhcrelay']['server'] = $pconfig['server']; + + write_config(); + + $retval = 0; + $retval = services_dhcrelay_configure(); + $savemsg = get_std_save_message($retval); + + } +} + +$closehead = false; +$pgtitle = array(gettext("Services"), gettext("DHCP Relay")); +$shortcut_section = "dhcp"; +include("head.inc"); + +if ($dhcpd_enabled) { + echo '<div class="alert alert-danger">DHCP Server is currently enabled. Cannot enable the DHCP Relay service while the DHCP Server is enabled on any interface.</div>'; + include("foot.inc"); + exit; +} + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +$form = new Form; + +$section = new Form_Section('DHCP Relay configuration'); + +$section->addInput(new Form_Checkbox( + 'enable', + 'Enable', + 'Enable DHCP relay on interface', + $pconfig['enable'] +))->toggles('.form-group:not(:first-child)'); + +$section->addInput(new Form_Select( + 'interface', + 'Interface(s)', + $pconfig['interface'], + $iflist, + true +))->setHelp('Interfaces without an IP address will not be shown.'); + +$section->addInput(new Form_Checkbox( + 'agentoption', + '', + 'Append circuit ID and agent ID to requests', + 'yes', + $pconfig['agentoption'] +))->setHelp( + 'If this is checked, the DHCP relay will append the circuit ID (%s interface number) and the agent ID to the DHCP request.', + [$g['product_name']] +); + +//Small function to prevent duplicate code +function createDestinationServerInputGroup($value = null) +{ + $group = new Form_Group('Destination server'); + + $group->add(new Form_IpAddress( + 'server', + 'Destination server', + $value + ))->setWidth(4)->setHelp( + 'This is the IP address of the server to which DHCP requests are relayed.' + )->setIsRepeated(); + + $group->enableDuplication(null, true); // Buttons are in-line with the input + return $group; +} + +if (!isset($pconfig['server']) || count($pconfig['server']) < 1) + $section->add(createDestinationServerInputGroup()); +else { + foreach (explode(',', $pconfig['server']) as $server) { + $section->add(createDestinationServerInputGroup($server)); + } +} + +$form->add($section); +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_dhcpv6.php b/src/usr/local/www/services_dhcpv6.php new file mode 100644 index 0000000..b8a4909 --- /dev/null +++ b/src/usr/local/www/services_dhcpv6.php @@ -0,0 +1,1027 @@ +<?php +/* $Id$ */ +/* + services_dhcpv6.php + parts of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + part of pfSense (https://www.pfsense.org) + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /bin/rm + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-services-dhcpv6server +##|*NAME=Services: DHCPv6 server page +##|*DESCR=Allow access to the 'Services: DHCPv6 server' page. +##|*MATCH=services_dhcpv6.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("filter.inc"); + +if (!$g['services_dhcp_server_enable']) { + header("Location: /"); + exit; +} + +/* Fix failover DHCP problem + * http://article.gmane.org/gmane.comp.security.firewalls.pfsense.support/18749 + */ +ini_set("memory_limit", "64M"); + +$if = $_GET['if']; +if ($_POST['if']) { + $if = $_POST['if']; +} + +/* if OLSRD is enabled, allow WAN to house DHCP. */ +if ($config['installedpackages']['olsrd']) { + foreach ($config['installedpackages']['olsrd']['config'] as $olsrd) { + if ($olsrd['enable']) { + $is_olsr_enabled = true; + break; + } + } +} + +$iflist = get_configured_interface_with_descr(); +$iflist = array_merge($iflist, get_configured_pppoe_server_interfaces()); + +/* set the starting interface */ +if (!$if || !isset($iflist[$if])) { + foreach ($iflist as $ifent => $ifname) { + $oc = $config['interfaces'][$ifent]; + if ((is_array($config['dhcpdv6'][$ifent]) && !isset($config['dhcpdv6'][$ifent]['enable']) && !(is_ipaddrv6($oc['ipaddrv6']) && (!is_linklocal($oc['ipaddrv6'])))) || + (!is_array($config['dhcpdv6'][$ifent]) && !(is_ipaddrv6($oc['ipaddrv6']) && (!is_linklocal($oc['ipaddrv6']))))) { + continue; + } + $if = $ifent; + break; + } +} + +if (is_array($config['dhcpdv6'][$if])) { + /* DHCPv6 */ + if (is_array($config['dhcpdv6'][$if]['range'])) { + $pconfig['range_from'] = $config['dhcpdv6'][$if]['range']['from']; + $pconfig['range_to'] = $config['dhcpdv6'][$if]['range']['to']; + } + if (is_array($config['dhcpdv6'][$if]['prefixrange'])) { + $pconfig['prefixrange_from'] = $config['dhcpdv6'][$if]['prefixrange']['from']; + $pconfig['prefixrange_to'] = $config['dhcpdv6'][$if]['prefixrange']['to']; + $pconfig['prefixrange_length'] = $config['dhcpdv6'][$if]['prefixrange']['prefixlength']; + } + $pconfig['deftime'] = $config['dhcpdv6'][$if]['defaultleasetime']; + $pconfig['maxtime'] = $config['dhcpdv6'][$if]['maxleasetime']; + $pconfig['domain'] = $config['dhcpdv6'][$if]['domain']; + $pconfig['domainsearchlist'] = $config['dhcpdv6'][$if]['domainsearchlist']; + list($pconfig['wins1'], $pconfig['wins2']) = $config['dhcpdv6'][$if]['winsserver']; + list($pconfig['dns1'], $pconfig['dns2'], $pconfig['dns3'], $pconfig['dns4']) = $config['dhcpdv6'][$if]['dnsserver']; + $pconfig['enable'] = isset($config['dhcpdv6'][$if]['enable']); + $pconfig['ddnsdomain'] = $config['dhcpdv6'][$if]['ddnsdomain']; + $pconfig['ddnsdomainprimary'] = $config['dhcpdv6'][$if]['ddnsdomainprimary']; + $pconfig['ddnsdomainkeyname'] = $config['dhcpdv6'][$if]['ddnsdomainkeyname']; + $pconfig['ddnsdomainkey'] = $config['dhcpdv6'][$if]['ddnsdomainkey']; + $pconfig['ddnsupdate'] = isset($config['dhcpdv6'][$if]['ddnsupdate']); + list($pconfig['ntp1'], $pconfig['ntp2']) = $config['dhcpdv6'][$if]['ntpserver']; + $pconfig['tftp'] = $config['dhcpdv6'][$if]['tftp']; + $pconfig['ldap'] = $config['dhcpdv6'][$if]['ldap']; + $pconfig['netboot'] = isset($config['dhcpdv6'][$if]['netboot']); + $pconfig['bootfile_url'] = $config['dhcpdv6'][$if]['bootfile_url']; + $pconfig['netmask'] = $config['dhcpdv6'][$if]['netmask']; + $pconfig['numberoptions'] = $config['dhcpdv6'][$if]['numberoptions']; + $pconfig['dhcpv6leaseinlocaltime'] = $config['dhcpdv6'][$if]['dhcpv6leaseinlocaltime']; + if (!is_array($config['dhcpdv6'][$if]['staticmap'])) { + $config['dhcpdv6'][$if]['staticmap'] = array(); + } + $a_maps = &$config['dhcpdv6'][$if]['staticmap']; +} + +$ifcfgip = get_interface_ipv6($if); +$ifcfgsn = get_interface_subnetv6($if); + +/* set the enabled flag which will tell us if DHCP relay is enabled + * on any interface. We will use this to disable DHCP server since + * the two are not compatible with each other. + */ + +$dhcrelay_enabled = false; +$dhcrelaycfg = $config['dhcrelay6']; + +if (is_array($dhcrelaycfg)) { + foreach ($dhcrelaycfg as $dhcrelayif => $dhcrelayifconf) { + if (isset($dhcrelayifconf['enable']) && isset($iflist[$dhcrelayif]) && + (!link_interface_to_bridge($dhcrelayif))) { + $dhcrelay_enabled = true; + } + } +} + +if ($_POST) { + unset($input_errors); + + $old_dhcpdv6_enable = ($pconfig['enable'] == true); + $new_dhcpdv6_enable = ($_POST['enable'] ? true : false); + $dhcpdv6_enable_changed = ($old_dhcpdv6_enable != $new_dhcpdv6_enable); + + $pconfig = $_POST; + + $numberoptions = array(); + for ($x = 0; $x < 99; $x++) { + if (isset($_POST["number{$x}"]) && ctype_digit($_POST["number{$x}"])) { + $numbervalue = array(); + $numbervalue['number'] = htmlspecialchars($_POST["number{$x}"]); + $numbervalue['value'] = htmlspecialchars($_POST["value{$x}"]); + $numberoptions['item'][] = $numbervalue; + } + } + // Reload the new pconfig variable that the forum uses. + $pconfig['numberoptions'] = $numberoptions; + + /* input validation */ + if ($_POST['enable']) { + $reqdfields = explode(" ", "range_from range_to"); + $reqdfieldsn = array(gettext("Range begin"), gettext("Range end")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($_POST['prefixrange_from'] && !is_ipaddrv6($_POST['prefixrange_from']))) { + $input_errors[] = gettext("A valid range must be specified."); + } + if (($_POST['prefixrange_to'] && !is_ipaddrv6($_POST['prefixrange_to']))) { + $input_errors[] = gettext("A valid prefix range must be specified."); + } + if (($_POST['range_from'] && !is_ipaddrv6($_POST['range_from']))) { + $input_errors[] = gettext("A valid range must be specified."); + } + if (($_POST['range_to'] && !is_ipaddrv6($_POST['range_to']))) { + $input_errors[] = gettext("A valid range must be specified."); + } + if (($_POST['gateway'] && !is_ipaddrv6($_POST['gateway']))) { + $input_errors[] = gettext("A valid IPv6 address must be specified for the gateway."); + } + if (($_POST['dns1'] && !is_ipaddrv6($_POST['dns1'])) || + ($_POST['dns2'] && !is_ipaddrv6($_POST['dns2'])) || + ($_POST['dns3'] && !is_ipaddrv6($_POST['dns3'])) || + ($_POST['dns4'] && !is_ipaddrv6($_POST['dns4']))) { + $input_errors[] = gettext("A valid IPv6 address must be specified for each of the DNS servers."); + } + + if ($_POST['deftime'] && (!is_numeric($_POST['deftime']) || ($_POST['deftime'] < 60))) { + $input_errors[] = gettext("The default lease time must be at least 60 seconds."); + } + if ($_POST['maxtime'] && (!is_numeric($_POST['maxtime']) || ($_POST['maxtime'] < 60) || ($_POST['maxtime'] <= $_POST['deftime']))) { + $input_errors[] = gettext("The maximum lease time must be at least 60 seconds and higher than the default lease time."); + } + if (($_POST['ddnsdomain'] && !is_domain($_POST['ddnsdomain']))) { + $input_errors[] = gettext("A valid domain name must be specified for the dynamic DNS registration."); + } + if (($_POST['ddnsdomain'] && !is_ipaddrv4($_POST['ddnsdomainprimary']))) { + $input_errors[] = gettext("A valid primary domain name server IPv4 address must be specified for the dynamic domain name."); + } + if (($_POST['ddnsdomainkey'] && !$_POST['ddnsdomainkeyname']) || + ($_POST['ddnsdomainkeyname'] && !$_POST['ddnsdomainkey'])) { + $input_errors[] = gettext("You must specify both a valid domain key and key name."); + } + if ($_POST['domainsearchlist']) { + $domain_array=preg_split("/[ ;]+/", $_POST['domainsearchlist']); + foreach ($domain_array as $curdomain) { + if (!is_domain($curdomain)) { + $input_errors[] = gettext("A valid domain search list must be specified."); + break; + } + } + } + + if (($_POST['ntp1'] && !is_ipaddrv6($_POST['ntp1'])) || ($_POST['ntp2'] && !is_ipaddrv6($_POST['ntp2']))) { + $input_errors[] = gettext("A valid IPv6 address must be specified for the primary/secondary NTP servers."); + } + if (($_POST['domain'] && !is_domain($_POST['domain']))) { + $input_errors[] = gettext("A valid domain name must be specified for the DNS domain."); + } + if ($_POST['tftp'] && !is_ipaddr($_POST['tftp']) && !is_domain($_POST['tftp']) && !is_URL($_POST['tftp'])) { + $input_errors[] = gettext("A valid IPv6 address or hostname must be specified for the TFTP server."); + } + if (($_POST['bootfile_url'] && !is_URL($_POST['bootfile_url']))) { + $input_errors[] = gettext("A valid URL must be specified for the network bootfile."); + } + + // Disallow a range that includes the virtualip + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['interface'] == $if) { + if ($vip['subnetv6'] && is_inrange_v6($vip['subnetv6'], $_POST['range_from'], $_POST['range_to'])) { + $input_errors[] = sprintf(gettext("The subnet range cannot overlap with virtual IPv6 address %s."), $vip['subnetv6']); + } + } + } + } + + $noip = false; + if (is_array($a_maps)) { + foreach ($a_maps as $map) { + if (empty($map['ipaddrv6'])) { + $noip = true; + } + } + } + if (!$input_errors) { + /* make sure the range lies within the current subnet */ + $subnet_start = gen_subnetv6($ifcfgip, $ifcfgsn); + $subnet_end = gen_subnetv6_max($ifcfgip, $ifcfgsn); + + if (is_ipaddrv6($ifcfgip)) { + if ((!is_inrange_v6($_POST['range_from'], $subnet_start, $subnet_end)) || + (!is_inrange_v6($_POST['range_to'], $subnet_start, $subnet_end))) { + $input_errors[] = gettext("The specified range lies outside of the current subnet."); + } + } + /* "from" cannot be higher than "to" */ + if (inet_pton($_POST['range_from']) > inet_pton($_POST['range_to'])) { + $input_errors[] = gettext("The range is invalid (first element higher than second element)."); + } + + /* make sure that the DHCP Relay isn't enabled on this interface */ + if (isset($config['dhcrelay'][$if]['enable'])) { + $input_errors[] = sprintf(gettext("You must disable the DHCP relay on the %s interface before enabling the DHCP server."), $iflist[$if]); + } + + + /* Verify static mappings do not overlap: + - available DHCP range + - prefix delegation range (FIXME: still need to be completed) */ + $dynsubnet_start = inet_pton($_POST['range_from']); + $dynsubnet_end = inet_pton($_POST['range_to']); + + if (is_array($a_maps)) { + foreach ($a_maps as $map) { + if (empty($map['ipaddrv6'])) { + continue; + } + if ((inet_pton($map['ipaddrv6']) > $dynsubnet_start) && + (inet_pton($map['ipaddrv6']) < $dynsubnet_end)) { + $input_errors[] = sprintf(gettext("The DHCP range cannot overlap any static DHCP mappings.")); + break; + } + } + } + } + } + + if (!$input_errors) { + if (!is_array($config['dhcpdv6'][$if])) { + $config['dhcpdv6'][$if] = array(); + } + if (!is_array($config['dhcpdv6'][$if]['range'])) { + $config['dhcpdv6'][$if]['range'] = array(); + } + if (!is_array($config['dhcpdv6'][$if]['prefixrange'])) { + $config['dhcpdv6'][$if]['prefixrange'] = array(); + } + + $config['dhcpdv6'][$if]['range']['from'] = $_POST['range_from']; + $config['dhcpdv6'][$if]['range']['to'] = $_POST['range_to']; + $config['dhcpdv6'][$if]['prefixrange']['from'] = $_POST['prefixrange_from']; + $config['dhcpdv6'][$if]['prefixrange']['to'] = $_POST['prefixrange_to']; + $config['dhcpdv6'][$if]['prefixrange']['prefixlength'] = $_POST['prefixrange_length']; + $config['dhcpdv6'][$if]['defaultleasetime'] = $_POST['deftime']; + $config['dhcpdv6'][$if]['maxleasetime'] = $_POST['maxtime']; + $config['dhcpdv6'][$if]['netmask'] = $_POST['netmask']; + + unset($config['dhcpdv6'][$if]['winsserver']); + + unset($config['dhcpdv6'][$if]['dnsserver']); + if ($_POST['dns1']) { + $config['dhcpdv6'][$if]['dnsserver'][] = $_POST['dns1']; + } + if ($_POST['dns2']) { + $config['dhcpdv6'][$if]['dnsserver'][] = $_POST['dns2']; + } + if ($_POST['dns3']) { + $config['dhcpdv6'][$if]['dnsserver'][] = $_POST['dns3']; + } + if ($_POST['dns4']) { + $config['dhcpdv6'][$if]['dnsserver'][] = $_POST['dns4']; + } + + $config['dhcpdv6'][$if]['domain'] = $_POST['domain']; + $config['dhcpdv6'][$if]['domainsearchlist'] = $_POST['domainsearchlist']; + $config['dhcpdv6'][$if]['enable'] = ($_POST['enable']) ? true : false; + $config['dhcpdv6'][$if]['ddnsdomain'] = $_POST['ddnsdomain']; + $config['dhcpdv6'][$if]['ddnsdomainprimary'] = $_POST['ddnsdomainprimary']; + $config['dhcpdv6'][$if]['ddnsdomainkeyname'] = $_POST['ddnsdomainkeyname']; + $config['dhcpdv6'][$if]['ddnsdomainkey'] = $_POST['ddnsdomainkey']; + $config['dhcpdv6'][$if]['ddnsupdate'] = ($_POST['ddnsupdate']) ? true : false; + + unset($config['dhcpdv6'][$if]['ntpserver']); + if ($_POST['ntp1']) { + $config['dhcpdv6'][$if]['ntpserver'][] = $_POST['ntp1']; + } + if ($_POST['ntp2']) { + $config['dhcpdv6'][$if]['ntpserver'][] = $_POST['ntp2']; + } + + $config['dhcpdv6'][$if]['tftp'] = $_POST['tftp']; + $config['dhcpdv6'][$if]['ldap'] = $_POST['ldap']; + $config['dhcpdv6'][$if]['netboot'] = ($_POST['netboot']) ? true : false; + $config['dhcpdv6'][$if]['bootfile_url'] = $_POST['bootfile_url']; + $config['dhcpdv6'][$if]['dhcpv6leaseinlocaltime'] = $_POST['dhcpv6leaseinlocaltime']; + + // Handle the custom options rowhelper + if (isset($config['dhcpdv6'][$if]['numberoptions']['item'])) { + unset($config['dhcpdv6'][$if]['numberoptions']['item']); + } + + $config['dhcpdv6'][$if]['numberoptions'] = $numberoptions; + + write_config(); + + $retval = 0; + $retvaldhcp = 0; + $retvaldns = 0; + /* Stop DHCPv6 so we can cleanup leases */ + killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid"); + // dhcp_clean_leases(); + /* dnsmasq_configure calls dhcpd_configure */ + /* no need to restart dhcpd twice */ + if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic'])) { + $retvaldns = services_dnsmasq_configure(); + if ($retvaldns == 0) { + clear_subsystem_dirty('hosts'); + clear_subsystem_dirty('staticmaps'); + } + } else if (isset($config['unbound']['enable']) && isset($config['unbound']['regdhcpstatic'])) { + $retvaldns = services_unbound_configure(); + if ($retvaldns == 0) { + clear_subsystem_dirty('unbound'); + clear_subsystem_dirty('staticmaps'); + } + } else { + $retvaldhcp = services_dhcpd_configure(); + if ($retvaldhcp == 0) { + clear_subsystem_dirty('staticmaps'); + } + } + if ($dhcpdv6_enable_changed) { + $retvalfc = filter_configure(); + } + if ($retvaldhcp == 1 || $retvaldns == 1 || $retvalfc == 1) { + $retval = 1; + } + $savemsg = get_std_save_message($retval); + } +} + +if ($_GET['act'] == "del") { + if ($a_maps[$_GET['id']]) { + unset($a_maps[$_GET['id']]); + write_config(); + if (isset($config['dhcpdv6'][$if]['enable'])) { + mark_subsystem_dirty('staticmapsv6'); + if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstaticv6'])) { + mark_subsystem_dirty('hosts'); + } + } + header("Location: services_dhcpv6.php?if={$if}"); + exit; + } +} + +// Delete a row in the options table +if($_GET['act'] == "delopt") { + $idx = $_GET['id']; + + if($pconfig['numberoptions'] && is_array($pconfig['numberoptions']['item'][$idx])) { + unset($pconfig['numberoptions']['item'][$idx]); + } +} + +// Add an option row +if($_GET['act'] == "addopt") { + if(!is_array($pconfig['numberoptions']['item'])) + $pconfig['numberoptions']['item'] = array(); + + array_push($pconfig['numberoptions']['item'], array('number' => null, 'value' => null)); +} + +$closehead = false; +$pgtitle = array(gettext("Services"), gettext("DHCPv6 server")); +$shortcut_section = "dhcp6"; + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if ($dhcrelay_enabled) { + print_info_box(gettext("DHCP Relay is currently enabled. Cannot enable the DHCP Server service while the DHCP Relay is enabled on any interface."), 'danger'); + include("foot.inc"); + exit; +} + +if (is_subsystem_dirty('staticmaps')) + print_info_box_np(gettext('The static mapping configuration has been changed') . '.<br />' . gettext('You must apply the changes in order for them to take effect.')); + +/* active tabs */ +$tab_array = array(); +$tabscounter = 0; +$i = 0; +foreach ($iflist as $ifent => $ifname) { + $oc = $config['interfaces'][$ifent]; + if ((is_array($config['dhcpdv6'][$ifent]) && !isset($config['dhcpdv6'][$ifent]['enable']) && !(is_ipaddrv6($oc['ipaddrv6']) && (!is_linklocal($oc['ipaddrv6'])))) || + (!is_array($config['dhcpdv6'][$ifent]) && !(is_ipaddrv6($oc['ipaddrv6']) && (!is_linklocal($oc['ipaddrv6']))))) + continue; + + if ($ifent == $if) + $active = true; + else + $active = false; + + $tab_array[] = array($ifname, $active, "services_dhcpv6.php?if={$ifent}"); + $tabscounter++; +} +/* tack on PPPoE or PPtP servers here */ +/* pppoe server */ +if (is_array($config['pppoes']['pppoe'])) { + foreach($config['pppoes']['pppoe'] as $pppoe) { + if ($pppoe['mode'] == "server") { + $ifent = "poes". $pppoe['pppoeid']; + $ifname = strtoupper($ifent); + + if ($ifent == $if) + $active = true; + else + $active = false; + + $tab_array[] = array($ifname, $active, "services_dhcpv6.php?if={$ifent}"); + $tabscounter++; + } + } +} + +if (empty($tabs_array)) { + print_info_box(gettext("The DHCPv6 Server can only be enabled on interfaces configured with a static IPv6 address. This system has none."), 'danger'); + include("foot.inc"); + exit; +} + +$tab_array = array(); +$tab_array[] = array(gettext("DHCPv6 Server"), true, "services_dhcpv6.php?if={$if}"); +$tab_array[] = array(gettext("Router Advertisements"), false, "services_router_advertisements.php?if={$if}"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form(new Form_Button( + 'Submit', + 'Save' +)); + +$section = new Form_Section('DHCPv6 Options'); + +$section->addInput(new Form_Checkbox( + 'enable', + 'DHCPv6 Server', + 'Enable DHCPv6 server on interface ' . $iflist[$if], + $pconfig['enable'] +))->toggles('.form-group:not(:first-child)'); + +if(is_ipaddrv6($ifcfgip)) { + + $section->addInput(new Form_StaticText( + 'Subnet', + gen_subnetv6($ifcfgip, $ifcfgsn) + )); + + $section->addInput(new Form_StaticText( + 'Subnet Mask', + $ifcfgsn . ' bits' + )); + + $section->addInput(new Form_StaticText( + 'Available Range', + $range_from = gen_subnetv6($ifcfgip, $ifcfgsn) . ' to ' . gen_subnetv6_max($ifcfgip, $ifcfgsn) + )); +} + +if($is_olsr_enabled) { + $section->addInput(new Form_Select( + 'netmask', + 'Subnetmask', + $pconfig['netmask'], + array_combine(range(128, 1, -1), range(128, 1, -1)) + )); +} + +$f1 = new Form_Input( + 'range_from', + null, + 'text', + $pconfig['range_from'] +); + +$f1->setHelp('To'); + +$f2 = new Form_Input( + 'range_to', + null, + 'text', + $pconfig['range_to'] +); + +$f2->setHelp('From'); + +$group = new Form_Group('Range'); + +$group->add($f1); +$group->add($f2); + +$section->add($group); + +$f1 = new Form_Input( + 'prefix_from', + null, + 'text', + $pconfig['prefix_from'] +); + +$f1->setHelp('To'); + +$f2 = new Form_Input( + 'prefix_to', + null, + 'text', + $pconfig['prefix_to'] +); + +$f2->setHelp('From'); +$group = new Form_Group('Prefix Delegation Range'); + +$group->add($f1); +$group->add($f2); + +$section->add($group); + +$section->addInput(new Form_Select( + 'prefixrange_length', + 'Prefix Delegation Size', + $pconfig['prefixrange_length'], + array( + '48' => '48', + '52' => '52', + '56' => '56', + '60' => '60', + '62' => '62', + '63' => '63', + '64' => '64' + ) +))->setHelp('You can define a Prefix range here for DHCP Prefix Delegation. This allows for assigning networks to subrouters. The start and end of the range must end on boundaries of the prefix delegation size.'); + +$group = new Form_Group('DNS Servers'); + +for($i=1;$i<=4; $i++) { + $group->add(new Form_input( + 'dns' . $i, + null, + 'text', + $pconfig['dns' . $i] + ))->setHelp('DNS ' . $i); +} + +$group->setHelp('Leave blank to use the system default DNS servers,this interface\'s IP if DNS forwarder is enabled, or the servers configured on the "General" page.'); +$section->add($group); + +$section->addInput(new Form_Input( + 'domain', + 'Domain Name', + 'text', + $pconfig['domain'] +))->setHelp('The default is to use the domain name of this system as the default domain name provided by DHCP. You may specify an alternate domain name here. '); + +$section->addInput(new Form_Input( + 'domainsearchlist', + 'Domain search list', + 'text', + $pconfig['domainsearchlist'] +))->setHelp('The DHCP server can optionally provide a domain search list. Use the semicolon character as separator'); + +$section->addInput(new Form_Input( + 'deftime', + 'Default lease time', + 'text', + $pconfig['deftime'] +))->setHelp('Seconds . Used for clients that do not ask for a specific expiration time. ' . ' <br />' . + 'The default is 7200 seconds.'); + +$section->addInput(new Form_Input( + 'maxtime', + 'Max lease time', + 'text', + $pconfig['maxtime'] +))->setHelp('Maximum lease time for clients that ask for a specific expiration time.' . ' <br />' . + 'The default is 86400 seconds.'); + +$section->addInput(new Form_Checkbox( + 'dhcpv6leaseinlocaltime', + 'Time Format Change', + 'Change DHCPv6 display lease time from UTC to local time', + $pconfig['dhcpv6leaseinlocaltime'] +))->setHelp('By default DHCPv6 leases are displayed in UTC time. ' . + 'By checking this box DHCPv6 lease time will be displayed in local time and set to time zone selected. ' . + 'This will be used for all DHCPv6 interfaces lease time.'); + +$btndyndns = new Form_Button( + 'btndyndns', + 'Advanced' +); + +$btndyndns->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'Dynamic DNS', + $btndyndns . ' ' . 'Show dynamic DNS settings' +)); + +$section->addInput(new Form_Checkbox( + 'ddnsupdate', + 'DHCP Registration', + 'Enable registration of DHCP client names in DNS.', + $pconfig['ddnsupdate'] +)); + +$section->addInput(new Form_Input( + 'ddnsdomain', + 'DDNS Domain', + 'text', + $pconfig['ddnsdomain'] +))->setHelp('Leave blank to disable dynamic DNS registration. Enter the dynamic DNS domain which will be used to register client names in the DNS server.'); + +$section->addInput(new Form_IpAddress( + 'ddnsdomainprimary', + 'DDNS Server IP', + $pconfig['ddnsdomainprimary'] +))->setHelp('Enter the primary domain name server IP address for the dynamic domain name.'); + +$section->addInput(new Form_Input( + 'ddnsdomainkeyname', + 'DDNS Domain Key name', + 'text', + $pconfig['ddnsdomainkeyname'] +))->setHelp('Enter the dynamic DNS domain key name which will be used to register client names in the DNS server.'); + +$section->addInput(new Form_Input( + 'ddnsdomainkey', + 'DDNS Domain Key secret', + 'text', + $pconfig['ddnsdomainkey'] +))->setHelp('Enter the dynamic DNS domain key secret which will be used to register client names in the DNS server.'); + +$btnntp = new Form_Button( + 'btnntp', + 'Advanced' +); + +$btnntp->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'NTP servers', + $btnntp . ' ' . 'Show NTP Configuration' +)); + +$group = new Form_Group('NTP Servers'); + +$group->add(new Form_Input( + 'ntp1', + 'NTP Server 1', + 'text', + $pconfig['ntp1'], + ['placeholder' => 'NTP 1'] +)); + +$group->add(new Form_Input( + 'ntp2', + 'NTP Server 1', + 'text', + $pconfig['ntp2'], + ['placeholder' => 'NTP 2'] +)); + +$group->addClass('ntpclass'); + +$section->add($group); + +$btnldap = new Form_Button( + 'btnldap', + 'Advanced' +); + +$btnldap->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'LDAP', + $btnldap . ' ' . 'Show LDAP Configuration' +)); + +$section->addInput(new Form_Input( + 'ldap', + 'LDAP URI', + 'text', + $pconfig['ldap'] +)); + +$btnnetboot = new Form_Button( + 'btnnetboot', + 'Advanced' +); + +$btnnetboot->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'Network booting', + $btnnetboot . ' ' . 'Show Netwok booting' +)); + +$section->addInput(new Form_Checkbox( + 'shownetboot', + 'Network booting', + 'Enable Network Booting', + $pconfig['shownetboot'] +)); + +$section->addInput(new Form_Input( + 'bootfile_url', + 'Bootfile URL', + 'text', + $pconfig['bootfile_url'] +)); + +$btnadnl = new Form_Button( + 'btnadnl', + 'Advanced' +); + +$btnadnl->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'Additional BOOTP/DHCP Options', + $btnadnl . ' ' . 'Aditional BOOTP/DHCP Options' +)); + +$form->add($section); + +$title = 'Show Additional BOOTP/DHCP Options'; + +if($pconfig['numberoptions']) { + $counter = 0; + $last = count($pconfig['numberoptions']['item']) - 1; + + foreach($pconfig['numberoptions']['item'] as $item) { + $group = new Form_Group(null); + + $group->add(new Form_Input( + 'number' . $counter, + null, + 'text', + $item['number'] + ))->setHelp($counter == $last ? 'Number':null); + + $group->add(new Form_Input( + 'value' . $counter, + null, + 'text', + $item['value'] + ))->setHelp($counter == $last ? 'Value':null); + + $btn = new Form_Button( + 'btn' . $counter, + 'Delete', + 'services_dhcpv6.php?if=' . $if . '&act=delopt' . '&id=' . $counter + ); + + $btn->removeClass('btn-primary')->addClass('btn-danger btn-xs adnlopt'); + $group->addClass('adnlopt'); + $group->add($btn); + $section->add($group); + $counter++; + } +} + +$btnaddopt = new Form_Button( + 'btnaddopt', + 'Add Option', + 'services_dhcpv6.php?if=' . $if . '&act=addopt' +); + +$btnaddopt->removeClass('btn-primary')->addClass('btn-success btn-sm'); + +$section->addInput($btnaddopt); + +$section->addInput(new Form_Input( + 'if', + null, + 'hidden', + $if +)); + +print($form); + +print_info_box(gettext('The DNS servers entered in ') . '<a href="system.php">' . gettext(' System: General setup') . '</a>' . + gettext(' (or the ') . '<a href="services_dnsmasq.php"/>' . gettext('DNS forwarder') . '</a>, ' . gettext('if enabled) ') . + gettext('will be assigned to clients by the DHCP server.') . '<br />' . + gettext('The DHCP lease table can be viewed on the ') . '<a href="status_dhcpv6_leases.php">' . + gettext('Status: DHCPv6 leases') . '</a>' . gettext(' page.')); +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">DHCPv6 Static Mappings for this interface.</h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("DUID")?></th> + <th><?=gettext("IPv6 address")?></th> + <th><?=gettext("Hostname")?></th> + <th><?=gettext("Description")?></th> + <th><!-- Buttons --></th> + </tr> + </thead> + <tbody> +<?php +if(is_array($a_maps)): + $i = 0; + foreach ($a_maps as $mapent): + if($mapent['duid'] != "" or $mapent['ipaddrv6'] != ""): +?> + <tr> + <td> + <?=htmlspecialchars($mapent['duid'])?> + </td> + <td> + <?=htmlspecialchars($mapent['ipaddrv6'])?> + </td> + <td> + <?=htmlspecialchars($mapent['hostname'])?> + </td> + <td> + <?=htmlspecialchars($mapent['descr'])?> + </td> + <td> + <a href="services_dhcpv6_edit.php?if=<?=$if?>&id=<?=$i?>" class="btn btn-info btn-xs"/>Edit</a> + <a href="services_dhcpv6.php?if=<?=$if?>&act=del&id=<?=$i?>" class="btn btn-danger btn-xs"/>Delete</a> + </td> + </tr> +<?php + endif; + $i++; + endforeach; +endif; +?> + </tbody> + </table> + </div> +</div> + +<nav class="action-buttons"> + <a href="services_dhcpv6_edit.php?if=<?=$if?>" class="btn btn-sm btn-success"/>Add</a> +</nav> + +<script> +//<![CDATA[ +events.push(function(){ + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified icheckbox lives so that the input, its label and help text are hidden + // Checkboxes live inside <label></label> tags so we need another parent level + function hideCheckBox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + function hideDDNS(hide) { + hideCheckBox('ddnsupdate', hide); + hideInput('ddnsdomain', hide); + hideInput('ddnsdomainprimary', hide); + hideInput('ddnsdomainkeyname', hide); + hideInput('ddnsdomainkey', hide); + } + + // Make the ‘Copy My MAC’ button a plain button, not a submit button + $("#btnmymac").prop('type','button'); + + // On click, copy the hidden 'mymac' text to the 'mac' input + $("#btnmymac").click(function() { + $('#mac').val('<?=$mymac?>'); + }); + + // Make the ‘tftp’ button a plain button, not a submit button + $("#btntftp").prop('type','button'); + + // Show tftp controls + $("#btntftp").click(function() { + hideInput('tftp', false); + }); + + // Make the ‘ntp’ button a plain button, not a submit button + $("#btnntp").prop('type','button'); + + // Show ntp controls + $("#btnntp").click(function() { + hideClass('ntpclass', false); + }); + + // Make the ‘ddns’ button a plain button, not a submit button + $("#btndyndns").prop('type','button'); + + // Show ddns controls + $("#btndyndns").click(function() { + hideDDNS(false); + }); + + // Make the ‘ldap’ button a plain button, not a submit button + $("#btnldap").prop('type','button'); + + // Show ldap controls + $("#btnldap").click(function() { + hideInput('ldap', false); + }); + + // Make the ‘netboot’ button a plain button, not a submit button + $("#btnnetboot").prop('type','button'); + + // Show netboot controls + $("#btnnetboot").click(function() { + hideInput('bootfile_url', false); + hideCheckBox('shownetboot', false); + }); + + // Make the ‘aditional options’ button a plain button, not a submit button + $("#btnadnl").prop('type','button'); + + // Show aditional controls + $("#btnadnl").click(function() { + hideClass('adnlopt', false); + hideInput('btnaddopt', false); + }); + + // On initial load + hideDDNS(true); + hideClass('ntpclass', true); + hideInput('tftp', true); + hideInput('ldap', true); + hideInput('bootfile_url', true); + hideCheckBox('shownetboot', true); + hideClass('adnlopt', true); + hideInput('btnaddopt', true); +}); +//]]> +</script> + +<?php include('foot.inc');
\ No newline at end of file diff --git a/src/usr/local/www/services_dhcpv6_edit.php b/src/usr/local/www/services_dhcpv6_edit.php new file mode 100644 index 0000000..a6df8db --- /dev/null +++ b/src/usr/local/www/services_dhcpv6_edit.php @@ -0,0 +1,276 @@ +<?php +/* $Id$ */ +/* + services_dhcpv6_edit.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2011 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/sbin/arp + pfSense_MODULE: dhcpserver +*/ + +##|+PRIV +##|*IDENT=page-services-dhcpserverv6-editstaticmapping +##|*NAME=Services: DHCPv6 Server : Edit static mapping page +##|*DESCR=Allow access to the 'Services: DHCPv6 Server : Edit static mapping' page. +##|*MATCH=services_dhcpv6_edit.php* +##|-PRIV + +function staticmapcmp($a, $b) { + return ipcmp($a['ipaddrv6'], $b['ipaddrv6']); +} + +function staticmaps_sort($ifgui) { + global $g, $config; + + usort($config['dhcpdv6'][$ifgui]['staticmap'], "staticmapcmp"); +} + +require_once('globals.inc'); + +if(!$g['services_dhcp_server_enable']) { + header("Location: /"); + exit; +} + +require("guiconfig.inc"); + +$if = $_GET['if']; +if ($_POST['if']) { + $if = $_POST['if']; +} + +if (!$if) { + header("Location: services_dhcpv6.php"); + exit; +} + +if (!is_array($config['dhcpdv6'])) { + $config['dhcpdv6'] = array(); +} +if (!is_array($config['dhcpdv6'][$if])) { + $config['dhcpdv6'][$if] = array(); +} +if (!is_array($config['dhcpdv6'][$if]['staticmap'])) { + $config['dhcpdv6'][$if]['staticmap'] = array(); +} + +$netboot_enabled = isset($config['dhcpdv6'][$if]['netboot']); +$a_maps = &$config['dhcpdv6'][$if]['staticmap']; +$ifcfgipv6 = get_interface_ipv6($if); +$ifcfgsnv6 = get_interface_subnetv6($if); +$ifcfgdescr = convert_friendly_interface_to_friendly_descr($if); + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_maps[$id]) { + $pconfig['duid'] = $a_maps[$id]['duid']; + $pconfig['hostname'] = $a_maps[$id]['hostname']; + $pconfig['ipaddrv6'] = $a_maps[$id]['ipaddrv6']; + $pconfig['filename'] = $a_maps[$id]['filename']; + $pconfig['rootpath'] = $a_maps[$id]['rootpath']; + $pconfig['descr'] = $a_maps[$id]['descr']; +} else { + $pconfig['duid'] = $_GET['duid']; + $pconfig['hostname'] = $_GET['hostname']; + $pconfig['filename'] = $_GET['filename']; + $pconfig['rootpath'] = $a_maps[$id]['rootpath']; + $pconfig['descr'] = $_GET['descr']; +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "duid"); + $reqdfieldsn = array(gettext("DUID")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ($_POST['hostname']) { + preg_match("/\-\$/", $_POST['hostname'], $matches); + if ($matches) { + $input_errors[] = gettext("The hostname cannot end with a hyphen according to RFC952"); + } + if (!is_hostname($_POST['hostname'])) { + $input_errors[] = gettext("The hostname can only contain the characters A-Z, 0-9 and '-'."); + } else { + if (!is_unqualified_hostname($_POST['hostname'])) { + $input_errors[] = gettext("A valid hostname is specified, but the domain name part should be omitted"); + } + } + } + if (($_POST['ipaddrv6'] && !is_ipaddrv6($_POST['ipaddrv6']))) { + $input_errors[] = gettext("A valid IPv6 address must be specified."); + } + + if (empty($_POST['duid'])) { + $input_errors[] = gettext("A valid DUID must be specified."); + } + + /* check for overlaps */ + foreach ($a_maps as $mapent) { + if (isset($id) && ($a_maps[$id]) && ($a_maps[$id] === $mapent)) { + continue; + } + + if ((($mapent['hostname'] == $_POST['hostname']) && $mapent['hostname']) || ($mapent['duid'] == $_POST['duid'])) { + $input_errors[] = gettext("This Hostname, IP or DUID already exists."); + break; + } + } + + /* make sure it's not within the dynamic subnet */ + if ($_POST['ipaddrv6']) { + /* oh boy, we need to be able to somehow do this at some point. skip */ + } + + if (!$input_errors) { + $mapent = array(); + $mapent['duid'] = $_POST['duid']; + $mapent['ipaddrv6'] = $_POST['ipaddrv6']; + $mapent['hostname'] = $_POST['hostname']; + $mapent['descr'] = $_POST['descr']; + $mapent['filename'] = $_POST['filename']; + $mapent['rootpath'] = $_POST['rootpath']; + + if (isset($id) && $a_maps[$id]) { + $a_maps[$id] = $mapent; + } else { + $a_maps[] = $mapent; + } + staticmaps_sort($if); + + write_config(); + + if (isset($config['dhcpdv6'][$if]['enable'])) { + mark_subsystem_dirty('staticmaps'); + if (isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcpstatic'])) { + mark_subsystem_dirty('hosts'); + } + if (isset($config['unbound']['enable']) && isset($config['unbound']['regdhcpstatic'])) { + mark_subsystem_dirty('unbound'); + } + + } + + header("Location: services_dhcpv6.php?if={$if}"); + exit; + } +} + +$pgtitle = array(gettext("Services"), gettext("DHCPv6"), gettext("Edit static mapping")); +$shortcut_section = "dhcp6"; + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Static DHCPv6 Mapping'); + +$section->addInput(new Form_Input( + 'duid', + 'DUID', + 'text', + $pconfig['duid'], + ['placeholder' => 'DUID-LLT - ETH -- TIME --- ---- address ---- xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx'] +))->setHelp(gettext('Enter a DUID in the following format: ') . '<br />' . + 'DUID-LLT - ETH -- TIME --- ---- address ---- ' . + 'xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx'); + +$section->addInput(new Form_Input( + 'ipaddrv6', + 'Server 3', + 'text', + $pconfig['ipaddrv6'] +))->setHelp('If an IPv6 address is entered, the address must be outside of the pool.' . '<br />' . + 'If no IPv6 address is given, one will be dynamically allocated from the pool.'); + +$section->addInput(new Form_Input( + 'hostname', + 'Hostname', + 'text', + $pconfig['hostname'] +))->setHelp('Name of the host, without domain part.'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +if($netboot_enabled) { + $section->addInput(new Form_Input( + 'filename', + 'Netboot filename', + 'text', + $pconfig['remoteserver3'] + ))->setHelp('Name of the file that should be loaded when this host boots off of the network, overrides setting on main page.'); + + $section->addInput(new Form_Input( + 'rootpath', + 'Root path', + 'text', + $pconfig['remoteserver3'] + ))->setHelp('Enter the root-path string. This overrides setting on main page.'); +} + +if (isset($id) && $a_maps[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$section->addInput(new Form_Input( + 'if', + null, + 'hidden', + $if +)); + +$form->add($section); +print($form); + +include("foot.inc"); diff --git a/src/usr/local/www/services_dhcpv6_relay.php b/src/usr/local/www/services_dhcpv6_relay.php new file mode 100644 index 0000000..2fdbcbc --- /dev/null +++ b/src/usr/local/www/services_dhcpv6_relay.php @@ -0,0 +1,204 @@ +<?php +/* + services_dhcpv6_relay.php + + Copyright (C) 2003-2004 Justin Ellison <justin@techadvise.com>. + Copyright (C) 2010 Ermal Luçi + Copyright (C) 2010 Seth Mos + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: dhcpv6relay +*/ + +##|+PRIV +##|*IDENT=page-services-dhcpv6relay +##|*NAME=Services: DHCPv6 Relay page +##|*DESCR=Allow access to the 'Services: DHCPv6 Relay' page. +##|*MATCH=services_dhcpv6_relay.php* +##|-PRIV + +require("guiconfig.inc"); +require('classes/Form.class.php'); + +function filterDestinationServers(array $destinationServers) +{ + return array_unique( + array_filter($destinationServers) + ); +} + +$pconfig['enable'] = isset($config['dhcrelay6']['enable']); +if (empty($config['dhcrelay6']['interface'])) { + $pconfig['interface'] = array(); +} else { + $pconfig['interface'] = explode(",", $config['dhcrelay6']['interface']); +} + +$pconfig['server'] = filterDestinationServers( + explode(',', $config['dhcrelay6']['server']) +); + +$pconfig['agentoption'] = isset($config['dhcrelay6']['agentoption']); + +$iflist = array_intersect_key( + get_configured_interface_with_descr(), + array_flip( + array_filter( + array_keys(get_configured_interface_with_descr()), + function($if) { + return is_ipaddrv6(get_interface_ipv6($if)); + } + ) + ) +); + +/* set the enabled flag which will tell us if DHCP server is enabled + * on any interface. We will use this to disable dhcp-relay since + * the two are not compatible with each other. + */ +$dhcpd_enabled = false; +if (is_array($config['dhcpdv6'])) { + foreach ($config['dhcpdv6'] as $dhcp) { + if (isset($dhcp['enable']) && isset($config['interfaces'][$dhcpif]['enable'])) { + $dhcpd_enabled = true; + break; + } + } +} + +if ($_POST) { + + unset($input_errors); + + if ($_POST['server']) + $_POST['server'] = filterDestinationServers($_POST['server']); + + $pconfig = $_POST; + + /* input validation */ + if ($_POST['enable']) { + $reqdfields = explode(" ", "server interface"); + $reqdfieldsn = array(gettext("Destination Server"), gettext("Interface")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ($_POST['server']) { + foreach ($_POST['server'] as $srv) { + if (!is_ipaddrv6($srv)) + $input_errors[] = gettext("A valid Destination Server IPv6 address must be specified."); + } + } + } + + if (!$input_errors) { + $config['dhcrelay6']['enable'] = $_POST['enable'] ? true : false; + $config['dhcrelay6']['interface'] = implode(",", $_POST['interface']); + $config['dhcrelay6']['agentoption'] = $_POST['agentoption'] ? true : false; + $config['dhcrelay6']['server'] = $_POST['server']; + + write_config(); + + $retval = 0; + $retval = services_dhcrelay6_configure(); + $savemsg = get_std_save_message($retval); + } +} + +$closehead = false; +$pgtitle = array(gettext("Services"), gettext("DHCPv6 Relay")); +$shortcut_section = "dhcp6"; +include("head.inc"); + +if ($dhcpd_enabled) +{ + echo '<div class="alert alert-danger">DHCPv6 Server is currently enabled. Cannot enable the DHCPv6 Relay service while the DHCPv6 Server is enabled on any interface.</div>'; + include("foot.inc"); + exit; +} + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg); + +$form = new Form; + +$section = new Form_Section('DHCPv6 Relay configuration'); + +$section->addInput(new Form_Checkbox( + 'enable', + 'Enable', + 'Enable DHCPv6 relay on interface', + $pconfig['enable'] +))->toggles('.form-group:not(:first-child)'); + +$section->addInput(new Form_Select( + 'interface', + 'Interface(s)', + $pconfig['interface'], + $iflist, + true +))->setHelp('Interfaces without an IPv6 address will not be shown.'); + + +$section->addInput(new Form_Checkbox( + 'agentoption', + '', + 'Append circuit ID and agent ID to requests', + 'yes', + $pconfig['agentoption'] +))->setHelp( + 'If this is checked, the DHCPv6 relay will append the circuit ID (%s interface number) and the agent ID to the DHCPv6 request.', + [$g['product_name']] +); + +function createDestinationServerInputGroup($value = null) +{ + $group = new Form_Group('Destination server'); + $group->enableDuplication(); + + $group->add(new Form_IpAddress( + 'server', + 'Destination server', + $value + ))->setHelp( + 'This is the IPv6 address of the server to which DHCPv6 requests are relayed.' + )->setIsRepeated(); + + return $group; +} + +if (!isset($pconfig['server']) || count($pconfig['server']) < 1) + $section->add(createDestinationServerInputGroup()); +else + foreach ($pconfig['server'] as $idx => $server) + $section->add(createDestinationServerInputGroup($server)); + +$form->add($section); +print $form; + +include("foot.inc"); diff --git a/src/usr/local/www/services_dnsmasq.php b/src/usr/local/www/services_dnsmasq.php new file mode 100644 index 0000000..ac6b44c --- /dev/null +++ b/src/usr/local/www/services_dnsmasq.php @@ -0,0 +1,452 @@ +<?php +/* $Id$ */ +/* + services_dnsmasq.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Bob Zoller <bob@kludgebox.com> and Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: dnsforwarder +*/ + +##|+PRIV +##|*IDENT=page-services-dnsforwarder +##|*NAME=Services: DNS Forwarder page +##|*DESCR=Allow access to the 'Services: DNS Forwarder' page. +##|*MATCH=services_dnsmasq.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("system.inc"); + +$pconfig['enable'] = isset($config['dnsmasq']['enable']); +$pconfig['regdhcp'] = isset($config['dnsmasq']['regdhcp']); +$pconfig['regdhcpstatic'] = isset($config['dnsmasq']['regdhcpstatic']); +$pconfig['dhcpfirst'] = isset($config['dnsmasq']['dhcpfirst']); +$pconfig['strict_order'] = isset($config['dnsmasq']['strict_order']); +$pconfig['domain_needed'] = isset($config['dnsmasq']['domain_needed']); +$pconfig['no_private_reverse'] = isset($config['dnsmasq']['no_private_reverse']); +$pconfig['port'] = $config['dnsmasq']['port']; +$pconfig['custom_options'] = $config['dnsmasq']['custom_options']; + +$pconfig['strictbind'] = isset($config['dnsmasq']['strictbind']); +if (!empty($config['dnsmasq']['interface'])) { + $pconfig['interface'] = explode(",", $config['dnsmasq']['interface']); +} else { + $pconfig['interface'] = array(); +} + +if (!is_array($config['dnsmasq']['hosts'])) { + $config['dnsmasq']['hosts'] = array(); +} + +if (!is_array($config['dnsmasq']['domainoverrides'])) { + $config['dnsmasq']['domainoverrides'] = array(); +} + +$a_hosts = &$config['dnsmasq']['hosts']; +$a_domainOverrides = &$config['dnsmasq']['domainoverrides']; + +if ($_POST) { + $pconfig = $_POST; + unset($input_errors); + + $config['dnsmasq']['enable'] = ($_POST['enable']) ? true : false; + $config['dnsmasq']['regdhcp'] = ($_POST['regdhcp']) ? true : false; + $config['dnsmasq']['regdhcpstatic'] = ($_POST['regdhcpstatic']) ? true : false; + $config['dnsmasq']['dhcpfirst'] = ($_POST['dhcpfirst']) ? true : false; + $config['dnsmasq']['strict_order'] = ($_POST['strict_order']) ? true : false; + $config['dnsmasq']['domain_needed'] = ($_POST['domain_needed']) ? true : false; + $config['dnsmasq']['no_private_reverse'] = ($_POST['no_private_reverse']) ? true : false; + $config['dnsmasq']['custom_options'] = str_replace("\r\n", "\n", $_POST['custom_options']); + $config['dnsmasq']['strictbind'] = ($_POST['strictbind']) ? true : false; + + if (isset($_POST['enable']) && isset($config['unbound']['enable'])) { + if ($_POST['port'] == $config['unbound']['port']) { + $input_errors[] = "The DNS Resolver is enabled using this port. Choose a non-conflicting port, or disable DNS Resolver."; + } + } + + if ($_POST['port']) { + if (is_port($_POST['port'])) { + $config['dnsmasq']['port'] = $_POST['port']; + } else { + $input_errors[] = gettext("You must specify a valid port number"); + } + } else if (isset($config['dnsmasq']['port'])) { + unset($config['dnsmasq']['port']); + } + + if (is_array($_POST['interface'])) { + $config['dnsmasq']['interface'] = implode(",", $_POST['interface']); + } elseif (isset($config['dnsmasq']['interface'])) { + unset($config['dnsmasq']['interface']); + } + + if ($config['dnsmasq']['custom_options']) { + $args = ''; + foreach (preg_split('/\s+/', $config['dnsmasq']['custom_options']) as $c) { + $args .= escapeshellarg("--{$c}") . " "; + } + exec("/usr/local/sbin/dnsmasq --test $args", $output, $rc); + if ($rc != 0) { + $input_errors[] = gettext("Invalid custom options"); + } + } + + if (!$input_errors) { + write_config(); + + $retval = 0; + $retval = services_dnsmasq_configure(); + $savemsg = get_std_save_message($retval); + + // Reload filter (we might need to sync to CARP hosts) + filter_configure(); + /* Update resolv.conf in case the interface bindings exclude localhost. */ + system_resolvconf_generate(); + /* Start or restart dhcpleases when it's necessary */ + system_dhcpleases_configure(); + + if ($retval == 0) { + clear_subsystem_dirty('hosts'); + } + } +} + +if ($_GET['act'] == "del") { + if ($_GET['type'] == 'host') { + if ($a_hosts[$_GET['id']]) { + unset($a_hosts[$_GET['id']]); + write_config(); + mark_subsystem_dirty('hosts'); + header("Location: services_dnsmasq.php"); + exit; + } + } + elseif ($_GET['type'] == 'doverride') { + if ($a_domainOverrides[$_GET['id']]) { + unset($a_domainOverrides[$_GET['id']]); + write_config(); + mark_subsystem_dirty('hosts'); + header("Location: services_dnsmasq.php"); + exit; + } + } +} + +function build_if_list() { + $interface_addresses = get_possible_listen_ips(true); + $iflist = array('options' => array(), 'selected' => array()); + + $iflist['options'][""] = "All"; + if (empty($pconfig['interface']) || empty($pconfig['interface'][0])) + array_push($iflist['selected'], ""); + + foreach ($interface_addresses as $laddr => $ldescr) { + $iflist['options'][$laddr] = htmlspecialchars($ldescr); + + if ($pconfig['interface'] && in_array($laddr, $pconfig['interface'])) + array_push($iflist['selected'], $laddr); + } + + unset($interface_addresses); + + return($iflist); +} + +$closehead = false; +$pgtitle = array(gettext("Services"), gettext("DNS forwarder")); +$shortcut_section = "forwarder"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('hosts')) + print_info_box_np(gettext("The DNS forwarder configuration has been changed") . ".<br />" . gettext("You must apply the changes in order for them to take effect.")); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('General DNS Forwarder Options'); + +$section->addInput(new Form_Checkbox( + 'enable', + 'Enable', + 'Enable DNS forwarder', + $pconfig['enable'] +))->toggles('.toggle-dhcp', 'disable'); + +$section->addInput(new Form_Checkbox( + 'regdhcp', + 'DHCP Registration', + 'Register DHCP leases in DNS forwarder', + $pconfig['regdhcp'] +))->setHelp(sprintf("If this option is set, then machines that specify". + " their hostname when requesting a DHCP lease will be registered". + " in the DNS forwarder, so that their name can be resolved.". + " You should also set the domain in %sSystem:". + " General setup%s to the proper value.",'<a href="system.php">','</a>')) + ->addClass('toggle-dhcp'); + +$section->addInput(new Form_Checkbox( + 'regdhcpstatic', + 'Static DHCP', + 'Register DHCP static mappings in DNS forwarder', + $pconfig['regdhcpstatic'] +))->setHelp(sprintf("If this option is set, then DHCP static mappings will ". + "be registered in the DNS forwarder, so that their name can be ". + "resolved. You should also set the domain in %s". + "System: General setup%s to the proper value.",'<a href="system.php">','</a>')) + ->addClass('toggle-dhcp'); + +$section->addInput(new Form_Checkbox( + 'dhcpfirst', + 'Prefer DHCP', + 'Resolve DHCP mappings first', + $pconfig['dhcpfirst'] +))->setHelp(sprintf("If this option is set, then DHCP mappings will ". + "be resolved before the manual list of names below. This only ". + "affects the name given for a reverse lookup (PTR).")) + ->addClass('toggle-dhcp'); + +$group = new Form_Group('DNS Query Forwarding'); + +$group->add(new Form_Checkbox( + 'strict_order', + 'DNS Query Forwarding', + 'Query DNS servers sequentially', + $pconfig['strict_order'] +))->setHelp(sprintf("If this option is set, %s DNS Forwarder (dnsmasq) will ". + "query the DNS servers sequentially in the order specified (<i>System - General Setup - DNS Servers</i>), ". + "rather than all at once in parallel. ", $g['product_name'])); + +$group->add(new Form_Checkbox( + 'domain_needed', + null, + 'Require domain', + $pconfig['domain_needed'] +))->setHelp(sprintf("If this option is set, %s DNS Forwarder (dnsmasq) will ". + "not forward A or AAAA queries for plain names, without dots or domain parts, to upstream name servers. ". + "If the name is not known from /etc/hosts or DHCP then a \"not found\" answer is returned. ", $g['product_name'])); + +$group->add(new Form_Checkbox( + 'no_private_reverse', + null, + 'Do not forward private reverse lookups', + $pconfig['no_private_reverse'] +))->setHelp(sprintf("If this option is set, %s DNS Forwarder (dnsmasq) will ". + "not forward reverse DNS lookups (PTR) for private addresses (RFC 1918) to upstream name servers. ". + "Any entries in the Domain Overrides section forwarding private \"n.n.n.in-addr.arpa\" names to a specific server are still forwarded. ". + "If the IP to name is not known from /etc/hosts, DHCP or a specific domain override then a \"not found\" answer is immediately returned. ", $g['product_name'])); + +$section->add($group); + +$section->addInput(new Form_Input( + 'port', + 'Listen Port', + 'number', + $pconfig['port'], + ['placeholder' => '53'] +))->setHelp('The port used for responding to DNS queries. It should normally be left blank unless another service needs to bind to TCP/UDP port 53.'); + +$iflist = build_if_list(); + +$section->addInput(new Form_Select( + 'interface', + 'Interfaces', + $iflist['selected'], + $iflist['options'], + true +))->setHelp('Interface IPs used by the DNS Forwarder for responding to queries from clients. If an interface has both IPv4 and IPv6 IPs, both are used. Queries to other interface IPs not selected below are discarded. ' . + 'The default behavior is to respond to queries on every available IPv4 and IPv6 address.'); + +$section->addInput(new Form_Checkbox( + 'strictbind', + 'Strict binding', + 'Strict interface binding', + $pconfig['strictbind'] +))->setHelp('If this option is set, the DNS forwarder will only bind to the interfaces containing the IP addresses selected above, ' . + 'rather than binding to all interfaces and discarding queries to other addresses.' . '<br /><br />' . + 'This option does NOT work with IPv6. If set, dnsmasq will not bind to IPv6 addresses.'); + +$section->addInput(new Form_TextArea( + 'custom_options', + 'Custom options', + $pconfig['custom_options'] +))->setHelp('Enter any additional options you would like to add to the dnsmasq configuration here, separated by a space or newline') + ->addClass('advanced'); + +$form->add($section); +print($form); + +print_info_box(sprintf("If the DNS forwarder is enabled, the DHCP". + " service (if enabled) will automatically serve the LAN IP". + " address as a DNS server to DHCP clients so they will use". + " the forwarder. The DNS forwarder will use the DNS servers". + " entered in %sSystem: General setup%s". + " or those obtained via DHCP or PPP on WAN if the "Allow". + " DNS server list to be overridden by DHCP/PPP on WAN"". + " is checked. If you don't use that option (or if you use". + " a static IP address on WAN), you must manually specify at". + " least one DNS server on the %sSystem:". + "General setup%s page.",'<a href="system.php">','</a>','<a href="system.php">','</a>')); +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2><?=gettext("Host Overrides")?></h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Host")?></th> + <th><?=gettext("Domain")?></th> + <th><?=gettext("IP")?></th> + <th><?=gettext("Description")?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php +foreach ($a_hosts as $i => $hostent): +?> + <tr> + <td> + <?=strtolower($hostent['host'])?> + </td> + <td> + <?=strtolower($hostent['domain'])?> + </td> + <td> + <?=$hostent['ip']?> + </td> + <td> + <?=htmlspecialchars($hostent['descr'])?> + </td> + <td> + <a href="services_dnsmasq_edit.php?id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext('Edit')?></a> + <a href="services_dnsmasq.php?type=host&act=del&id=<?=$i?>" class="btn btn-xs btn-danger"><?=gettext('Delete')?></a> + </td> + </tr> + +<?php + if ($hostent['aliases']['item'] && is_array($hostent['aliases']['item'])): + foreach ($hostent['aliases']['item'] as $i => $alias): +?> + <tr> + <td> + <?=strtolower($alias['host'])?> + </td> + <td> + <?=strtolower($alias['domain'])?> + </td> + <td> + Alias for <?=$hostent['host'] ? $hostent['host'] . '.' . $hostent['domain'] : $hostent['domain']?> + </td> + <td> + <?=htmlspecialchars($alias['description'])?> + </td> + <td> + <a href="services_dnsmasq_edit.php?id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext('Edit')?></a> + </td> + </tr> +<?php + endforeach; + endif; +endforeach; +?> + </tbody> + </table> + </div> +</div> + +<nav class="action-buttons"> + <a href="services_dnsmasq_edit.php" class="btn btn-sm btn-success"><?=gettext('Add')?></a> +</nav> + +<?php +print_info_box(gettext("Entries in this section override individual results from the forwarders.") . + gettext("Use these for changing DNS results or for adding custom DNS records.")); +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2><?=gettext("Domain Overrides")?></h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Domain")?></th> + <th><?=gettext("IP")?></th> + <th><?=gettext("Description")?></th> + <th></th> + </tr> + </thead> + + <tbody> +<?php +foreach ($a_domainOverrides as $i => $doment): +?> + <tr> + <td> + <?=strtolower($doment['domain'])?> + </td> + <td> + <?=$doment['ip']?> + </td> + <td> + <?=htmlspecialchars($doment['descr'])?> + </td> + <td> + <a href="services_dnsmasq_domainoverride_edit.php?id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext('Edit')?></a> + <a href="services_dnsmasq.php?act=del&type=doverride&id=<?=$i?>" class="btn btn-xs btn-danger"><?=gettext('Delete')?></a> + </td> + </tr> +<?php +endforeach; +?> + </tbody> + </table> + </div> +</div> + +<nav class="action-buttons"> + <a href="services_dnsmasq_domainoverride_edit.php" class="btn btn-sm btn-success"><?=gettext('Add')?></a> +</nav> + +<?php +print_info_box(gettext("Entries in this area override an entire domain, and subdomains, by specifying an". + " authoritative DNS server to be queried for that domain.")); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_dnsmasq_domainoverride_edit.php b/src/usr/local/www/services_dnsmasq_domainoverride_edit.php new file mode 100644 index 0000000..3bc37fa --- /dev/null +++ b/src/usr/local/www/services_dnsmasq_domainoverride_edit.php @@ -0,0 +1,183 @@ +<?php +/* + services_dnsmasq_domainoverride_edit.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2005 Bob Zoller <bob@kludgebox.com> and Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: dnsforwarder +*/ + +##|+PRIV +##|*IDENT=page-services-dnsforwarder-editdomainoverride +##|*NAME=Services: DNS Forwarder: Edit Domain Override page +##|*DESCR=Allow access to the 'Services: DNS Forwarder: Edit Domain Override' page. +##|*MATCH=services_dnsmasq_domainoverride_edit.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['dnsmasq']['domainoverrides'])) { + $config['dnsmasq']['domainoverrides'] = array(); +} + +$a_domainOverrides = &$config['dnsmasq']['domainoverrides']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_domainOverrides[$id]) { + $pconfig['domain'] = $a_domainOverrides[$id]['domain']; + if (is_ipaddr($a_domainOverrides[$id]['ip']) && ($a_domainOverrides[$id]['ip'] != '#')) { + $pconfig['ip'] = $a_domainOverrides[$id]['ip']; + } else { + $dnsmasqpieces = explode('@', $a_domainOverrides[$id]['ip'], 2); + $pconfig['ip'] = $dnsmasqpieces[0]; + $pconfig['dnssrcip'] = $dnsmasqpieces[1]; + } + $pconfig['descr'] = $a_domainOverrides[$id]['descr']; +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "domain ip"); + $reqdfieldsn = array(gettext("Domain"),gettext("IP address")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + function String_Begins_With($needle, $haystack) { + return (substr($haystack, 0, strlen($needle))==$needle); + } + + if (String_Begins_With(_msdcs, $_POST['domain'])) { + $subdomainstr = substr($_POST['domain'], 7); + + if ($subdomainstr && !is_domain($subdomainstr)) { + $input_errors[] = gettext("A valid domain must be specified after _msdcs."); + } + } + elseif ($_POST['domain'] && !is_domain($_POST['domain'])) { + $input_errors[] = gettext("A valid domain must be specified."); + } + + if ($_POST['ip'] && !is_ipaddr($_POST['ip']) && ($_POST['ip'] != '#') && ($_POST['ip'] != '!')) { + $input_errors[] = gettext("A valid IP address must be specified, or # for an exclusion or ! to not forward at all."); + } + + if ($_POST['dnssrcip'] && !in_array($_POST['dnssrcip'], get_configured_ip_addresses())) { + $input_errors[] = gettext("An interface IP address must be specified for the DNS query source."); + } + + if (!$input_errors) { + $doment = array(); + $doment['domain'] = $_POST['domain']; + + if (empty($_POST['dnssrcip'])) + $doment['ip'] = $_POST['ip']; + else + $doment['ip'] = $_POST['ip'] . "@" . $_POST['dnssrcip']; + + $doment['descr'] = $_POST['descr']; + + if (isset($id) && $a_domainOverrides[$id]) { + $a_domainOverrides[$id] = $doment; + } else { + $a_domainOverrides[] = $doment; + } + + $retval = services_dnsmasq_configure(); + + write_config(); + + header("Location: services_dnsmasq.php"); + exit; + } +} + +$pgtitle = array(gettext("Services"), gettext("DNS forwarder"), gettext("Edit Domain Override")); +$shortcut_section = "forwarder"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Domain override options'); + +$section->addInput(new Form_Input( + 'domain', + 'Domain', + 'text', + $pconfig['domain'] +))->setHelp('Domain to override (NOTE: this does not have to be a valid TLD!)' . '<br />' . + 'e.g.: test or mycompany.localdomain or 1.168.192.in-addr.arpa'); + +$section->addInput(new Form_IpAddress( + 'ip', + 'IP Address', + $pconfig['ip'] +))->setHelp('IP address of the authoritative DNS server for this domain' . '<br />' . + 'e.g.: 192.168.100.100' . '<br />' . + 'Or enter # for an exclusion to pass through this host/subdomain to standard nameservers instead of a previous override.' . '<br />' . + 'Or enter ! for lookups for this host/subdomain to NOT be forwarded anywhere.'); + +$section->addInput(new Form_IpAddress( + 'dnssrcip', + 'Source IP', + $pconfig['dnssrcip'] +))->setHelp('Source IP address for queries to the DNS server for the override domain. Leave blank unless your DNS server is accessed through a VPN tunnel.'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +if (isset($id) && $a_domainOverrides[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $pconfig['id'] + ))->setHelp('You may enter a description here for your reference (not parsed).'); +} + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_dnsmasq_edit.php b/src/usr/local/www/services_dnsmasq_edit.php new file mode 100644 index 0000000..f9513d9 --- /dev/null +++ b/src/usr/local/www/services_dnsmasq_edit.php @@ -0,0 +1,316 @@ +<?php +/* $Id$ */ +/* + services_dnsmasq_edit.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Bob Zoller <bob@kludgebox.com> and Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: dnsforwarder +*/ + +##|+PRIV +##|*IDENT=page-services-dnsforwarder-edithost +##|*NAME=Services: DNS Forwarder: Edit host page +##|*DESCR=Allow access to the 'Services: DNS Forwarder: Edit host' page. +##|*MATCH=services_dnsmasq_edit.php* +##|-PRIV + +function hostcmp($a, $b) { + return strcasecmp($a['host'], $b['host']); +} + +function hosts_sort() { + global $g, $config; + + if (!is_array($config['dnsmasq']['hosts'])) { + return; + } + + usort($config['dnsmasq']['hosts'], "hostcmp"); +} + +require("guiconfig.inc"); + +if (!is_array($config['dnsmasq']['hosts'])) + $config['dnsmasq']['hosts'] = array(); + +$a_hosts = &$config['dnsmasq']['hosts']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_hosts[$id]) { + $pconfig['host'] = $a_hosts[$id]['host']; + $pconfig['domain'] = $a_hosts[$id]['domain']; + $pconfig['ip'] = $a_hosts[$id]['ip']; + $pconfig['descr'] = $a_hosts[$id]['descr']; + $pconfig['aliases'] = $a_hosts[$id]['aliases']; +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "domain ip"); + $reqdfieldsn = array(gettext("Domain"), gettext("IP address")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ($_POST['host']) { + if (!is_hostname($_POST['host'])) { + $input_errors[] = gettext("The hostname can only contain the characters A-Z, 0-9 and '-'. It may not start or end with '-'."); + } else { + if (!is_unqualified_hostname($_POST['host'])) { + $input_errors[] = gettext("A valid hostname is specified, but the domain name part should be omitted"); + } + } + } + + if (($_POST['domain'] && !is_domain($_POST['domain']))) { + $input_errors[] = gettext("A valid domain must be specified."); + } + + if (($_POST['ip'] && !is_ipaddr($_POST['ip']))) { + $input_errors[] = gettext("A valid IP address must be specified."); + } + + /* collect aliases */ + $aliases = array(); + foreach ($_POST as $key => $value) { + $entry = ''; + if (!substr_compare('aliashost', $key, 0, 9)) { + $entry = substr($key, 9); + $field = 'host'; + } + elseif (!substr_compare('aliasdomain', $key, 0, 11)) { + $entry = substr($key, 11); + $field = 'domain'; + } + elseif (!substr_compare('aliasdescription', $key, 0, 16)) { + $entry = substr($key, 16); + $field = 'description'; + } + if (ctype_digit($entry)) { + $aliases[$entry][$field] = $value; + } + } + $pconfig['aliases']['item'] = $aliases; + + /* validate aliases */ + foreach ($aliases as $idx => $alias) { + $aliasreqdfields = array('aliasdomain' . $idx); + $aliasreqdfieldsn = array(gettext("Alias Domain")); + + do_input_validation($_POST, $aliasreqdfields, $aliasreqdfieldsn, $input_errors); + if ($alias['host']) { + if (!is_hostname($alias['host'])) { + $input_errors[] = gettext("Hostnames in an alias list can only contain the characters A-Z, 0-9 and '-'. They may not start or end with '-'."); + } else { + if (!is_unqualified_hostname($alias['host'])) { + $input_errors[] = gettext("A valid alias hostname is specified, but the domain name part should be omitted"); + } + } + } + + if (($alias['domain'] && !is_domain($alias['domain']))) { + $input_errors[] = gettext("A valid domain must be specified in alias list."); + } + } + + /* check for overlaps */ + foreach ($a_hosts as $hostent) { + if (isset($id) && ($a_hosts[$id]) && ($a_hosts[$id] === $hostent)) { + continue; + } + + if (($hostent['host'] == $_POST['host']) && + ($hostent['domain'] == $_POST['domain']) && + ((is_ipaddrv4($hostent['ip']) && is_ipaddrv4($_POST['ip'])) || + (is_ipaddrv6($hostent['ip']) && is_ipaddrv6($_POST['ip'])))) { + $input_errors[] = gettext("This host/domain already exists."); + break; + } + } + + if (!$input_errors) { + $hostent = array(); + $hostent['host'] = $_POST['host']; + $hostent['domain'] = $_POST['domain']; + $hostent['ip'] = $_POST['ip']; + $hostent['descr'] = $_POST['descr']; + $hostent['aliases']['item'] = $aliases; + + if (isset($id) && $a_hosts[$id]) { + $a_hosts[$id] = $hostent; + } else { + $a_hosts[] = $hostent; + } + hosts_sort(); + + mark_subsystem_dirty('hosts'); + + write_config(); + + header("Location: services_dnsmasq.php"); + exit; + } +} + +// Delete a row in the options table +if($_GET['act'] == "delopt") { + $idx = $_GET['id']; + + if($pconfig['aliases'] && is_array($pconfig['aliases']['item'][$idx])) { + unset($pconfig['aliases']['item'][$idx]); + } +} + +// Add an option row +if($_GET['act'] == "addopt") { + if(!is_array($pconfig['aliases']['item'])) + $pconfig['aliases']['item'] = array(); + + array_push($pconfig['aliases']['item'], array('host' => null, 'domain' => null, 'description' => null)); +} + +$pgtitle = array(gettext("Services"),gettext("DNS forwarder"),gettext("Edit host")); +$shortcut_section = "forwarder"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Domain override options'); + +$section->addInput(new Form_Input( + 'host', + 'Host', + 'text', + $pconfig['domain'] +))->setHelp('Name of the host, without the domain part' . '<br />' . + 'e.g.: "myhost"'); + +$section->addInput(new Form_Input( + 'domain', + 'Domain', + 'text', + $pconfig['domain'] +))->setHelp('Domain of the host' . '<br />' . + 'e.g.: "example.com"'); + +$section->addInput(new Form_IpAddress( + 'ip', + 'IP Address', + $pconfig['ip'] +))->setHelp('IP address of the host' . '<br />' . + 'e.g.: 192.168.100.100 or fd00:abcd::1'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +if (isset($id) && $a_hosts[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $pconfig['id'] + )); +} + +$form->add($section); + +$section = new Form_Section('Additional names for this host'); + +if( $pconfig['aliases']['item']) { + $counter = 0; + $last = count($pconfig['aliases']['item']) - 1; + + foreach($pconfig['aliases']['item'] as $item) { + $group = new Form_Group(null); + + $group->add(new Form_Input( + 'aliashost' . $counter, + null, + 'text', + $item['host'] + ))->setHelp($counter == $last ? 'Host name':null); + + $group->add(new Form_Input( + 'aliasdomain' . $counter, + null, + 'text', + $item['domain'] + ))->setHelp($counter == $last ? 'Value':null); + + $group->add(new Form_Input( + 'aliasdescription' . $counter, + null, + 'text', + $item['description'] + ))->setHelp($counter == $last ? 'Description':null); + + $btn = new Form_Button( + 'btn' . $counter, + 'Delete', + 'services_dnsmasq_edit.php?act=delopt' . '&id=' . $counter + ); + + $btn->removeClass('btn-primary')->addClass('btn-danger btn-sm'); + $group->add($btn); + $section->add($group); + $counter++; + } +} + +$btnaddopt = new Form_Button( + 'btnaddopt', + 'Add Option', + 'services_dnsmasq_edit.php?act=addopt' +); + +$btnaddopt->removeClass('btn-primary')->addClass('btn-success btn-sm'); + +$section->addInput($btnaddopt); + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_dyndns.php b/src/usr/local/www/services_dyndns.php new file mode 100644 index 0000000..c605241 --- /dev/null +++ b/src/usr/local/www/services_dyndns.php @@ -0,0 +1,193 @@ +<?php +/* $Id$ */ +/* + services_dyndns.php + + Copyright (C) 2008 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/bin/host + pfSense_MODULE: dyndns +*/ + +##|+PRIV +##|*IDENT=page-services-dynamicdnsclients +##|*NAME=Services: Dynamic DNS clients page +##|*DESCR=Allow access to the 'Services: Dynamic DNS clients' page. +##|*MATCH=services_dyndns.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['dyndnses']['dyndns'])) { + $config['dyndnses']['dyndns'] = array(); +} + +$a_dyndns = &$config['dyndnses']['dyndns']; + +if ($_GET['act'] == "del") { + $conf = $a_dyndns[$_GET['id']]; + @unlink("{$g['conf_path']}/dyndns_{$conf['interface']}{$conf['type']}" . escapeshellarg($conf['host']) . "{$conf['id']}.cache"); + unset($a_dyndns[$_GET['id']]); + + write_config(); + services_dyndns_configure(); + + header("Location: services_dyndns.php"); + exit; +} + +$pgtitle = array(gettext("Services"), gettext("Dynamic DNS clients")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +$tab_array = array(); +$tab_array[] = array(gettext("DynDns"), true, "services_dyndns.php"); +$tab_array[] = array(gettext("RFC 2136"), false, "services_rfc2136.php"); +display_top_tabs($tab_array); +?> +<form action="services_dyndns.php" method="post" name="iform" id="iform"> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Interface")?></th> + <th><?=gettext("Service")?></th> + <th><?=gettext("Hostname")?></th> + <th><?=gettext("Cached IP")?></th> + <th><?=gettext("Description")?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php +$i = 0; +foreach ($a_dyndns as $dyndns): +?> + <tr<?=!isset($dyndns['enable'])?' class="disabled""':''?>> + <td> +<?php + $iflist = get_configured_interface_with_descr(); + foreach ($iflist as $if => $ifdesc) { + if ($dyndns['interface'] == $if) { + print($ifdesc); + + break; + } + } + + $groupslist = return_gateway_groups_array(); + foreach ($groupslist as $if => $group) { + if ($dyndns['interface'] == $if) { + print($if); + break; + } + } +?> + </td> + <td> +<?php + $types = explode(",", DYNDNS_PROVIDER_DESCRIPTIONS); + $vals = explode(" ", DYNDNS_PROVIDER_VALUES); + + for ($j = 0; $j < count($vals); $j++) { + if ($vals[$j] == $dyndns['type']) { + print(htmlspecialchars($types[$j])); + + break; + } + } +?> + </td> + <td> +<?php + print(htmlspecialchars($dyndns['host'])); +?> + </td> + <td> +<?php + $filename = "{$g['conf_path']}/dyndns_{$dyndns['interface']}{$dyndns['type']}" . escapeshellarg($dyndns['host']) . "{$dyndns['id']}.cache"; + $filename_v6 = "{$g['conf_path']}/dyndns_{$dyndns['interface']}{$dyndns['type']}" . escapeshellarg($dyndns['host']) . "{$dyndns['id']}_v6.cache"; + if (file_exists($filename)) { + $ipaddr = dyndnsCheckIP($dyndns['interface']); + $cached_ip_s = explode(":", file_get_contents($filename)); + $cached_ip = $cached_ip_s[0]; + + if ($ipaddr != $cached_ip) + print('<font color="red">'); + else + print('<font color="green">'); + + print(htmlspecialchars($cached_ip)); + print('</font>'); + } else if (file_exists($filename_v6)) { + $ipv6addr = get_interface_ipv6($dyndns['interface']); + $cached_ipv6_s = explode("|", file_get_contents($filename_v6)); + $cached_ipv6 = $cached_ipv6_s[0]; + + if ($ipv6addr != $cached_ipv6) + print('<font color="red">'); + else + print('<font color="green">'); + + print(htmlspecialchars($cached_ipv6)); + print('</font>'); + } else { + print('N/A'); + } +?> + </td> + <td> +<?php + print(htmlspecialchars($dyndns['descr'])); +?> + </td> + <td> + <a href="services_dyndns_edit.php?id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext('Edit')?></a> + <a href="services_dyndns.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger"><?=gettext("Delete")?></a> + </td> + </tr> +<?php + $i++; + endforeach; +?> + </tbody> + </table> + </div> +</form> + +<nav class="action-buttons"> + <a href="services_dyndns_edit.php" class="btn btn-sm btn-success"><?=gettext('Add')?></a> +</nav> + +<?php + +print_info_box(gettext("IP addresses appearing in green are up to date with Dynamic DNS provider. " . + "You can force an update for an IP address on the edit page for that service.")); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_dyndns_edit.php b/src/usr/local/www/services_dyndns_edit.php new file mode 100644 index 0000000..750cd9b --- /dev/null +++ b/src/usr/local/www/services_dyndns_edit.php @@ -0,0 +1,470 @@ +<?php +/* $Id$ */ +/* + services_dyndns_edit.php + + Copyright (C) 2008 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /bin/rm + pfSense_MODULE: dyndns +*/ + +##|+PRIV +##|*IDENT=page-services-dynamicdnsclient +##|*NAME=Services: Dynamic DNS client page +##|*DESCR=Allow access to the 'Services: Dynamic DNS client' page. +##|*MATCH=services_dyndns_edit.php* +##|-PRIV + +/* returns true if $uname is a valid DynDNS username */ +function is_dyndns_username($uname) { + if (!is_string($uname)) { + return false; + } + + if (preg_match("/[^a-z0-9\-\+.@_:]/i", $uname)) { + return false; + } else { + return true; + } +} + +require("guiconfig.inc"); + +if (!is_array($config['dyndnses']['dyndns'])) { + $config['dyndnses']['dyndns'] = array(); +} + +$a_dyndns = &$config['dyndnses']['dyndns']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && isset($a_dyndns[$id])) { + $pconfig['username'] = $a_dyndns[$id]['username']; + $pconfig['password'] = $a_dyndns[$id]['password']; + $pconfig['host'] = $a_dyndns[$id]['host']; + $pconfig['mx'] = $a_dyndns[$id]['mx']; + $pconfig['type'] = $a_dyndns[$id]['type']; + $pconfig['enable'] = !isset($a_dyndns[$id]['enable']); + $pconfig['interface'] = $a_dyndns[$id]['interface']; + $pconfig['wildcard'] = isset($a_dyndns[$id]['wildcard']); + $pconfig['verboselog'] = isset($a_dyndns[$id]['verboselog']); + $pconfig['curl_ipresolve_v4'] = isset($a_dyndns[$id]['curl_ipresolve_v4']); + $pconfig['curl_ssl_verifypeer'] = isset($a_dyndns[$id]['curl_ssl_verifypeer']); + $pconfig['zoneid'] = $a_dyndns[$id]['zoneid']; + $pconfig['ttl'] = $a_dyndns[$id]['ttl']; + $pconfig['updateurl'] = $a_dyndns[$id]['updateurl']; + $pconfig['resultmatch'] = $a_dyndns[$id]['resultmatch']; + $pconfig['requestif'] = $a_dyndns[$id]['requestif']; + $pconfig['descr'] = $a_dyndns[$id]['descr']; +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + if (($pconfig['type'] == "freedns" || $pconfig['type'] == "namecheap") && $_POST['username'] == "") { + $_POST['username'] = "none"; + } + + /* input validation */ + $reqdfields = array(); + $reqdfieldsn = array(); + $reqdfields = array("type"); + $reqdfieldsn = array(gettext("Service type")); + + if ($pconfig['type'] != "custom" && $pconfig['type'] != "custom-v6") { + $reqdfields[] = "host"; + $reqdfieldsn[] = gettext("Hostname"); + $reqdfields[] = "passwordfld"; + $reqdfieldsn[] = gettext("Password"); + $reqdfields[] = "username"; + $reqdfieldsn[] = gettext("Username"); + } else { + $reqdfields[] = "updateurl"; + $reqdfieldsn[] = gettext("Update URL"); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (isset($_POST['host']) && in_array("host", $reqdfields)) { + /* Namecheap can have a @. in hostname */ + if ($pconfig['type'] == "namecheap" && substr($_POST['host'], 0, 2) == '@.') { + $host_to_check = substr($_POST['host'], 2); + } else { + $host_to_check = $_POST['host']; + } + + if ($pconfig['type'] != "custom" && $pconfig['type'] != "custom-v6") { + if (!is_domain($host_to_check)) { + $input_errors[] = gettext("The hostname contains invalid characters."); + } + } + + unset($host_to_check); + } + if (($_POST['mx'] && !is_domain($_POST['mx']))) { + $input_errors[] = gettext("The MX contains invalid characters."); + } + if ((in_array("username", $reqdfields) && $_POST['username'] && !is_dyndns_username($_POST['username'])) || ((in_array("username", $reqdfields)) && ($_POST['username'] == ""))) { + $input_errors[] = gettext("The username contains invalid characters."); + } + + if (!$input_errors) { + $dyndns = array(); + $dyndns['type'] = $_POST['type']; + $dyndns['username'] = $_POST['username']; + $dyndns['password'] = $_POST['passwordfld']; + $dyndns['host'] = $_POST['host']; + $dyndns['mx'] = $_POST['mx']; + $dyndns['wildcard'] = $_POST['wildcard'] ? true : false; + $dyndns['verboselog'] = $_POST['verboselog'] ? true : false; + $dyndns['curl_ipresolve_v4'] = $_POST['curl_ipresolve_v4'] ? true : false; + $dyndns['curl_ssl_verifypeer'] = $_POST['curl_ssl_verifypeer'] ? true : false; + /* In this place enable means disabled */ + if ($_POST['enable']) { + unset($dyndns['enable']); + } else { + $dyndns['enable'] = true; + } + $dyndns['interface'] = $_POST['interface']; + $dyndns['zoneid'] = $_POST['zoneid']; + $dyndns['ttl'] = $_POST['ttl']; + $dyndns['updateurl'] = $_POST['updateurl']; + // Trim hard-to-type but sometimes returned characters + $dyndns['resultmatch'] = trim($_POST['resultmatch'], "\t\n\r"); + ($dyndns['type'] == "custom" || $dyndns['type'] == "custom-v6") ? $dyndns['requestif'] = $_POST['requestif'] : $dyndns['requestif'] = $_POST['interface']; + $dyndns['descr'] = $_POST['descr']; + $dyndns['force'] = isset($_POST['force']); + + if ($dyndns['username'] == "none") { + $dyndns['username'] = ""; + } + + if (isset($id) && $a_dyndns[$id]) { + $a_dyndns[$id] = $dyndns; + } else { + $a_dyndns[] = $dyndns; + $id = count($a_dyndns) - 1; + } + + $dyndns['id'] = $id; + //Probably overkill, but its better to be safe + for ($i = 0; $i < count($a_dyndns); $i++) { + $a_dyndns[$i]['id'] = $i; + } + + write_config(); + + services_dyndns_configure_client($dyndns); + + header("Location: services_dyndns.php"); + exit; + } +} + +function build_type_list() { + $types = explode(",", DYNDNS_PROVIDER_DESCRIPTIONS); + $vals = explode(" ", DYNDNS_PROVIDER_VALUES); + $typelist = array(); + + for ($j = 0; $j < count($vals); $j++) + $typelist[$vals[$j]] = htmlspecialchars($types[$j]); + + return($typelist); +} + +function build_if_list() { + $list = array(); + + $iflist = get_configured_interface_with_descr(); + + foreach ($iflist as $if => $ifdesc) + $list[$if] = $ifdesc; + + unset($iflist); + + $grouplist = return_gateway_groups_array(); + + foreach ($grouplist as $name => $group) + $list[$name] = 'GW Group ' . $name; + + unset($grouplist); + + return($list); +} + +$pgtitle = array(gettext("Services"),gettext("Dynamic DNS client")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +require('classes/Form.class.php'); + +$form = new Form; + +$section = new Form_Section('Dynamic DNS Client'); + +// Confusingly the 'enable' checkbox is labelled 'Disable', but thats the way it works! +// No action (hide or disable) is taken on selecting this. +$section->addInput(new Form_Checkbox( + 'enable', + 'Disable', + 'Disable this client', + $pconfig['enable'] +)); + +$section->addInput(new Form_Select( + 'type', + 'Service Type', + $pconfig['type'], + build_type_list() +)); + +$interfacelist = build_if_list(); + +$section->addInput(new Form_Select( + 'interface', + 'Interface to monitor', + $pconfig['interface'], + $interfacelist +)); + +$section->addInput(new Form_Select( + 'requestif', + 'Interface to send update from', + $pconfig['request'], + $interfacelist +))->setHelp('This is almost always the same as the Interface to Monitor. '); + +$section->addInput(new Form_Input( + 'host', + 'Hostname', + 'text', + $pconfig['host'] +))->setHelp('Enter the complete host/domain name. example: myhost.dyndns.org'. + 'he.net tunnelbroker: Enter your tunnel ID'. + 'GleSYS: Enter your record ID'. + 'DNSimple: Enter only the domain name.'); + +$section->addInput(new Form_Input( + 'mx', + 'MX', + 'text', + $pconfig['mx'] +))->setHelp('Note: With DynDNS service you can only use a hostname, not an IP address.'. + 'Set this option only if you need a special MX record. Not all services support this.'); + +$section->addInput(new Form_Checkbox( + 'wildcard', + 'Wildcards', + 'Enable Wildcard', + $pconfig['wildcard'] +)); + +$section->addInput(new Form_Checkbox( + 'verboselog', + 'Verbose logging', + 'Enable verbose logging', + $pconfig['verboselog'] +)); + +$section->addInput(new Form_Checkbox( + 'curl_ipresolve_v4', + 'CURL options', + 'Force IPv4 resolving', + $pconfig['curl_ipresolve_v4'] +)); + +$section->addInput(new Form_Checkbox( + 'curl_ssl_verifypeer', + null, + 'Verify SSL peer', + $pconfig['curl_ssl_verifypeer'] +)); + +$section->addInput(new Form_Input( + 'username', + 'Username', + 'text', + $pconfig['username'] +))->setHelp('Username is required for all types except Namecheap, FreeDNS and Custom Entries.' . '<br />' . + 'Route 53: Enter your Access Key ID.' . '<br />' . + 'GleSYS: Enter your API user.' . '<br />' . + 'For Custom Entries, Username and Password represent HTTP Authentication username and passwords.'); + +$section->addInput(new Form_Input( + 'passwordfld', + 'Password', + 'password', + $pconfig['passwordfld'] +))->setHelp('FreeDNS (freedns.afraid.org): Enter your \"Authentication Token\" provided by FreeDNS.' . '<br />' . + 'Route 53: Enter your Secret Access Key.' . '<br />' . + 'GleSYS: Enter your API key.' . '<br />' . + 'DNSimple: Enter your API token.'); + +$section->addInput(new Form_Input( + 'zoneid', + 'Zone ID', + 'text', + $pconfig['zoneid'] +))->setHelp('Enter Zone ID that you received when you created your domain in Route 53.' . '<br />' . + 'DNSimple: Enter the Record ID of record to update.'); + +$section->addInput(new Form_Input( + 'updateurl', + 'Update URL', + 'text', + $pconfig['updateurl'] +))->setHelp('This is the only field required by for Custom Dynamic DNS, and is only used by Custom Entries.'); + +$section->addInput(new Form_Textarea( + 'resultmatch', + 'Result Match', + $pconfig['resultmatch'] +))->sethelp('This field should be identical to what your DDNS Provider will return if the update succeeds, leave it blank to disable checking of returned results.' . '<br />' . + 'If you need the new IP to be included in the request, put %IP% in its place.' . '<br />' . + 'If you need to include multiple possible values, separate them with a |. If your provider includes a |, escape it with \\|)' . '<br />' . + 'Tabs (\\t), newlines (\\n) and carriage returns (\\r) at the beginning or end of the returned results are removed before comparison.'); + +$section->addInput(new Form_Input( + 'ttl', + 'TTL', + 'text', + $pconfig['ttl'] +))->setHelp('Choose TTL for your dns record.'); + +$section->addInput(new Form_Input( + 'description', + 'Description', + 'text', + $pconfig['description'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +if (isset($id) && $a_dyndns[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); + +print($form); + +// Certain input elements are hidden/shown based on the service type in the following script +?> + +<script> +//<![CDATA[ +events.push(function(){ + var visible = false; + + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified checkbox lives so that the checkbox, its label and help text are hidden + function hideCheckbox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + function setVisible(service) { + switch(service) { + case "custom" : + case "custom-v6" : + hideInput('resultmatch', false); + hideInput('updateurl', false); + hideInput('requestif', false); + hideCheckbox('curl_ipresolve_v4', false); + hideCheckbox('curl_ssl_verifypeer', false); + hideInput('host', true); + hideInput('mx', true); + hideCheckbox('wildcard', true); + hideInput('zoneid', true); + hideInput('ttl', true); + break; + + case "dnsimple": + case "route53": + hideInput('resultmatch', true); + hideInput('updateurl', true); + hideInput('requestif', true); + hideCheckbox('curl_ipresolve_v4', true); + hideCheckbox('curl_ssl_verifypeer', false); + hideInput('host', false); + hideInput('mx', false); + hideCheckbox('wildcard', false); + hideInput('zoneid', false); + hideInput('ttl', false); + break; + + default: + hideInput('resultmatch', true); + hideInput('updateurl', true); + hideInput('requestif', true); + hideCheckbox('curl_ipresolve_v4', true); + hideCheckbox('curl_ssl_verifypeer', true); + hideInput('host', false); + hideInput('mx', false); + hideCheckbox('wildcard', false); + hideInput('zoneid', true); + hideInput('ttl', true); + } + } + + // When the 'Service type" selector is changed, we show/hide certain elements + $('#type').on('change', function() { + setVisible( this.value ); + }); + + // On initial page load + setVisible($('#type').val()); + +}); +//]]> +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_igmpproxy.php b/src/usr/local/www/services_igmpproxy.php new file mode 100644 index 0000000..2953337 --- /dev/null +++ b/src/usr/local/www/services_igmpproxy.php @@ -0,0 +1,156 @@ +<?php +/* $Id$ */ +/* + services_igmpproxy.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2009 Ermal Luçi + Copyright (C) 2004 Scott Ullrich + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: igmpproxy +*/ + +##|+PRIV +##|*IDENT=page-services-igmpproxy +##|*NAME=Services: Igmpproxy page +##|*DESCR=Allow access to the 'Services: Igmpproxy' page. +##|*MATCH=services_igmpproxy.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['igmpproxy']['igmpentry'])) { + $config['igmpproxy']['igmpentry'] = array(); +} + +//igmpproxy_sort(); +$a_igmpproxy = &$config['igmpproxy']['igmpentry']; + +if ($_POST) { + $pconfig = $_POST; + + $retval = 0; + /* reload all components that use igmpproxy */ + $retval = services_igmpproxy_configure(); + + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message($retval); + } else { + $savemsg = $retval; + } + + clear_subsystem_dirty('igmpproxy'); +} + +if ($_GET['act'] == "del") { + if ($a_igmpproxy[$_GET['id']]) { + unset($a_igmpproxy[$_GET['id']]); + write_config(); + mark_subsystem_dirty('igmpproxy'); + header("Location: services_igmpproxy.php"); + exit; + } +} + +$pgtitle = array(gettext("Services"), gettext("IGMP Proxy")); +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('igmpproxy')) + print_info_box_np(gettext('The IGMP entry list has been changed.' . '<br />' . 'You must apply the changes in order for them to take effect.')); +?> + +<form action="services_igmpproxy.php" method="post"> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Name")?></th> + <th><?=gettext("Type")?></th> + <th><?=gettext("Values")?></th> + <th><?=gettext("Description")?></th> + <th</th> + </tr> + </thead> + <tbody> +<?php +$i = 0; +foreach ($a_igmpproxy as $igmpentry): +?> + <tr> + <td> + <?=htmlspecialchars(convert_friendly_interface_to_friendly_descr($igmpentry['ifname']))?> + </td> + <td> + <?=htmlspecialchars($igmpentry['type'])?> + </td> + <td> +<?php + $addresses = implode(", ", array_slice(explode(" ", $igmpentry['address']), 0, 10)); + print($addresses); + + if(count($addresses) < 10) { + print(' '); + } else { + print('...'); + } +?> + </td> + <td> + <?=htmlspecialchars($igmpentry['descr'])?> + </td> + <td> + <a href="services_igmpproxy_edit.php?id=<?=$i?>" class="btn btn-info btn-xs"><?=gettext('Edit')?></a> + <a href="services_igmpproxy.php?act=del&id=<?=$i?>" class="btn btn-danger btn-xs"><?=gettext('Delete')?></a> + </td> + </tr> +<?php + $i++; +endforeach; +?> + </tbody> + </table> + </div> + + <nav class="action-buttons"> + <input id="submit" name="submit" type="submit" class="btn btn-primary" value="<?=gettext("Save")?>" /> + <a href="services_igmpproxy_edit.php" class="btn btn-success"><?=gettext('Add')?></a> + </nav> + +</form> + +<?php + +print_info_box(gettext('Please add the interface for upstream, the allowed subnets, and the downstream interfaces you would like the proxy to allow. ' . + 'Only one "upstream" interface can be configured.')); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_igmpproxy_edit.php b/src/usr/local/www/services_igmpproxy_edit.php new file mode 100644 index 0000000..cbe8307 --- /dev/null +++ b/src/usr/local/www/services_igmpproxy_edit.php @@ -0,0 +1,291 @@ +<?php +/* $Id$ */ +/* + services_igmpproxy_edit.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2009 Ermal Luçi + Copyright (C) 2004 Scott Ullrich + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: igmpproxy +*/ + +##|+PRIV +##|*IDENT=page-services-igmpproxy-edit +##|*NAME=Firewall: Igmpproxy: Edit page +##|*DESCR=Allow access to the 'Services: Igmpproxy: Edit' page. +##|*MATCH=services_igmpproxy_edit.php* +##|-PRIV + +$pgtitle = array(gettext("Firewall"), gettext("IGMP Proxy"), gettext("Edit")); + +require("guiconfig.inc"); + +if (!is_array($config['igmpproxy']['igmpentry'])) { + $config['igmpproxy']['igmpentry'] = array(); +} + +//igmpproxy_sort(); +$a_igmpproxy = &$config['igmpproxy']['igmpentry']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_igmpproxy[$id]) { + $pconfig['ifname'] = $a_igmpproxy[$id]['ifname']; + $pconfig['threshold'] = $a_igmpproxy[$id]['threshold']; + $pconfig['type'] = $a_igmpproxy[$id]['type']; + $pconfig['address'] = $a_igmpproxy[$id]['address']; + $pconfig['descr'] = html_entity_decode($a_igmpproxy[$id]['descr']); +} + +// Add a row to the network table +if($_GET['act'] && $_GET['act'] == 'addrow') + $pconfig['address'] .= '/32'; + +// Remove a row from the network table +if($_GET['act'] && $_GET['act'] == 'delrow') { + $row = $_GET['row']; + + $addresses = explode(" ", $pconfig['address']); + + $pconfig['address'] = ""; + + $idx = 0; + foreach($addresses as $address) { + if($idx != $row) + $pconfig['address'] .= ($idx > 0 ? ' ':null) . $address; + + $idx++; + } +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + if ($_POST['type'] == "upstream") { + foreach ($a_igmpproxy as $pid => $proxyentry) { + if (isset($id) && $id == $pid) { + continue; + } + if ($proxyentry['type'] == "upstream" && $proxyentry['ifname'] != $_POST['interface']) { + $input_errors[] = gettext("Only one 'upstream' interface can be configured."); + } + } + } + + $igmpentry = array(); + $igmpentry['ifname'] = $_POST['ifname']; + $igmpentry['threshold'] = $_POST['threshold']; + $igmpentry['type'] = $_POST['type']; + $address = ""; + $isfirst = 0; + /* item is a normal igmpentry type */ + for ($x = 0; $x < 4999; $x++) { + if ($_POST["address{$x}"] <> "") { + if ($isfirst > 0) { + $address .= " "; + } + $address .= $_POST["address{$x}"]; + $address .= "/" . $_POST["address_subnet{$x}"]; + $isfirst++; + } + } + + if (!$input_errors) { + $igmpentry['address'] = $address; + $igmpentry['descr'] = $_POST['descr']; + + if (isset($id) && $a_igmpproxy[$id]) { + $a_igmpproxy[$id] = $igmpentry; + } else { + $a_igmpproxy[] = $igmpentry; + } + + write_config(); + + mark_subsystem_dirty('igmpproxy'); + header("Location: services_igmpproxy.php"); + exit; + } else { + //we received input errors, copy data to prevent retype + $pconfig['descr'] = $_POST['descr']; + $pconfig['address'] = $address; + $pconfig['type'] = $_POST['type']; + } +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +// These two inputs appear inthe original file. Don't know what they are for +// but they are here just in case. + +$h1 = new Form_Input( + 'address_type', + null, + 'textbox', + 'hidden' + ); + +$h2 = new Form_Input( + 'address_subnet_type', + null, + 'select', + 'hidden' + ); + +$form = new Form; + +$section = new Form_Section('IGMP Proxy Edit'); + +$optionlist = array(); +$iflist = get_configured_interface_with_descr(); + +foreach ($iflist as $ifnam => $ifdescr) + $optionlist[$ifnam] = $ifdescr; + +$section->addInput(new Form_Select( + 'ifname', + 'Interface', + $pconfig['ifname'], + $optionlist +)); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$section->addInput(new Form_Select( + 'type', + 'Type', + $pconfig['type'], + ['upstream' => gettext('Upstream Interface'), 'downstream' => gettext('Downstream Interface')] +))->setHelp('The upstream network interface is the outgoing interface which is responsible for communicating to available multicast data sources .' . + 'There can only be one upstream interface.' . '<br />' . + 'Downstream network interfaces are the distribution interfaces to the destination networks, where multicast clients can join groups and '. + 'receive multicast data. One or more downstream interfaces must be configured.'); + +$section->addInput(new Form_Input( + 'threshold', + 'Threshold', + 'text', + $pconfig['threshold'] +))->setHelp('Defines the TTL threshold for the network interface. Packets with a lower TTL than the threshold value will be ignored. ' . + 'This setting is optional, and by default the threshold is 1.'); + +if (isset($id) && $a_igmpproxy[$id]){ + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$counter = 0; +$address = $pconfig['address']; + +if ($address != "") { + $item = explode(" ", $address); + $rows = count($item) -1; + foreach($item as $ww) { + $address = $item[$counter]; + $address_subnet = ""; + $item2 = explode("/", $address); + foreach($item2 as $current) { + if($item2[1] != "") { + $address = $item2[0]; + $address_subnet = $item2[1]; + } + } + $item4 = $item3[$counter]; + $tracker = $counter; + + $group = new Form_group($tracker == 0? 'Network':null); + + $group->add(new Form_Input( + 'address' . $tracker, + null, + 'text', + $address, + ['placeholder' => 'Address'] + ))->sethelp($tracker == $rows ? 'Network':null); + + $group->add(new Form_Select( + 'ifname', + 'Interface', + $address_subnet, + array_combine(range(32, 1, -1), range(32, 1, -1)) + ))->sethelp($tracker == $rows ? 'CIDR':null);; + + $btndel = new Form_Button ( + 'removerow', + 'Remove', + 'services_igmpproxy_edit.php?act=delrow&row=' . $tracker + ); + + $btndel->removeClass('btn-primary')->addClass('btn-danger btn-sm'); + $group->add($btndel); + + $counter++; + $section->add($group); + } // end foreach +} // end if + +$btnadd = new Form_Button ( + 'addrow', + 'Add Network', + 'services_igmpproxy_edit.php?act=addrow' + ); + +$btnadd->removeClass('btn-primary')->addClass('btn-success btn-sm'); + +$section->addInput(new Form_StaticText( + null, + $btnadd . ' (Save after each Add or Delete)' +)); + +$form->add($section); + +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_ntpd.php b/src/usr/local/www/services_ntpd.php new file mode 100644 index 0000000..412678b --- /dev/null +++ b/src/usr/local/www/services_ntpd.php @@ -0,0 +1,553 @@ +<?php +/* + services_ntpd.php + + Copyright (C) 2013 Dagorlad + Copyright (C) 2012 Jim Pingle + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: ntpd +*/ + +##|+PRIV +##|*IDENT=page-services-ntpd +##|*NAME=Services: NTP +##|*DESCR=Allow access to the 'Services: NTP' page. +##|*MATCH=services_ntpd.php* +##|-PRIV + +define(NUMTIMESERVERS, 10); // The maximum number of configurable time servers +require("guiconfig.inc"); +require_once('rrd.inc'); +require_once("shaper.inc"); + +if (!is_array($config['ntpd'])) { + $config['ntpd'] = array(); +} + +if (empty($config['ntpd']['interface'])) { + if (is_array($config['installedpackages']['openntpd']) && is_array($config['installedpackages']['openntpd']['config']) && + is_array($config['installedpackages']['openntpd']['config'][0]) && !empty($config['installedpackages']['openntpd']['config'][0]['interface'])) { + $pconfig['interface'] = explode(",", $config['installedpackages']['openntpd']['config'][0]['interface']); + unset($config['installedpackages']['openntpd']); + write_config("Upgraded settings from openttpd"); + } else { + $pconfig['interface'] = array(); + } +} else { + $pconfig['interface'] = explode(",", $config['ntpd']['interface']); +} + +if($_GET['addrow']) + $maxrows = $_GET['addrow'] + 1; +else + $maxrows = 3; + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + if (!$input_errors) { + if (is_array($_POST['interface'])) { + $config['ntpd']['interface'] = implode(",", $_POST['interface']); + } elseif (isset($config['ntpd']['interface'])) { + unset($config['ntpd']['interface']); + } + + if (!empty($_POST['gpsport']) && file_exists('/dev/'.$_POST['gpsport'])) { + $config['ntpd']['gpsport'] = $_POST['gpsport']; + } elseif (isset($config['ntpd']['gpsport'])) { + unset($config['ntpd']['gpsport']); + } + + unset($config['ntpd']['prefer']); + unset($config['ntpd']['noselect']); + $timeservers = ''; + + for ($i = 0; $i < 10; $i++) { + $tserver = trim($_POST["server{$i}"]); + if (!empty($tserver)) { + $timeservers .= "{$tserver} "; + if (!empty($_POST["servprefer{$i}"])) { + $config['ntpd']['prefer'] .= "{$tserver} "; + } + if (!empty($_POST["servselect{$i}"])) { + $config['ntpd']['noselect'] .= "{$tserver} "; + } + } + } + if (trim($timeservers) == "") { + $timeservers = "pool.ntp.org"; + } + $config['system']['timeservers'] = trim($timeservers); + + if (!empty($_POST['ntporphan']) && ($_POST['ntporphan'] < 17) && ($_POST['ntporphan'] != '12')) { + $config['ntpd']['orphan'] = $_POST['ntporphan']; + } elseif (isset($config['ntpd']['orphan'])) { + unset($config['ntpd']['orphan']); + } + + if (!empty($_POST['logpeer'])) { + $config['ntpd']['logpeer'] = $_POST['logpeer']; + } elseif (isset($config['ntpd']['logpeer'])) { + unset($config['ntpd']['logpeer']); + } + + if (!empty($_POST['logsys'])) { + $config['ntpd']['logsys'] = $_POST['logsys']; + } elseif (isset($config['ntpd']['logsys'])) { + unset($config['ntpd']['logsys']); + } + + if (!empty($_POST['clockstats'])) { + $config['ntpd']['clockstats'] = $_POST['clockstats']; + } elseif (isset($config['ntpd']['clockstats'])) { + unset($config['ntpd']['clockstats']); + } + + if (!empty($_POST['loopstats'])) { + $config['ntpd']['loopstats'] = $_POST['loopstats']; + } elseif (isset($config['ntpd']['loopstats'])) { + unset($config['ntpd']['loopstats']); + } + + if (!empty($_POST['peerstats'])) { + $config['ntpd']['peerstats'] = $_POST['peerstats']; + } elseif (isset($config['ntpd']['peerstats'])) { + unset($config['ntpd']['peerstats']); + } + + if (empty($_POST['kod'])) { + $config['ntpd']['kod'] = 'on'; + } elseif (isset($config['ntpd']['kod'])) { + unset($config['ntpd']['kod']); + } + + if (empty($_POST['nomodify'])) { + $config['ntpd']['nomodify'] = 'on'; + } elseif (isset($config['ntpd']['nomodify'])) { + unset($config['ntpd']['nomodify']); + } + + if (!empty($_POST['noquery'])) { + $config['ntpd']['noquery'] = $_POST['noquery']; + } elseif (isset($config['ntpd']['noquery'])) { + unset($config['ntpd']['noquery']); + } + + if (!empty($_POST['noserve'])) { + $config['ntpd']['noserve'] = $_POST['noserve']; + } elseif (isset($config['ntpd']['noserve'])) { + unset($config['ntpd']['noserve']); + } + + if (empty($_POST['nopeer'])) { + $config['ntpd']['nopeer'] = 'on'; + } elseif (isset($config['ntpd']['nopeer'])) { + unset($config['ntpd']['nopeer']); + } + + if (empty($_POST['notrap'])) { + $config['ntpd']['notrap'] = 'on'; + } elseif (isset($config['ntpd']['notrap'])) { + unset($config['ntpd']['notrap']); + } + + if ((empty($_POST['statsgraph'])) == (isset($config['ntpd']['statsgraph']))) { + $enable_rrd_graphing = true; + } + if (!empty($_POST['statsgraph'])) { + $config['ntpd']['statsgraph'] = $_POST['statsgraph']; + } elseif (isset($config['ntpd']['statsgraph'])) { + unset($config['ntpd']['statsgraph']); + } + if (isset($enable_rrd_graphing)) { + enable_rrd_graphing(); + } + + if (!empty($_POST['leaptxt'])) { + $config['ntpd']['leapsec'] = base64_encode($_POST['leaptxt']); + } elseif (isset($config['ntpd']['leapsec'])) { + unset($config['ntpd']['leapsec']); + } + + if (is_uploaded_file($_FILES['leapfile']['tmp_name'])) { + $config['ntpd']['leapsec'] = base64_encode(file_get_contents($_FILES['leapfile']['tmp_name'])); + } + + write_config("Updated NTP Server Settings"); + + $retval = 0; + $retval = system_ntp_configure(); + $savemsg = get_std_save_message($retval); + } +} + +function build_interface_list() { + global $pconfig; + + $iflist = array('options' => array(), 'selected' => array()); + + $interfaces = get_configured_interface_with_descr(); + $carplist = get_configured_carp_interface_list(); + + foreach ($carplist as $cif => $carpip) + $interfaces[$cif] = $carpip . " (" . get_vip_descr($carpip) .")"; + + $aliaslist = get_configured_ip_aliases_list(); + + foreach ($aliaslist as $aliasip => $aliasif) + $interfaces[$aliasip] = $aliasip." (".get_vip_descr($aliasip).")"; + + $size = (count($interfaces) < 10) ? count($interfaces) : 10; + + foreach ($interfaces as $iface => $ifacename) { + if (!is_ipaddr(get_interface_ip($iface)) && !is_ipaddr($iface)) + continue; + + $iflist['options']['$iface'] = $ifacename; + + if (in_array($iface, $pconfig['interface'])) + array_push($iflist['slected'], $iface); + + } + + return($iflist); +} + +$closehead = false; +$pconfig = &$config['ntpd']; +if (empty($pconfig['interface'])) { + $pconfig['interface'] = array(); +} else { + $pconfig['interface'] = explode(",", $pconfig['interface']); +} +$pgtitle = array(gettext("Services"), gettext("NTP")); +$shortcut_section = "ntp"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg, 'success'); + +$tab_array = array(); +$tab_array[] = array(gettext("NTP"), true, "services_ntpd.php"); +$tab_array[] = array(gettext("Serial GPS"), false, "services_ntpd_gps.php"); +$tab_array[] = array(gettext("PPS"), false, "services_ntpd_pps.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form; + +$section = new Form_Section('NTP server configuration'); + +$iflist = build_interface_list(); + +$section->addInput(new Form_Select( + 'interface', + 'Interface', + $iflist['selected'], + $iflist['options'], + true +))->setHelp('Interfaces without an IP address will not be shown.' . '<br />' . + 'Selecting no interfaces will listen on all interfaces with a wildcard.' . '<br />' . + 'Selecting all interfaces will explicitly listen on only the interfaces/IPs specified.'); + +// NUMTIMESERVERS time servers are always available, but we only display a smaller number of these ($maxrows) +// Clicking the 'Add Row' button increments $maxrows so you can see more of time servers +$timeservers = explode( ' ', $config['system']['timeservers']); +for ($i = $j = 0; $i < NUMTIMESERVERS; $i++){ + + if($i >= $maxrows) + continue; + + $group = new Form_Group($i == 0 ? 'Time servers':''); + + $group->add(new Form_Input( + 'server' . $i, + null, + 'text', + $timeservers[$i] + )); + + $group->add(new Form_Checkbox( + 'servprefer' . $i, + null, + 'Prefer', + isset($config['ntpd']['prefer']) && isset($timeservers[$i]) && substr_count($config['ntpd']['prefer'], $timeservers[$i]) + )); + + $group->add(new Form_Checkbox( + 'servselect' . $i, + null, + 'NoSelect', + isset($config['ntpd']['noselect']) && isset($timeservers[$i]) && substr_count($config['ntpd']['noselect'], $timeservers[$i]) + )); + + $section->add($group); +} + +// Show the 'Add Rows' button only if we are currently displaying less than the maximum +// number of configured servers +if($maxrows < NUMTIMESERVERS) { + $btnaddrow = new Form_Button( + 'btnaddrow', + 'Add Server', + 'services_ntpd.php?addrow=' . $maxrows + ); + + $btnaddrow->removeClass('btn-primary')->addClass('btn-success btn-sm'); +} else + $btnaddrow = false; + +$section->addInput(new Form_StaticText( + null, + $btnaddrow +))->setHelp('For best results three to five servers should be configured here.' . '<br />' . + 'The prefer option indicates that NTP should favor the use of this server more than all others.' . '<br />' . + 'The noselect option indicates that NTP should not use this server for time, but stats for this server will be collected and displayed.'); + +$section->addInput(new Form_Input( + 'ntporphan', + 'Orphan mode', + 'text', + $pconfig['ntporphan'] +))->setHelp('Orphan mode allows the system clock to be used when no other clocks are available. ' . + 'The number here specifies the stratum reported during orphan mode and should normally be set to a number high enough ' . + 'to insure that any other servers available to clients are preferred over this server. (default: 12).'); + +$section->addInput(new Form_Checkbox( + 'statsgraph', + 'NTP Graphs', + 'Enable RRD graphs of NTP statistics (default: disabled).', + $pconfig['statsgraph'] +)); + +$section->addInput(new Form_Checkbox( + 'logpeer', + 'Syslog logging', + 'Enable logging of peer messages (default: disabled).', + $pconfig['logpeer'] +)); + +$section->addInput(new Form_Checkbox( + 'logsys', + null, + 'Enable logging of system messages (default: disabled).', + $pconfig['logsys'] +))->setHelp('These options enable additional messages from NTP to be written to the System Log ' . + '<a href="diag_logs_ntpd.php">' . 'Status > System Logs > NTP' . '</a>'); + +// Statistics logging section +$btnadvstats = new Form_Button( + 'btnadvstats', + 'Advanced' +); + +$btnadvstats->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'Statistics logging', + $btnadvstats +))->setHelp('Warning: These options will create persistant daily log files in /var/log/ntp.'); + +$section->addInput(new Form_Checkbox( + 'clockstats', + null, + 'Enable logging of reference clock statistics (default: disabled).', + $pconfig['clockstats'] +)); + +$section->addInput(new Form_Checkbox( + 'loopstats', + null, + 'Enable logging of clock discipline statistics (default: disabled).', + $pconfig['loopstats'] +)); + +$section->addInput(new Form_Checkbox( + 'peerstats', + null, + 'Enable logging of NTP peer statistics (default: disabled).', + $pconfig['peerstats'] +)); + +// Access restrictions section +$btnadvrestr = new Form_Button( + 'btnadvrestr', + 'Advanced' +); + +$btnadvrestr->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'Access Restrictions', + $btnadvrestr +))->setHelp('These options control access to NTP from the WAN.'); + +$section->addInput(new Form_Checkbox( + 'kod', + null, + 'Enable Kiss-o\'-death packets (default: enabled).', + $pconfig['kod'] +)); + +$section->addInput(new Form_Checkbox( + 'nomodify', + null, + 'Deny state modifications (i.e. run time configuration) by ntpq and ntpdc (default: enabled).', + $pconfig['nomodify'] +)); + +$section->addInput(new Form_Checkbox( + 'noquery', + null, + 'Disable ntpq and ntpdc queries (default: disabled).', + $pconfig['noquery'] +)); + +$section->addInput(new Form_Checkbox( + 'noserve', + null, + 'Disable all except ntpq and ntpdc queries (default: disabled).', + $pconfig['noserve'] +)); + +$section->addInput(new Form_Checkbox( + 'nopeer', + null, + 'Deny packets that attempt a peer association (default: enabled).', + $pconfig['nopeer'] +)); + +$section->addInput(new Form_Checkbox( + 'notrap', + null, + 'Deny mode 6 control message trap service (default: enabled).', + $pconfig['notrap'] +))->addClass('advrestrictions'); + +// Leap seconds section +$btnleap = new Form_Button( + 'btnleap', + 'Advanced' +); + +$btnleap->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'Leap seconds', + $btnleap +))->setHelp('A leap second file allows NTP to advertize an upcoming leap second addition or subtraction. ' . + 'Normally this is only useful if this server is a stratum 1 time server. '); + +$section->addInput(new Form_Textarea( + 'leaptext', + null, + base64_decode(chunk_split($pconfig['leapsec'])) +))->setHelp('Enter Leap second configuration as text OR select a file to upload'); + +$section->addInput(new Form_Input( + 'leapfile', + null, + 'file' +))->addClass('btn-default'); + +$form->add($section); +print($form); + +?> + +<script> +//<![CDATA[ +events.push(function(){ + + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified checkbox lives so that the checkbox, its label and help text are hidden + function hideCheckbox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Make the ‘clear’ button a plain button, not a submit button + $('#btnadvstats').prop('type','button'); + + // On click, show the controls in the stats section + $("#btnadvstats").click(function() { + hideCheckbox('clockstats', false); + hideCheckbox('loopstats', false); + hideCheckbox('peerstats', false); + }); + + // Make the ‘clear’ button a plain button, not a submit button + $('#btnadvrestr').prop('type','button'); + + // On click, show the controls in the restrictions section + $("#btnadvrestr").click(function() { + hideCheckbox('nomodify', false); + hideCheckbox('noquery', false); + hideCheckbox('noserve', false); + hideCheckbox('nopeer', false); + hideCheckbox('notrap', false); + }); + + // Make the ‘btnleap’ button a plain button, not a submit button + $('#btnleap').prop('type','button'); + + // On click, show the controls in the leap seconds section + $("#btnleap").click(function() { + hideInput('leaptext', false); + hideInput('leapfile', false); + }); + + // Set intial states + hideCheckbox('clockstats', true); + hideCheckbox('loopstats', true); + hideCheckbox('peerstats', true); + hideCheckbox('kod', true); + hideCheckbox('nomodify', true); + hideCheckbox('noquery', true); + hideCheckbox('noserve', true); + hideCheckbox('nopeer', true); + hideCheckbox('notrap', true); + hideInput('leaptext', true); + hideInput('leapfile', true); +}); +//]]> +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_ntpd_gps.php b/src/usr/local/www/services_ntpd_gps.php new file mode 100644 index 0000000..9bf100d --- /dev/null +++ b/src/usr/local/www/services_ntpd_gps.php @@ -0,0 +1,545 @@ +<?php +/* $Id$ */ +/* + services_ntpd_gps.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2013 Dagorlad + Copyright (C) 2012 Jim Pingle + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: ntpd_gps +*/ + +##|+PRIV +##|*IDENT=page-services-ntpd-gps +##|*NAME=Services: NTP Serial GPS page +##|*DESCR=Allow access to the 'Services: NTP Serial GPS' page.. +##|*MATCH=services_ntpd_gps.php* +##|-PRIV + +require_once("guiconfig.inc"); + +function set_default_gps() { + global $config; + + if (!is_array($config['ntpd'])) { + $config['ntpd'] = array(); + } + if (is_array($config['ntpd']['gps'])) { + unset($config['ntpd']['gps']); + } + + $config['ntpd']['gps'] = array(); + $config['ntpd']['gps']['type'] = 'Default'; + /* copy an existing configured GPS port if it exists, the unset may be uncommented post production */ + if (!empty($config['ntpd']['gpsport']) && empty($config['ntpd']['gps']['port'])) { + $config['ntpd']['gps']['port'] = $config['ntpd']['gpsport']; + unset($config['ntpd']['gpsport']); /* this removes the original port config from config.xml */ + $config['ntpd']['gps']['speed'] = 0; + $config['ntpd']['gps']['nmea'] = 0; + } + + write_config("Setting default NTPd settings"); +} + +if ($_POST) { + unset($input_errors); + + if (!empty($_POST['gpsport']) && file_exists('/dev/'.$_POST['gpsport'])) { + $config['ntpd']['gps']['port'] = $_POST['gpsport']; + } else { + /* if port is not set, remove all the gps config */ + unset($config['ntpd']['gps']); + } + + if (!empty($_POST['gpstype'])) { + $config['ntpd']['gps']['type'] = $_POST['gpstype']; + } elseif (isset($config['ntpd']['gps']['type'])) { + unset($config['ntpd']['gps']['type']); + } + + if (!empty($_POST['gpsspeed'])) { + $config['ntpd']['gps']['speed'] = $_POST['gpsspeed']; + } elseif (isset($config['ntpd']['gps']['speed'])) { + unset($config['ntpd']['gps']['speed']); + } + + if (!empty($_POST['gpsnmea']) && ($_POST['gpsnmea'][0] === "0")) { + $config['ntpd']['gps']['nmea'] = "0"; + } else { + $config['ntpd']['gps']['nmea'] = strval(array_sum($_POST['gpsnmea'])); + } + + if (!empty($_POST['gpsfudge1'])) { + $config['ntpd']['gps']['fudge1'] = $_POST['gpsfudge1']; + } elseif (isset($config['ntpd']['gps']['fudge1'])) { + unset($config['ntpd']['gps']['fudge1']); + } + + if (!empty($_POST['gpsfudge2'])) { + $config['ntpd']['gps']['fudge2'] = $_POST['gpsfudge2']; + } elseif (isset($config['ntpd']['gps']['fudge2'])) { + unset($config['ntpd']['gps']['fudge2']); + } + + if (!empty($_POST['gpsstratum']) && ($_POST['gpsstratum']) < 17) { + $config['ntpd']['gps']['stratum'] = $_POST['gpsstratum']; + } elseif (isset($config['ntpd']['gps']['stratum'])) { + unset($config['ntpd']['gps']['stratum']); + } + + if (empty($_POST['gpsprefer'])) { + $config['ntpd']['gps']['prefer'] = 'on'; + } elseif (isset($config['ntpd']['gps']['prefer'])) { + unset($config['ntpd']['gps']['prefer']); + } + + if (!empty($_POST['gpsselect'])) { + $config['ntpd']['gps']['noselect'] = $_POST['gpsselect']; + } elseif (isset($config['ntpd']['gps']['noselect'])) { + unset($config['ntpd']['gps']['noselect']); + } + + if (!empty($_POST['gpsflag1'])) { + $config['ntpd']['gps']['flag1'] = $_POST['gpsflag1']; + } elseif (isset($config['ntpd']['gps']['flag1'])) { + unset($config['ntpd']['gps']['flag1']); + } + + if (!empty($_POST['gpsflag2'])) { + $config['ntpd']['gps']['flag2'] = $_POST['gpsflag2']; + } elseif (isset($config['ntpd']['gps']['flag2'])) { + unset($config['ntpd']['gps']['flag2']); + } + + if (!empty($_POST['gpsflag3'])) { + $config['ntpd']['gps']['flag3'] = $_POST['gpsflag3']; + } elseif (isset($config['ntpd']['gps']['flag3'])) { + unset($config['ntpd']['gps']['flag3']); + } + + if (!empty($_POST['gpsflag4'])) { + $config['ntpd']['gps']['flag4'] = $_POST['gpsflag4']; + } elseif (isset($config['ntpd']['gps']['flag4'])) { + unset($config['ntpd']['gps']['flag4']); + } + + if (!empty($_POST['gpssubsec'])) { + $config['ntpd']['gps']['subsec'] = $_POST['gpssubsec']; + } elseif (isset($config['ntpd']['gps']['subsec'])) { + unset($config['ntpd']['gps']['subsec']); + } + + if (!empty($_POST['gpsrefid'])) { + $config['ntpd']['gps']['refid'] = $_POST['gpsrefid']; + } elseif (isset($config['ntpd']['gps']['refid'])) { + unset($config['ntpd']['gps']['refid']); + } + + if (!empty($_POST['gpsinitcmd'])) { + $config['ntpd']['gps']['initcmd'] = base64_encode($_POST['gpsinitcmd']); + } elseif (isset($config['ntpd']['gps']['initcmd'])) { + unset($config['ntpd']['gps']['initcmd']); + } + + write_config("Updated NTP GPS Settings"); + + $retval = system_ntp_configure(); + $savemsg = get_std_save_message($retval); +} else { + /* set defaults if they do not already exist */ + if (!is_array($config['ntpd']) || !is_array($config['ntpd']['gps']) || empty($config['ntpd']['gps']['type'])) { + set_default_gps(); + } +} + +function build_nmea_list() { + global $pconfig; + + $nmealist = array('options' => array(), 'selected' => array()); + + $nmealist['options'][0] = 'All'; + $nmealist['options'][1] = 'RMC'; + $nmealist['options'][2] = 'GGA'; + $nmealist['options'][4] = 'GLL'; + $nmealist['options'][8] = 'ZDA or ZDG'; + + if(!$pconfig['nmea']) + array_push($nmealist['selected'], 0); + + foreach($nmealist['options'] as $val => $opt) { + if($pconfig['nmea'] & $val) + array_push($nmealist['selected'], $val); + } + + return($nmealist); +} + +$closehead = false; +$pconfig = &$config['ntpd']['gps']; +$pgtitle = array(gettext("Services"), gettext("NTP GPS")); +$shortcut_section = "ntp"; +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("NTP"), false, "services_ntpd.php"); +$tab_array[] = array(gettext("Serial GPS"), true, "services_ntpd_gps.php"); +$tab_array[] = array(gettext("PPS"), false, "services_ntpd_pps.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form; + +$section = new Form_Section('NTP Serial GPS Configuration'); + +$section->addInput(new Form_StaticText( + 'Notes', + 'A GPS connected via a serial port may be used as a reference clock for NTP. If the GPS also supports PPS and is properly configured, ' . + 'and connected, that GPS may also be used as a Pulse Per Second clock reference. NOTE: A USB GPS may work, but is not recommended due to USB bus timing issues.' . '<br />' . + 'For the best results, NTP should have at least three sources of time. So it is best to configure at least 2 servers under ' . + '<a href="services_ntpd.php">Services > NTP</a>' . + ' to minimize clock drift if the GPS data is not valid over time. Otherwise ntpd may only use values from the unsynchronized local clock when providing time to clients.' +)); + +$gpstypes = array('Custom', 'Default', 'Generic', 'Garmin', 'MediaTek', 'SiRF', 'U-Blox', 'SureGPS'); + +$section->addInput(new Form_Select( + 'gpstype', + 'GPS', + $pconfig['type'], + array_combine($gpstypes, $gpstypes) +))->setHelp('This option allows you to select a predefined configuration.' . + 'Default is the configuration of pfSense 2.1 and earlier (not recommended). Select Generic if your GPS is not listed.' . '<br /><br />' . + 'The perdefined configurations assume your GPS has already been set to NMEA mode.'); + +$serialports = glob("/dev/cua?[0-9]{,.[0-9]}", GLOB_BRACE); + +if (!empty($serialports)) { + $splist = array(); + + foreach ($serialports as $port) { + $shortport = substr($port,5); + $splist[$shortport] = $shortport; + } + + $section->addInput(new Form_Select( + 'gpsport', + 'Serial port', + $pconfig['port'], + $splist + ))->setHelp('All serial ports are listed, be sure to pick the port with the GPS attached. '); + + $section->addInput(new Form_Select( + 'gpsspeed', + null, + $pconfig['speed'], + [0 => '4800', 15 => '9600', 32 => '19200', 48 => '38400', 64 => '57600', 80 => '115200'] + + ))->setHelp('A higher baud rate is generally only helpful if the GPS is sending too many sentences. ' . + 'It is recommended to configure the GPS to send only one sentence at a baud rate of 4800 or 9600.'); +} + +$nmealist = build_nmea_list(); +$section->addInput(new Form_Select( + 'gpsnmea', + 'NMEA Sentences', + $nmealist['selected'], + $nmealist['options'], + true +))->setHelp('By default NTP will listen for all supported NMEA sentences. One or more sentences to listen for may be specified.'); + +$section->addInput(new Form_Input( + 'gpsfudge1', + 'Fudge time 1', + 'text', + $pconfig['fudge1'] +))->setHelp('Fudge time 1 is used to specify the GPS PPS signal offset (default: 0.0).'); + +$section->addInput(new Form_Input( + 'gpsfudge2', + 'Fudge time 2', + 'text', + $pconfig['fudge2'] +))->setHelp('Fudge time 2 is used to specify the GPS time offset (default: 0.0).'); + +$section->addInput(new Form_Input( + 'gpsstratum', + 'Stratum (0-16)', + 'text', + $pconfig['stratum'] +))->setHelp('This may be used to change the GPS Clock stratum (default: 0). This may be useful if, for some reason, you want ntpd to prefer a different clock'); + +$section->addInput(new Form_Checkbox( + 'gpsprefer', + 'Flags', + 'NTP should prefer this clock (default: enabled).', + !$pconfig['prefer'] +)); + +$section->addInput(new Form_Checkbox( + 'gpsselect', + null, + 'NTP should not use this clock, it will be displayed for reference only(default: disabled).', + $pconfig['noselect'] +)); + +$section->addInput(new Form_Checkbox( + 'gpsflag1', + null, + 'Enable PPS signal processing (default: enabled).', + $pconfig['flag1'] +)); + +$section->addInput(new Form_Checkbox( + 'gpsflag2', + null, + 'Enable falling edge PPS signal processing (default: rising edge).', + $pconfig['flag2'] +)); + +$section->addInput(new Form_Checkbox( + 'gpsflag3', + null, + 'Enable kernel PPS clock discipline (default: enabled).', + $pconfig['flag3'] +)); + +$section->addInput(new Form_Checkbox( + 'gpsflag4', + null, + 'Obscure location in timestamp (default: unobscured).', + $pconfig['flag4'] +)); + +$section->addInput(new Form_Checkbox( + 'gpssubsec', + null, + 'Log the sub-second fraction of the received time stamp (default: Not logged).', + $pconfig['subsec'] +))->setHelp('Enabling this will rapidly fill the log, but is useful for tuning Fudge time 2.'); + +$section->addInput(new Form_Input( + 'gpsrefid', + 'Clock ID', + 'text', + $pconfig['refid'], + ['placeholder' => '1 to 4 characters'] +))->setHelp('This may be used to change the GPS Clock ID (default: GPS).'); + +// Statistics logging section +$btnadvgps = new Form_Button( + 'btnadvgps', + 'Advanced' +); + +$btnadvgps->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'GPS Initialization', + $btnadvgps . ' ' . 'Show GPS Initialization commands' +)); + +$section->addInput(new Form_Textarea( + 'gpsinitcmd', + null, + $pconfig['initcmd'] +))->setHelp('Commands entered here will be sent to the GPS during initialization. Please read and understand your GPS documentation before making any changes here'); + +$group = new Form_Group('NMEA Checksum Calculator'); + +$group->add(new Form_Input( + 'nmeastring', + null +)); + +$btncalc = new Form_Button( + 'btncalc', + 'Calculate' +); + +$btncalc->removeClass('btn-primary')->addClass('btn-success btn-sm'); + +$group->add($btncalc); + +$group->add(new Form_Input( + 'result', + null, + 'text', + null, + ['placeholder' => 'Result'] +)); + +$group->setHelp('Enter the text between "$" and "*" of a NMEA command string:'); +$group->addClass('calculator'); + +$section->add($group); + +$form->add($section); +print($form); + +?> + +<script> +//<![CDATA[ +events.push(function(){ + + function NMEAChecksum(cmd) { + // Compute the checksum by XORing all the character values in the string. + var checksum = 0; + + for(var i = 0; i < cmd.length; i++) { + checksum = checksum ^ cmd.charCodeAt(i); + } + // Convert it to hexadecimal (base-16, upper case, most significant byte first). + var hexsum = Number(checksum).toString(16).toUpperCase(); + + if (hexsum.length < 2) { + hexsum = ("00" + hexsum).slice(-2); + } + + return(hexsum); + } + + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + function set_gps_default(type) { + $('#gpsnmea').val(0); + $('#gpsspeed').val(0); + $('#gpsfudge1').val(0); + + //stuff the JS object as needed for each type + switch (type) { + case "Default": + $('#gpsfudge1').val("0.155"); + $('#gpsfudge2').val(""); + $('#gpsinitcmd').val("JFBVQlgsNDAsR1NWLDAsMCwwLDAqNTkNCiRQVUJYLDQwLEdMTCwwLDAsMCwwKjVDDQokUFVCWCw0MCxaREEsMCwwLDAsMCo0NA0KJFBVQlgsNDAsVlRHLDAsMCwwLDAqNUUNCiRQVUJYLDQwLEdTViwwLDAsMCwwKjU5DQokUFVCWCw0MCxHU0EsMCwwLDAsMCo0RQ0KJFBVQlgsNDAsR0dBLDAsMCwwLDANCiRQVUJYLDQwLFRYVCwwLDAsMCwwDQokUFVCWCw0MCxSTUMsMCwwLDAsMCo0Ng0KJFBVQlgsNDEsMSwwMDA3LDAwMDMsNDgwMCwwDQokUFVCWCw0MCxaREEsMSwxLDEsMQ0K"); + break; + + case "Garmin": + $('#gpsfudge2').val("0.600"); + $('#gpsinitcmd').val("JFBHUk1DLCwsLCwsLCwsLDMsLDIsOCo1RQ0KJFBHUk1DMSwsMSwsLCwsLFcsLCwsLCwsKjMwDQokUEdSTU8sLDMqNzQNCiRQR1JNTyxHUFJNQywxKjNEDQokUEdSTU8sR1BHR0EsMSoyMA0KJFBHUk1PLEdQR0xMLDEqMjYNCg=="); + break; + + case "Generic": + $('#gpsfudge2').val("0.400"); + $('#gpsinitcmd').val(""); + break; + + case "MediaTek": + $('#gpsfudge2').val("0.400"); + $('#gpsinitcmd').val("JFBNVEsyMjUsMCoyQg0KJFBNVEszMTQsMSwxLDAsMSwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDEsMCoyOA0KJFBNVEszMDEsMioyRQ0KJFBNVEszMjAsMCoyRg0KJFBNVEszMzAsMCoyRQ0KJFBNVEszODYsMCoyMw0KJFBNVEszOTcsMCoyMw0KJFBNVEsyNTEsNDgwMCoxNA0K"); + break; + + case "SiRF": + $('#gpsfudge2').val("0.704"); //valid for 4800, 0.688 @ 9600, 0.640 @ USB + $('#gpsinitcmd').val("JFBTUkYxMDMsMDAsMDAsMDEsMDEqMjUNCiRQU1JGMTAzLDAxLDAwLDAxLDAxKjI0DQokUFNSRjEwMywwMiwwMCwwMCwwMSoyNA0KJFBTUkYxMDMsMDMsMDAsMDAsMDEqMjQNCiRQU1JGMTAzLDA0LDAwLDAxLDAxKjI0DQokUFNSRjEwMywwNSwwMCwwMCwwMSoyNA0KJFBTUkYxMDAsMSw0ODAwLDgsMSwwKjBFDQo="); + break; + + case "U-Blox": + $('#gpsfudge2').val("0.400"); + $('#gpsinitcmd').val("JFBVQlgsNDAsR0dBLDEsMSwxLDEsMCwwKjVBDQokUFVCWCw0MCxHTEwsMSwxLDEsMSwwLDAqNUMNCiRQVUJYLDQwLEdTQSwwLDAsMCwwLDAsMCo0RQ0KJFBVQlgsNDAsR1NWLDAsMCwwLDAsMCwwKjU5DQokUFVCWCw0MCxSTUMsMSwxLDEsMSwwLDAqNDcNCiRQVUJYLDQwLFZURywwLDAsMCwwLDAsMCo1RQ0KJFBVQlgsNDAsR1JTLDAsMCwwLDAsMCwwKjVEDQokUFVCWCw0MCxHU1QsMCwwLDAsMCwwLDAqNUINCiRQVUJYLDQwLFpEQSwxLDEsMSwxLDAsMCo0NA0KJFBVQlgsNDAsR0JTLDAsMCwwLDAsMCwwKjREDQokUFVCWCw0MCxEVE0sMCwwLDAsMCwwLDAqNDYNCiRQVUJYLDQwLEdQUSwwLDAsMCwwLDAsMCo1RA0KJFBVQlgsNDAsVFhULDAsMCwwLDAsMCwwKjQzDQokUFVCWCw0MCxUSFMsMCwwLDAsMCwwLDAqNTQNCiRQVUJYLDQxLDEsMDAwNywwMDAzLDQ4MDAsMCoxMw0K"); + break; + + case "SureGPS": + $('#gpsnmea').val(1); + $('#gpsspeed').val(16); + $('#gpsfudge2').val("0.407"); + $('#gpsinitcmd').val("JFBNVEsyMjUsMCoyQg0KJFBNVEszMTQsMSwxLDAsMSwwLDUsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDEsMCoyRA0KJFBNVEszMDEsMioyRQ0KJFBNVEszOTcsMCoyMw0KJFBNVEsxMDIqMzENCiRQTVRLMzEzLDEqMkUNCiRQTVRLNTEzLDEqMjgNCiRQTVRLMzE5LDAqMjUNCiRQTVRLNTI3LDAuMDAqMDANCiRQTVRLMjUxLDk2MDAqMTcNCg=="); + break; + default: + return; + } + + $('#gpsstratum').val(""); + $('#gpsrefid').val(""); + $('#gpsflag1').prop('checked', true); + $('#gpsflag2').prop('checked', false); + $('#gpsflag3').prop('checked', true); + $('#gpsflag4').prop('checked', false); + $('#gpssubsec').prop('checked', false); + } + + // Make the ‘Advanced’ button a plain button, not a submit button + $('#btnadvgps').prop('type','button'); + + // On click, show the controls in the GPS Initialization section + $("#btnadvgps").click(function() { + hideInput('gpsinitcmd', false); + hideClass('calculator', false); + }); + + // Make the ‘Calculate’ button a plain button, not a submit button + $('#btncalc').prop('type','button'); + $('#result').prop("disabled", true); + + // Onclick read the string from the nmeastring box, calculate the checksum + // and display the results in the result box + $("#btncalc").click(function() { + $('#result').val(NMEAChecksum($('#nmeastring').val())); + }); + + // When the 'GPS' selector is changed, we set tth gps defaults + $('#gpstype').on('change', function() { + set_gps_default($(this).val()); + }); + + hideInput('gpsinitcmd', true); + hideClass('calculator', true); + + set_gps_default('<?=$pconfig['type']?>'); + + // Checkboxes gpsprefer and gpsselect are mutually exclusive + $('#gpsprefer').click(function() { + if ($(this).is(':checked')) { + $('#gpsselect').prop('checked', false); + } + }); + + $('#gpsselect').click(function() { + if ($(this).is(':checked')) { + $('#gpsprefer').prop('checked', false); + } + }); +}); +//]]> +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_ntpd_pps.php b/src/usr/local/www/services_ntpd_pps.php new file mode 100644 index 0000000..c8d817b --- /dev/null +++ b/src/usr/local/www/services_ntpd_pps.php @@ -0,0 +1,209 @@ +<?php +/* $Id$ */ +/* + services_ntpd_pps.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2013 Dagorlad + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: ntpd_pps +*/ + +##|+PRIV +##|*IDENT=page-services-ntpd-pps +##|*NAME=Services: NTP PPS page +##|*DESCR=Allow access to the 'Services: NTP PPS' page.. +##|*MATCH=services_ntpd_pps.php* +##|-PRIV + +require_once("guiconfig.inc"); + +if (!is_array($config['ntpd'])) { + $config['ntpd'] = array(); +} +if (!is_array($config['ntpd']['pps'])) { + $config['ntpd']['pps'] = array(); +} + +if ($_POST) { + unset($input_errors); + + if (!$input_errors) { + if (!empty($_POST['ppsport']) && file_exists('/dev/'.$_POST['ppsport'])) { + $config['ntpd']['pps']['port'] = $_POST['ppsport']; + } else { + /* if port is not set, remove all the pps config */ + unset($config['ntpd']['pps']); + } + + if (!empty($_POST['ppsfudge1'])) { + $config['ntpd']['pps']['fudge1'] = $_POST['ppsfudge1']; + } elseif (isset($config['ntpd']['pps']['fudge1'])) { + unset($config['ntpd']['pps']['fudge1']); + } + + if (!empty($_POST['ppsstratum']) && ($_POST['ppsstratum']) < 17) { + $config['ntpd']['pps']['stratum'] = $_POST['ppsstratum']; + } elseif (isset($config['ntpd']['pps']['stratum'])) { + unset($config['ntpd']['pps']['stratum']); + } + + if (!empty($_POST['ppsselect'])) { + $config['ntpd']['pps']['noselect'] = $_POST['ppsselect']; + } elseif (isset($config['ntpd']['pps']['noselect'])) { + unset($config['ntpd']['pps']['noselect']); + } + + if (!empty($_POST['ppsflag2'])) { + $config['ntpd']['pps']['flag2'] = $_POST['ppsflag2']; + } elseif (isset($config['ntpd']['pps']['flag2'])) { + unset($config['ntpd']['pps']['flag2']); + } + + if (!empty($_POST['ppsflag3'])) { + $config['ntpd']['pps']['flag3'] = $_POST['ppsflag3']; + } elseif (isset($config['ntpd']['pps']['flag3'])) { + unset($config['ntpd']['pps']['flag3']); + } + + if (!empty($_POST['ppsflag4'])) { + $config['ntpd']['pps']['flag4'] = $_POST['ppsflag4']; + } elseif (isset($config['ntpd']['pps']['flag4'])) { + unset($config['ntpd']['pps']['flag4']); + } + + if (!empty($_POST['ppsrefid'])) { + $config['ntpd']['pps']['refid'] = $_POST['ppsrefid']; + } elseif (isset($config['ntpd']['pps']['refid'])) { + unset($config['ntpd']['pps']['refid']); + } + + write_config("Updated NTP PPS Settings"); + + $retval = 0; + $retval = system_ntp_configure(); + $savemsg = get_std_save_message($retval); + } +} + +$pconfig = &$config['ntpd']['pps']; + +$pgtitle = array(gettext("Services"), gettext("NTP PPS")); +$shortcut_section = "ntp"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +$tab_array = array(); +$tab_array[] = array(gettext("NTP"), false, "services_ntpd.php"); +$tab_array[] = array(gettext("Serial GPS"), false, "services_ntpd_gps.php"); +$tab_array[] = array(gettext("PPS"), true, "services_ntpd_pps.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form; + +$section = new Form_Section('NTP Serial PPS Configuration'); + +$section->addInput(new Form_StaticText( + 'Notes', + 'Devices with a Pulse Per Second output such as radios that receive a time signal from DCF77 (DE), JJY (JP), MSF (GB) or WWVB (US) may be used as a PPS reference for NTP.' . + 'A serial GPS may also be used, but the serial GPS driver would usually be the better option. ' . + 'A PPS signal only provides a reference to the change of a second, so at least one other source to number the seconds is required.' . '<br /><br />' . + 'At least 3 additional time sources should be configured under ' . + '<a href="services_ntpd.php">' . 'Services > NTP' . '</a>' . ' to reliably supply the time of each PPS pulse.' +)); + +$serialports = glob("/dev/cua?[0-9]{,.[0-9]}", GLOB_BRACE); + +if (!empty($serialports)) { + $splist = array(); + + foreach ($serialports as $port) { + $shortport = substr($port,5); + $splist[$shortport] = $shortport; + } + + $section->addInput(new Form_Select( + 'ppsport', + 'Serial port', + $pconfig['port'], + $splist + ))->setHelp('All serial ports are listed, be sure to pick the port with the PPS source attached. '); +} + +$section->addInput(new Form_Input( + 'ppsfudge1', + 'Fudge time', + 'text', + $pconfig['fudge1'] +))->setHelp('Fudge time is used to specify the PPS signal offset from the actual second such as the transmission delay between the transmitter and the receiver. (default: 0.0).'); + +$section->addInput(new Form_Input( + 'ppsstratum', + 'Stratum', + 'text', + $pconfig['stratum'] +))->setHelp('This may be used to change the PPS Clock stratum (default: 0). This may be useful if, for some reason, you want ntpd to prefer a different clock and just monitor this source.'); + +$section->addInput(new Form_Checkbox( + 'ppsflag2', + 'Flags', + 'Enable falling edge PPS signal processing (default: rising edge).', + $pconfig['flag2'] +)); + +$section->addInput(new Form_Checkbox( + 'ppsflag3', + null, + 'Enable kernel PPS clock discipline (default: disabled).', + $pconfig['flag3'] +)); + +$section->addInput(new Form_Checkbox( + 'ppsflag4', + null, + 'Record a timestamp once for each second, useful for constructing Allan deviation plots (default: disabled).', + $pconfig['flag4'] +)); + +$section->addInput(new Form_Input( + 'ppsrefid', + 'Clock ID', + 'text', + $pconfig['refid'], + ['placeholder' => '1 to 4 characters'] +))->setHelp('This may be used to change the PPS Clock ID (default: PPS).'); + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_rfc2136.php b/src/usr/local/www/services_rfc2136.php new file mode 100644 index 0000000..a73880a --- /dev/null +++ b/src/usr/local/www/services_rfc2136.php @@ -0,0 +1,178 @@ +<?php +/* $Id$ */ +/* + services_rfc2136.php + + Copyright (C) 2008 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: dnsupdate +*/ + +##|+PRIV +##|*IDENT=page-services-rfc2136clients +##|*NAME=Services: RFC 2136 clients page +##|*DESCR=Allow access to the 'Services: RFC 2136 clients' page. +##|*MATCH=services_rfc2136.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['dnsupdates']['dnsupdate'])) { + $config['dnsupdates']['dnsupdate'] = array(); +} + +$a_rfc2136 = &$config['dnsupdates']['dnsupdate']; + +if ($_GET['act'] == "del") { + unset($a_rfc2136[$_GET['id']]); + + write_config(); + + header("Location: services_rfc2136.php"); + exit; +} + +$pgtitle = array(gettext("Services"), gettext("RFC 2136 clients")); +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("DynDns"), false, "services_dyndns.php"); +$tab_array[] = array(gettext("RFC 2136"), true, "services_rfc2136.php"); +display_top_tabs($tab_array); + +if ($input_errors) + print_input_errors($input_errors); +?> + +<form action="services_rfc2136.php" method="post" name="iform" id="iform"> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("If")?></th> + <th><?=gettext("Server")?></th> + <th><?=gettext("Hostname")?></th> + <th><?=gettext("Cached IP")?></th> + <th><?=gettext("Description")?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php + + +$iflist = get_configured_interface_with_descr(); + +$i = 0; +foreach ($a_rfc2136 as $rfc2136): +?> + <tr <?=(isset($rfc2136['enable']) ? '' : 'class="disabled"')?>"> + <td> +<?php + foreach ($iflist as $if => $ifdesc) { + if ($rfc2136['interface'] == $if) { + print($ifdesc); + break; + } + } +?> + </td> + <td> + <?=htmlspecialchars($rfc2136['server'])?> + </td> + <td> + <?=htmlspecialchars($rfc2136['host'])?> + </td> + <td> +<?php + $filename = "{$g['conf_path']}/dyndns_{$rfc2136['interface']}_rfc2136_" . escapeshellarg($rfc2136['host']) . "_{$rfc2136['server']}.cache"; + + if (file_exists($filename)) { + print('IPv4: '); + if (isset($rfc2136['usepublicip'])) + $ipaddr = dyndnsCheckIP($rfc2136['interface']); + else + $ipaddr = get_interface_ip($rfc2136['interface']); + + $cached_ip_s = explode("|", file_get_contents($filename)); + $cached_ip = $cached_ip_s[0]; + + if ($ipaddr != $cached_ip) + print('<font color="red">'); + else + print('<font color="green">'); + + print(tmlspecialchars($cached_ip)); + print('</font>'); + } else { + print('IPv4: N/A'); + } + + print('<br />'); + + if (file_exists("{$filename}.ipv6")) { + print('IPv6: '); + $ipaddr = get_interface_ipv6($rfc2136['interface']); + $cached_ip_s = explode("|", file_get_contents("{$filename}.ipv6")); + $cached_ip = $cached_ip_s[0]; + + if ($ipaddr != $cached_ip) + print('<font color="red">'); + else + print('<font color="green">'); + + print(htmlspecialchars($cached_ip)); + print('</font>'); + } else { + print('IPv6: N/A'); + } + +?> + </td> + <td> + <?=htmlspecialchars($rfc2136['descr'])?> + </td> + <td> + <a href="services_rfc2136_edit.php?id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext('Edit')?></a> + <a href="services_rfc2136.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger"><?=gettext("Delete")?></a> + </td> + </tr> +<?php + $i++; +endforeach; ?> + + </tbody> + </table> + </div> +</form> + +<nav class="action-buttons"> + <a href="services_rfc2136_edit.php" class="btn btn-sm btn-success"><?=gettext('Add')?></a> +</nav> + +<?php +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_rfc2136_edit.php b/src/usr/local/www/services_rfc2136_edit.php new file mode 100644 index 0000000..53445bb --- /dev/null +++ b/src/usr/local/www/services_rfc2136_edit.php @@ -0,0 +1,295 @@ +<?php +/* $Id$ */ +/* + services_rfc2136_edit.php + + Copyright (C) 2008 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: dnsupdate +*/ + +require("guiconfig.inc"); + +if (!is_array($config['dnsupdates']['dnsupdate'])) { + $config['dnsupdates']['dnsupdate'] = array(); +} + +$a_rfc2136 = &$config['dnsupdates']['dnsupdate']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && isset($a_rfc2136[$id])) { + $pconfig['enable'] = isset($a_rfc2136[$id]['enable']); + $pconfig['host'] = $a_rfc2136[$id]['host']; + $pconfig['ttl'] = $a_rfc2136[$id]['ttl']; + if (!$pconfig['ttl']) { + $pconfig['ttl'] = 60; + } + $pconfig['keydata'] = $a_rfc2136[$id]['keydata']; + $pconfig['keyname'] = $a_rfc2136[$id]['keyname']; + $pconfig['keytype'] = $a_rfc2136[$id]['keytype']; + if (!$pconfig['keytype']) { + $pconfig['keytype'] = "zone"; + } + $pconfig['server'] = $a_rfc2136[$id]['server']; + $pconfig['interface'] = $a_rfc2136[$id]['interface']; + $pconfig['usetcp'] = isset($a_rfc2136[$id]['usetcp']); + $pconfig['usepublicip'] = isset($a_rfc2136[$id]['usepublicip']); + $pconfig['recordtype'] = $a_rfc2136[$id]['recordtype']; + if (!$pconfig['recordtype']) { + $pconfig['recordtype'] = "both"; + } + $pconfig['descr'] = $a_rfc2136[$id]['descr']; + +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = array(); + $reqdfieldsn = array(); + $reqdfields = array_merge($reqdfields, explode(" ", "host ttl keyname keydata")); + $reqdfieldsn = array_merge($reqdfieldsn, array(gettext("Hostname"), gettext("TTL"), gettext("Key name"), gettext("Key"))); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($_POST['host'] && !is_domain($_POST['host']))) { + $input_errors[] = gettext("The DNS update host name contains invalid characters."); + } + if (($_POST['ttl'] && !is_numericint($_POST['ttl']))) { + $input_errors[] = gettext("The DNS update TTL must be an integer."); + } + if (($_POST['keyname'] && !is_domain($_POST['keyname']))) { + $input_errors[] = gettext("The DNS update key name contains invalid characters."); + } + + if (!$input_errors) { + $rfc2136 = array(); + $rfc2136['enable'] = $_POST['enable'] ? true : false; + $rfc2136['host'] = $_POST['host']; + $rfc2136['ttl'] = $_POST['ttl']; + $rfc2136['keyname'] = $_POST['keyname']; + $rfc2136['keytype'] = $_POST['keytype']; + $rfc2136['keydata'] = $_POST['keydata']; + $rfc2136['server'] = $_POST['server']; + $rfc2136['usetcp'] = $_POST['usetcp'] ? true : false; + $rfc2136['usepublicip'] = $_POST['usepublicip'] ? true : false; + $rfc2136['recordtype'] = $_POST['recordtype']; + $rfc2136['interface'] = $_POST['interface']; + $rfc2136['descr'] = $_POST['descr']; + + if (isset($id) && $a_rfc2136[$id]) { + $a_rfc2136[$id] = $rfc2136; + } else { + $a_rfc2136[] = $rfc2136; + } + + write_config(gettext("New/Edited RFC2136 dnsupdate entry was posted.")); + + if ($_POST['Submit'] == gettext("Save & Force Update")) { + $retval = services_dnsupdate_process("", $rfc2136['host'], true); + } else { + $retval = services_dnsupdate_process(); + } + + header("Location: services_rfc2136.php"); + exit; + } +} + +$pgtitle = array(gettext("Services"), gettext("RFC 2136 client"), gettext("Edit")); +include("head.inc"); + +require('classes/Form.class.php'); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg); + +$form = new Form; + +$section = new Form_Section('RFC 2136 client'); + +$section->addInput(new Form_Checkbox( + 'enable', + 'Enable', + null, + $pconfig['enable'] +)); + +$optionlist = array(); +$iflist = get_configured_interface_with_descr(); + +foreach ($iflist as $ifnam => $ifdescr) + $optionlist[$ifnam] = $ifdescr; + +$section->addInput(new Form_Select( + 'ifname', + 'Interface', + $pconfig['ifname'], + $optionlist +)); + +$section->addInput(new Form_Input( + 'host', + 'Hostname', + 'text', + $pconfig['host'] +))->setHelp('Fully qualified hostname of the host to be updated'); + +$section->addInput(new Form_Input( + 'ttl', + 'TTL (seconds)', + 'number', + $pconfig['ttl'] +)); + +$section->addInput(new Form_Input( + 'keyname', + 'Key name', + 'text', + $pconfig['keyname'] +))->setHelp('This must match the setting on the DNS server.'); + +$group = new Form_Group('Key Type'); + +$group->add(new Form_Checkbox( + 'keytype', + 'Key Type', + 'Zone', + ($pconfig['keytype']=='zone'), + 'zone' +))->displayAsRadio(); + +$group->add($input = new Form_Checkbox( + 'keytype', + 'Key Type', + 'Host', + ($pconfig['keytype']=='host'), + 'host' +))->displayAsRadio(); + +$group->add($input = new Form_Checkbox( + 'keytype', + 'Key Type', + 'User', + ($pconfig['keytype']=='user'), + 'user' +))->displayAsRadio(); + +$section->add($group); + +$section->addInput(new Form_Input( + 'keydata', + 'Key', + 'text', + $pconfig['keydata'] +))->setHelp('Paste an HMAC-MD5 key here.'); + +$section->addInput(new Form_Input( + 'server', + 'Server', + 'text', + $pconfig['server'] +)); + +$section->addInput(new Form_Checkbox( + 'usetcp', + 'Protocol', + 'Use TCP instead of UDP', + $pconfig['usetcp'] +)); + +$section->addInput(new Form_Checkbox( + 'usepublicip', + 'Use public IP', + 'If the interface IP is private, attempt to fetch and use the public IP instead.', + $pconfig['usepublicip'] +)); + +$group = new Form_Group('Record Type'); + +$group->add(new Form_Checkbox( + 'recordtype', + 'Record Type', + 'A (IPv4)', + ($pconfig['keytype']=='A'), + 'A' +))->displayAsRadio(); + +$group->add($input = new Form_Checkbox( + 'recordtype', + 'Record Type', + 'AAAA (IPv6)', + ($pconfig['keytype']=='AAAA'), + 'AAAA' +))->displayAsRadio(); + +$group->add($input = new Form_Checkbox( + 'recordtype', + 'Record Type', + 'Both', + ($pconfig['keytype']=='both'), + 'both' +))->displayAsRadio(); + +$section->add($group); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +if (isset($id) && $a_rfc2136[$id]){ + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); +print($form); + +print_info_box(sprintf('You must configure a DNS server in %sSystem: ' . + 'General setup %sor allow the DNS server list to be overridden ' . + 'by DHCP/PPP on WAN for dynamic DNS updates to work.','<a href="system.php">', '</a>')); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_router_advertisements.php b/src/usr/local/www/services_router_advertisements.php new file mode 100644 index 0000000..3cf9dbb --- /dev/null +++ b/src/usr/local/www/services_router_advertisements.php @@ -0,0 +1,480 @@ +<?php +/* $Id$ */ +/* + services_router_advertisements.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + part of pfSense (https://www.pfsense.org) + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /bin/rm + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-services-router-advertisements +##|*NAME=Services: Router advertisementspage +##|*DESCR=Allow access to the 'Services: Router Advertisements' page. +##|*MATCH=services_router_advertisements.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!$g['services_dhcp_server_enable']) { + header("Location: /"); + exit; +} + +/* Fix failover DHCP problem + * http://article.gmane.org/gmane.comp.security.firewalls.pfsense.support/18749 + */ +ini_set("memory_limit", "64M"); + +$if = $_GET['if']; +if ($_POST['if']) { + $if = $_POST['if']; +} + +/* if OLSRD is enabled, allow WAN to house DHCP. */ +if ($config['installedpackages']['olsrd']) { + foreach ($config['installedpackages']['olsrd']['config'] as $olsrd) { + if ($olsrd['enable']) { + $is_olsr_enabled = true; + break; + } + } +} + +if (!$_GET['if']) { + $savemsg = "<p><b>" . gettext("The DHCPv6 Server can only be enabled on interfaces configured with static IP addresses") . ".</b></p>" . + "<p><b>" . gettext("Only interfaces configured with a static IP will be shown") . ".</b></p>"; +} + +$iflist = get_configured_interface_with_descr(); + +/* set the starting interface */ +if (!$if || !isset($iflist[$if])) { + foreach ($iflist as $ifent => $ifname) { + $oc = $config['interfaces'][$ifent]; + if ((is_array($config['dhcpdv6'][$ifent]) && !isset($config['dhcpdv6'][$ifent]['enable']) && !(is_ipaddrv6($oc['ipaddrv6']) && (!is_linklocal($oc['ipaddrv6'])))) || + (!is_array($config['dhcpdv6'][$ifent]) && !(is_ipaddrv6($oc['ipaddrv6']) && (!is_linklocal($oc['ipaddrv6']))))) { + continue; + } + $if = $ifent; + break; + } +} + +if (is_array($config['dhcpdv6'][$if])) { + /* RA specific */ + $pconfig['ramode'] = $config['dhcpdv6'][$if]['ramode']; + $pconfig['rapriority'] = $config['dhcpdv6'][$if]['rapriority']; + if ($pconfig['rapriority'] == "") { + $pconfig['rapriority'] = "medium"; + } + $pconfig['rainterface'] = $config['dhcpdv6'][$if]['rainterface']; + $pconfig['radomainsearchlist'] = $config['dhcpdv6'][$if]['radomainsearchlist']; + list($pconfig['radns1'], $pconfig['radns2'], $pconfig['radns3'], $pconfig['radns4']) = $config['dhcpdv6'][$if]['radnsserver']; + $pconfig['rasamednsasdhcp6'] = isset($config['dhcpdv6'][$if]['rasamednsasdhcp6']); + + $pconfig['subnets'] = $config['dhcpdv6'][$if]['subnets']['item']; +} +if (!is_array($pconfig['subnets'])) { + $pconfig['subnets'] = array(); +} + +$advertise_modes = array("disabled" => "Disabled", + "router" => "Router Only", + "unmanaged" => "Unmanaged", + "managed" => "Managed", + "assist" => "Assisted", + "stateless_dhcp" => "Stateless DHCP"); +$priority_modes = array("low" => "Low", + "medium" => "Normal", + "high" => "High"); +$carplist = get_configured_carp_interface_list(); + +$subnets_help = gettext("Subnets are specified in CIDR format. " . + "Select the CIDR mask that pertains to each entry. " . + "/128 specifies a single IPv6 host; /64 specifies a normal IPv6 network; etc. " . + "If no subnets are specified here, the Router Advertisement (RA) Daemon will advertise to the subnet to which the router's interface is assigned."); + +if ($_POST) { + unset($input_errors); + + $pconfig = $_POST; + + /* input validation */ + + $pconfig['subnets'] = array(); + for ($x = 0; $x < 5000; $x += 1) { + $address = trim($_POST['subnet_address' . $x]); + if ($address === "") { + continue; + } + + $bits = trim($_POST['subnet_bits' . $x]); + if ($bits === "") { + $bits = "128"; + } + + if (is_alias($address)) { + $pconfig['subnets'][] = $address; + } else { + $pconfig['subnets'][] = $address . "/" . $bits; + if (!is_ipaddrv6($address)) { + $input_errors[] = sprintf(gettext("An invalid subnet or alias was specified. [%s/%s]"), $address, $bits); + } + } + } + + if (($_POST['radns1'] && !is_ipaddrv6($_POST['radns1'])) || ($_POST['radns2'] && !is_ipaddrv6($_POST['radns2'])) || ($_POST['radns3'] && !is_ipaddrv6($_POST['radns3'])) || ($_POST['radns4'] && !is_ipaddrv6($_POST['radns4']))) { + $input_errors[] = gettext("A valid IPv6 address must be specified for each of the DNS servers."); + } + if ($_POST['radomainsearchlist']) { + $domain_array=preg_split("/[ ;]+/", $_POST['radomainsearchlist']); + foreach ($domain_array as $curdomain) { + if (!is_domain($curdomain)) { + $input_errors[] = gettext("A valid domain search list must be specified."); + break; + } + } + } + + if (!$input_errors) { + if (!is_array($config['dhcpdv6'][$if])) { + $config['dhcpdv6'][$if] = array(); + } + + $config['dhcpdv6'][$if]['ramode'] = $_POST['ramode']; + $config['dhcpdv6'][$if]['rapriority'] = $_POST['rapriority']; + $config['dhcpdv6'][$if]['rainterface'] = $_POST['rainterface']; + + $config['dhcpdv6'][$if]['radomainsearchlist'] = $_POST['radomainsearchlist']; + unset($config['dhcpdv6'][$if]['radnsserver']); + if ($_POST['radns1']) { + $config['dhcpdv6'][$if]['radnsserver'][] = $_POST['radns1']; + } + if ($_POST['radns2']) { + $config['dhcpdv6'][$if]['radnsserver'][] = $_POST['radns2']; + } + if ($_POST['radns3']) { + $config['dhcpdv6'][$if]['radnsserver'][] = $_POST['radns3']; + } + if ($_POST['radns4']) { + $config['dhcpdv6'][$if]['radnsserver'][] = $_POST['radns4']; + } + + $config['dhcpdv6'][$if]['rasamednsasdhcp6'] = ($_POST['rasamednsasdhcp6']) ? true : false; + + if (count($pconfig['subnets'])) { + $config['dhcpdv6'][$if]['subnets']['item'] = $pconfig['subnets']; + } else { + unset($config['dhcpdv6'][$if]['subnets']); + } + + write_config(); + $retval = services_radvd_configure(); + $savemsg = get_std_save_message($retval); + } +} + +$pgtitle = array(gettext("Services"), gettext("Router advertisements")); + +include("head.inc"); + +?> + +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> + +<script type="text/javascript" src="/javascript/row_helper.js"> +</script> +<script type="text/javascript" src="/javascript/autosuggest.js?rev=1"> +</script> +<script type="text/javascript" src="/javascript/suggestions.js"> +</script> +<script type="text/javascript"> +//<![CDATA[ + rowname[0] = "subnet_address"; + rowtype[0] = "textbox"; + rowsize[0] = "30"; + rowname[1] = "subnet_bits"; + rowtype[1] = "select"; + rowsize[1] = "1"; + function add_alias_control() { + var name = "subnet_address" + (totalrows - 1); + obj = document.getElementById(name); + obj.setAttribute('class', 'formfldalias'); + obj.setAttribute('autocomplete', 'off'); + objAlias[totalrows - 1] = new AutoSuggestControl(obj, new StateSuggestions(addressarray)); + } +//]]> +</script> + +<form action="services_router_advertisements.php" method="post" name="iform" id="iform"> +<?php if ($input_errors) print_input_errors($input_errors); ?> +<?php if ($savemsg) print_info_box($savemsg); ?> +<table width="100%" border="0" cellpadding="0" cellspacing="0" summary="router advert"> + <tr> + <td> +<?php + /* active tabs */ + $tab_array = array(); + $tabscounter = 0; + $i = 0; + foreach ($iflist as $ifent => $ifname) { + $oc = $config['interfaces'][$ifent]; + if ((is_array($config['dhcpdv6'][$ifent]) && !isset($config['dhcpdv6'][$ifent]['enable']) && !(is_ipaddrv6($oc['ipaddrv6']) && (!is_linklocal($oc['ipaddrv6'])))) || + (!is_array($config['dhcpdv6'][$ifent]) && !(is_ipaddrv6($oc['ipaddrv6']) && (!is_linklocal($oc['ipaddrv6']))))) { + continue; + } + if ($ifent == $if) { + $active = true; + } else { + $active = false; + } + $tab_array[] = array($ifname, $active, "services_dhcpv6.php?if={$ifent}"); + $tabscounter++; + } + if ($tabscounter == 0) { + echo "</td></tr></table></form>"; + include("fend.inc"); + echo "</body>"; + echo "</html>"; + exit; + } + display_top_tabs($tab_array); +?> + </td> + </tr> + <tr> + <td class="tabnavtbl"> +<?php + $tab_array = array(); + $tab_array[] = array(gettext("DHCPv6 Server"), false, "services_dhcpv6.php?if={$if}"); + $tab_array[] = array(gettext("Router Advertisements"), true, "services_router_advertisements.php?if={$if}"); + display_top_tabs($tab_array); +?> + </td> + </tr> + <tr> + <td> + <div id="mainarea"> + <table class="tabcont" width="100%" border="0" cellpadding="6" cellspacing="0" summary="main area"> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Router Advertisements");?></td> + <td width="78%" class="vtable"> + <select name="ramode" id="ramode"> + <?php foreach ($advertise_modes as $name => $value) { ?> + <option value="<?=$name ?>" <?php if ($pconfig['ramode'] == $name) echo "selected=\"selected\""; ?> > <?=$value ?></option> + <?php } ?> + </select> + <br /> + <strong><?php printf(gettext("Select the Operating Mode for the Router Advertisement (RA) Daemon."))?></strong> + <?php printf(gettext("Use \"Router Only\" to only advertise this router, \"Unmanaged\" for Router Advertising with Stateless Autoconfig, \"Managed\" for assignment through (a) DHCPv6 Server, \"Assisted\" for DHCPv6 Server assignment combined with Stateless Autoconfig"));?> + <?php printf(gettext("It is not required to activate this DHCPv6 server when set to \"Managed\", this can be another host on the network")); ?> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Router Priority");?></td> + <td width="78%" class="vtable"> + <select name="rapriority" id="rapriority"> + <?php foreach ($priority_modes as $name => $value) { ?> + <option value="<?=$name ?>" <?php if ($pconfig['rapriority'] == $name) echo "selected=\"selected\""; ?> > <?=$value ?></option> + <?php } ?> + </select> + <br /> + <strong><?php printf(gettext("Select the Priority for the Router Advertisement (RA) Daemon."))?></strong> + </td> + </tr> +<?php + $carplistif = array(); + if (count($carplist) > 0) { + foreach ($carplist as $ifname => $vip) { + if ((preg_match("/^{$if}_/", $ifname)) && (is_ipaddrv6($vip))) { + $carplistif[$ifname] = $vip; + } + } + } + if (count($carplistif) > 0) { +?> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("RA Interface");?></td> + <td width="78%" class="vtable"> + <select name="rainterface" id="rainterface"> + <?php foreach ($carplistif as $ifname => $vip) { ?> + <option value="interface" <?php if ($pconfig['rainterface'] == "interface") echo "selected=\"selected\""; ?> > <?=strtoupper($if); ?></option> + <option value="<?=$ifname ?>" <?php if ($pconfig['rainterface'] == $ifname) echo "selected=\"selected\""; ?> > <?="$ifname - $vip"; ?></option> + <?php } ?> + </select> + <br /> + <strong><?php printf(gettext("Select the Interface for the Router Advertisement (RA) Daemon."))?></strong> + </td> + </tr> +<?php + } +?> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("RA Subnet(s)");?></td> + <td width="78%" class="vtable"> + <div><?= htmlentities($subnets_help) ?></div> + <table id="maintable" summary="subnets"> + <tbody> +<?php + $counter = 0; + foreach ($pconfig['subnets'] as $subnet) { + $address_name = "subnet_address" . $counter; + $bits_name = "subnet_bits" . $counter; + list($address, $subnet) = explode("/", $subnet); +?> + <tr> + <td> + <input autocomplete="off" name="<?= $address_name ?>" type="text" class="formfldalias" id="<?= $address_name ?>" size="30" value="<?= htmlentities($address) ?>" /> + </td> + <td> + <select name="<?= $bits_name ?>" class="formselect" id="<?= $bits_name ?>"> + <option value=""> + <?php for ($i = 128; $i >= 0; $i -= 1) { ?> + <option value="<?= $i ?>" <?= ("$subnet" === "$i") ? "selected='selected'" : "" ?>><?= $i ?></option> + <?php } ?> + </select> + </td> + <td> + <a onclick="removeRow(this); return false;" href="#"><img border="0" src="/themes/<?echo $g['theme'];?>/images/icons/icon_x.gif" alt="" title="<?=gettext("remove this entry"); ?>" /></a> + </td> + </tr> +<?php + $counter += 1; + } +?> + <tr style="display:none"> + <td></td> + </tr> + </tbody> + </table> + <script type="text/javascript"> + //<![CDATA[ + field_counter_js = 2; + totalrows = <?= $counter ?>; + //]]> + </script> + <div id="addrowbutton"> + <a onclick="javascript:addRowTo('maintable'); add_alias_control(); return false;" href="#"><!-- + --><img border="0" src="/themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" alt="" title="<?=gettext("add another entry"); ?>" /></a> + </div> + </td> + </tr> + + <tr> + <td colspan="2" class="list" height="12"> </td> + </tr> + + <tr> + <td colspan="2" valign="top" class="listtopic">DNS</td> + </tr> + + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("DNS servers");?></td> + <td width="78%" class="vtable"> + <input name="radns1" type="text" class="formfld unknown" id="radns1" size="28" value="<?=htmlspecialchars($pconfig['radns1']);?>" /><br /> + <input name="radns2" type="text" class="formfld unknown" id="radns2" size="28" value="<?=htmlspecialchars($pconfig['radns2']);?>" /><br /> + <input name="radns3" type="text" class="formfld unknown" id="radns3" size="28" value="<?=htmlspecialchars($pconfig['radns3']);?>" /><br /> + <input name="radns4" type="text" class="formfld unknown" id="radns4" size="28" value="<?=htmlspecialchars($pconfig['radns4']);?>" /><br /> + <?=gettext("NOTE: leave blank to use the system default DNS servers - this interface's IP if DNS Forwarder or Resolver is enabled, otherwise the servers configured on the General page.");?> + </td> + </tr> + + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Domain search list");?></td> + <td width="78%" class="vtable"> + <input name="radomainsearchlist" type="text" class="formfld unknown" id="radomainsearchlist" size="28" value="<?=htmlspecialchars($pconfig['radomainsearchlist']);?>" /><br /> + <?=gettext("The RA server can optionally provide a domain search list. Use the semicolon character as separator");?> + </td> + </tr> + + <tr> + <td width="22%" valign="top" class="vncell"> </td> + <td width="78%" class="vtable"> + <input id="rasamednsasdhcp6" name="rasamednsasdhcp6" type="checkbox" value="yes" <?php if ($pconfig['rasamednsasdhcp6']) { echo "checked='checked'"; } ?> /> + <strong><?= gettext("Use same settings as DHCPv6 server"); ?></strong> + </td> + </tr> + + <tr> + <td width="22%" valign="top"> </td> + <td width="78%"> + <input name="if" type="hidden" value="<?=$if;?>" /> + <input name="Submit" type="submit" class="formbtn" value="<?=gettext("Save");?>" /> + </td> + </tr> + </table> + </div> + </td> + </tr> +</table> +</form> + +<script type="text/javascript"> +//<![CDATA[ + jQuery(function ($) { + var $rasamednsasdhcp6 = $("#rasamednsasdhcp6"); + var $triggered_checkboxes = $("#radns1, #radns2, #radns3, #radns4, #radomainsearchlist"); + if ($rasamednsasdhcp6.length !== 1) { return; } + var onchange = function () { + var checked = $rasamednsasdhcp6.is(":checked"); + if (checked) { + $triggered_checkboxes.each(function () { this.disabled = true; }); + } else { + $triggered_checkboxes.each(function () { this.disabled = false; }); + } + }; + $rasamednsasdhcp6.bind("change", onchange); + onchange(); + }); + + var addressarray = <?= json_encode(get_alias_list("host", "network", "openvpn", "urltable")); ?>; + var objAlias = []; + function createAutoSuggest () { + <?php for ($i = 0; $i < $counter; $i += 1) { ?> + objAlias.push(new AutoSuggestControl(document.getElementById('subnet_address<?= $i ?>'), new StateSuggestions(addressarray))); + <?php } ?> + new AutoSuggestControl(document.getElementById('radns1'), new StateSuggestions(addressarray)); + new AutoSuggestControl(document.getElementById('radns2'), new StateSuggestions(addressarray)); + new AutoSuggestControl(document.getElementById('radns3'), new StateSuggestions(addressarray)); + new AutoSuggestControl(document.getElementById('radns4'), new StateSuggestions(addressarray)); + } + setTimeout(createAutoSuggest, 500); +//]]> +</script> + +<?php include("fend.inc"); ?> +</body> +</html> diff --git a/src/usr/local/www/services_snmp.php b/src/usr/local/www/services_snmp.php new file mode 100644 index 0000000..ec94af7 --- /dev/null +++ b/src/usr/local/www/services_snmp.php @@ -0,0 +1,366 @@ +<?php +/* $Id$ */ +/* + services_snmp.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + part of pfSense + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: snmp +*/ + +##|+PRIV +##|*IDENT=page-services-snmp +##|*NAME=Services: SNMP page +##|*DESCR=Allow access to the 'Services: SNMP' page. +##|*MATCH=services_snmp.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); + +if (!is_array($config['snmpd'])) { + $config['snmpd'] = array(); + $config['snmpd']['rocommunity'] = "public"; + $config['snmpd']['pollport'] = "161"; +} + +if (!is_array($config['snmpd']['modules'])) { + $config['snmpd']['modules'] = array(); + $config['snmpd']['modules']['mibii'] = true; + $config['snmpd']['modules']['netgraph'] = true; + $config['snmpd']['modules']['pf'] = true; + $config['snmpd']['modules']['hostres'] = true; + $config['snmpd']['modules']['bridge'] = true; + $config['snmpd']['modules']['ucd'] = true; + $config['snmpd']['modules']['regex'] = true; +} + +$pconfig['enable'] = isset($config['snmpd']['enable']); +$pconfig['pollport'] = $config['snmpd']['pollport']; +$pconfig['syslocation'] = $config['snmpd']['syslocation']; +$pconfig['syscontact'] = $config['snmpd']['syscontact']; +$pconfig['rocommunity'] = $config['snmpd']['rocommunity']; +/* disabled until some docs show up on what this does. +$pconfig['rwenable'] = isset($config['snmpd']['rwenable']); +$pconfig['rwcommunity'] = $config['snmpd']['rwcommunity']; +*/ +$pconfig['trapenable'] = isset($config['snmpd']['trapenable']); +$pconfig['trapserver'] = $config['snmpd']['trapserver']; +$pconfig['trapserverport'] = $config['snmpd']['trapserverport']; +$pconfig['trapstring'] = $config['snmpd']['trapstring']; + +$pconfig['mibii'] = isset($config['snmpd']['modules']['mibii']); +$pconfig['netgraph'] = isset($config['snmpd']['modules']['netgraph']); +$pconfig['pf'] = isset($config['snmpd']['modules']['pf']); +$pconfig['hostres'] = isset($config['snmpd']['modules']['hostres']); +$pconfig['bridge'] = isset($config['snmpd']['modules']['bridge']); +$pconfig['ucd'] = isset($config['snmpd']['modules']['ucd']); +$pconfig['regex'] = isset($config['snmpd']['modules']['regex']); +$pconfig['bindip'] = $config['snmpd']['bindip']; + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if ($_POST['enable']) { + if (strstr($_POST['syslocation'], "#")) { + $input_errors[] = gettext("Invalid character '#' in system location"); + } + if (strstr($_POST['syscontact'], "#")) { + $input_errors[] = gettext("Invalid character '#' in system contact"); + } + if (strstr($_POST['rocommunity'], "#")) { + $input_errors[] = gettext("Invalid character '#' in read community string"); + } + + $reqdfields = explode(" ", "rocommunity"); + $reqdfieldsn = array(gettext("Community")); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + $reqdfields = explode(" ", "pollport"); + $reqdfieldsn = array(gettext("Polling Port")); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + + } + + if ($_POST['trapenable']) { + if (strstr($_POST['trapstring'], "#")) { + $input_errors[] = gettext("Invalid character '#' in SNMP trap string"); + } + + $reqdfields = explode(" ", "trapserver"); + $reqdfieldsn = array(gettext("Trap server")); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + $reqdfields = explode(" ", "trapserverport"); + $reqdfieldsn = array(gettext("Trap server port")); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + $reqdfields = explode(" ", "trapstring"); + $reqdfieldsn = array(gettext("Trap string")); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + } + +/* disabled until some docs show up on what this does. + if ($_POST['rwenable']) { + $reqdfields = explode(" ", "rwcommunity"); + $reqdfieldsn = explode(",", "Write community string"); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + } +*/ + + + + if (!$input_errors) { + $config['snmpd']['enable'] = $_POST['enable'] ? true : false; + $config['snmpd']['pollport'] = $_POST['pollport']; + $config['snmpd']['syslocation'] = $_POST['syslocation']; + $config['snmpd']['syscontact'] = $_POST['syscontact']; + $config['snmpd']['rocommunity'] = $_POST['rocommunity']; + /* disabled until some docs show up on what this does. + $config['snmpd']['rwenable'] = $_POST['rwenable'] ? true : false; + $config['snmpd']['rwcommunity'] = $_POST['rwcommunity']; + */ + $config['snmpd']['trapenable'] = $_POST['trapenable'] ? true : false; + $config['snmpd']['trapserver'] = $_POST['trapserver']; + $config['snmpd']['trapserverport'] = $_POST['trapserverport']; + $config['snmpd']['trapstring'] = $_POST['trapstring']; + + $config['snmpd']['modules']['mibii'] = $_POST['mibii'] ? true : false; + $config['snmpd']['modules']['netgraph'] = $_POST['netgraph'] ? true : false; + $config['snmpd']['modules']['pf'] = $_POST['pf'] ? true : false; + $config['snmpd']['modules']['hostres'] = $_POST['hostres'] ? true : false; + $config['snmpd']['modules']['bridge'] = $_POST['bridge'] ? true : false; + $config['snmpd']['modules']['ucd'] = $_POST['ucd'] ? true : false; + $config['snmpd']['modules']['regex'] = $_POST['regex'] ? true : false; + $config['snmpd']['bindip'] = $_POST['bindip']; + + write_config(); + + $retval = 0; + $retval = services_snmpd_configure(); + $savemsg = get_std_save_message($retval); + } +} + +function build_iplist() { + $listenips = get_possible_listen_ips(); + $iplist = array(); + $iplist[''] = 'All'; + + foreach ($listenips as $lip => $ldescr) { + $iplist[$lip] = $ldescr; + } + unset($listenips); + + return($iplist); +} + +$closehead = false; +$pgtitle = array(gettext("Services"), gettext("SNMP")); +$shortcut_section = "snmp"; + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('SNMP Daemon'); + +$section->addInput(new Form_Checkbox( + 'enable', + 'Enable', + 'Enable the SNMP Daemon and its controls', + $pconfig['enable'] +)); + +$form->add($section); + +$section = new Form_Section('SNMP Daemon settings'); + +$section->addInput(new Form_Input( + 'pollport', + 'Polling Port', + 'text', + ($pconfig['pollport'] ? $pconfig['pollport']:'161') +))->setHelp('Enter the port to accept polling events on (default 161)'); + +$section->addInput(new Form_Input( + 'syslocation', + 'System Location', + 'text', + $pconfig['syslocation'] +)); + +$section->addInput(new Form_Input( + 'syscontact', + 'System Contact', + 'text', + $pconfig['syscontact'] +)); + +$section->addInput(new Form_Input( + 'rocommunity', + 'Read Community String', + 'text', + $pconfig['rocommunity'] +))->setHelp('The community string is like a password, restricting access to querying SNMP to hosts knowing the community string. Use a strong value here to protect from unauthorized information disclosure.'); + +$form->add($section); + +$section = new Form_Section('SNMP Traps Enable'); + +$section->addInput(new Form_Checkbox( + 'trapenable', + 'Enable', + 'Enable the SNMP Trap and its controls', + $pconfig['trapenable'] +))->toggles('.toggle-traps'); + +$form->add($section); + +$section = new Form_Section('SNMP Trap settings'); + +if($pconfig['trapenable']) + $section->addClass('toggle-traps', 'in'); +else + $section->addClass('toggle-traps', 'collapse'); + +$section->addInput(new Form_Input( + 'trapserver', + 'Trap server', + 'text', + $pconfig['trapserver'] +))->setHelp('Enter the trap server name'); + +$section->addInput(new Form_Input( + 'trapserverport', + 'Trap Server Port', + 'text', + ($pconfig['trapserverport'] ? $pconfig['trapserverport']:'162') +))->setHelp('Enter the port to send the traps to (default 162)'); + +$section->addInput(new Form_Input( + 'trapstring', + 'SNMP Trap String', + 'text', + $pconfig['trapstring'] +)); + +$form->add($section); + +$section = new Form_Section('SNMP Modules'); + +$group = new Form_MultiCheckboxGroup('SNMP modules'); + +$group->add(new Form_MultiCheckbox( + 'mibii', + null, + 'MibII', + $pconfig['mibii'] +)); + +$group->add(new Form_MultiCheckbox( + 'netgraph', + null, + 'Netgraph', + $pconfig['netgraph'] +)); + +$group->add(new Form_MultiCheckbox( + 'pf', + null, + 'PF', + $pconfig['pf'] +)); + +$group->add(new Form_MultiCheckbox( + 'hostres', + null, + 'Host Resources', + $pconfig['hostres'] +)); + +$group->add(new Form_MultiCheckbox( + 'ucd', + null, + 'UCD', + $pconfig['ucd'] +)); + +$group->add(new Form_MultiCheckbox( + 'regex', + null, + 'Regex', + $pconfig['regex'] +)); + +$section->add($group); +$form->add($section); + +$section = new Form_Section('Interface Binding'); + +$section->addInput(new Form_Select( + 'bindip', + 'Bind Interface', + $pconfig['bindip'], + build_iplist() +)); + +$form->add($section); + +print($form); +?> + +<script type="text/javascript"> +//<![CDATA[ + +// hostres requires mibii so we force that here +events.push(function(){ + $('#hostres').change(function(){ + if($('#hostres').is(':checked')) + $('#mibii').attr('checked', 'checked'); + }); +}); +//]]> +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_unbound.php b/src/usr/local/www/services_unbound.php new file mode 100644 index 0000000..57f72e5 --- /dev/null +++ b/src/usr/local/www/services_unbound.php @@ -0,0 +1,525 @@ +<?php +/* $Id$ */ +/* + services_unbound.php + part of the pfSense project (https://www.pfsense.org) + Copyright (C) 2014 Warren Baker (warren@pfsense.org) + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: dnsresolver +*/ + +##|+PRIV +##|*IDENT=page-services-unbound +##|*NAME=Services: DNS Resolver page +##|*DESCR=Allow access to the 'Services: DNS Resolver' page. +##|*MATCH=services_unbound.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("unbound.inc"); +require_once("system.inc"); + +if (!is_array($config['unbound'])) { + $config['unbound'] = array(); +} + +$a_unboundcfg =& $config['unbound']; + +if (!is_array($config['unbound']['hosts'])) { + $config['unbound']['hosts'] = array(); +} + +$a_hosts =& $config['unbound']['hosts']; + +if (!is_array($config['unbound']['domainoverrides'])) { + $config['unbound']['domainoverrides'] = array(); +} + +$a_domainOverrides = &$config['unbound']['domainoverrides']; + +if (isset($config['unbound']['enable'])) { + $pconfig['enable'] = true; +} +if (isset($config['unbound']['dnssec'])) { + $pconfig['dnssec'] = true; +} +if (isset($config['unbound']['forwarding'])) { + $pconfig['forwarding'] = true; +} +if (isset($config['unbound']['regdhcp'])) { + $pconfig['regdhcp'] = true; +} +if (isset($config['unbound']['regdhcpstatic'])) { + $pconfig['regdhcpstatic'] = true; +} +if (isset($config['unbound']['txtsupport'])) { + $pconfig['txtsupport'] = true; +} + +$pconfig['port'] = $config['unbound']['port']; +$pconfig['custom_options'] = base64_decode($config['unbound']['custom_options']); + +if (empty($config['unbound']['active_interface'])) { + $pconfig['active_interface'] = array(); +} else { + $pconfig['active_interface'] = explode(",", $config['unbound']['active_interface']); +} + +if (empty($config['unbound']['outgoing_interface'])) { + $pconfig['outgoing_interface'] = array(); +} else { + $pconfig['outgoing_interface'] = explode(",", $config['unbound']['outgoing_interface']); +} + +if ($_POST) { + $pconfig = $_POST; + unset($input_errors); + + if ($_POST['apply']) { + $retval = services_unbound_configure(); + $savemsg = get_std_save_message($retval); + if ($retval == 0) { + clear_subsystem_dirty('unbound'); + } + /* Update resolv.conf in case the interface bindings exclude localhost. */ + system_resolvconf_generate(); + /* Start or restart dhcpleases when it's necessary */ + system_dhcpleases_configure(); + } else { + if (isset($_POST['enable']) && isset($config['dnsmasq']['enable'])) { + if ($_POST['port'] == $config['dnsmasq']['port']) { + $input_errors[] = "The DNS Forwarder is enabled using this port. Choose a non-conflicting port, or disable the DNS Forwarder."; + } + } + + if (empty($_POST['active_interface'])) { + $input_errors[] = "One or more Network Interfaces must be selected for binding."; + } else if (!isset($config['system']['dnslocalhost']) && (!in_array("lo0", $_POST['active_interface']) && !in_array("all", $_POST['active_interface']))) { + $input_errors[] = "This system is configured to use the DNS Resolver as its DNS server, so Localhost or All must be selected in Network Interfaces."; + } + + if (empty($_POST['outgoing_interface'])) { + $input_errors[] = "One or more Outgoing Network Interfaces must be selected."; + } + + if ($_POST['port']) { + if (is_port($_POST['port'])) { + $a_unboundcfg['port'] = $_POST['port']; + } else { + $input_errors[] = gettext("You must specify a valid port number."); + } + } else if (isset($config['unbound']['port'])) { + unset($config['unbound']['port']); + } + + if (isset($_POST['enable'])) { + $a_unboundcfg['enable'] = true; + } else { + unset($a_unboundcfg['enable']); + } + if (isset($_POST['dnssec'])) { + $a_unboundcfg['dnssec'] = true; + } else { + unset($a_unboundcfg['dnssec']); + } + if (isset($_POST['forwarding'])) { + $a_unboundcfg['forwarding'] = true; + } else { + unset($a_unboundcfg['forwarding']); + } + if (isset($_POST['regdhcp'])) { + $a_unboundcfg['regdhcp'] = true; + } else { + unset($a_unboundcfg['regdhcp']); + } + if (isset($_POST['regdhcpstatic'])) { + $a_unboundcfg['regdhcpstatic'] = true; + } else { + unset($a_unboundcfg['regdhcpstatic']); + } + if (isset($_POST['txtsupport'])) { + $a_unboundcfg['txtsupport'] = true; + } else { + unset($a_unboundcfg['txtsupport']); + } + if (is_array($_POST['active_interface']) && !empty($_POST['active_interface'])) { + $a_unboundcfg['active_interface'] = implode(",", $_POST['active_interface']); + } + + if (is_array($_POST['outgoing_interface']) && !empty($_POST['outgoing_interface'])) { + $a_unboundcfg['outgoing_interface'] = implode(",", $_POST['outgoing_interface']); + } + + $a_unboundcfg['custom_options'] = base64_encode(str_replace("\r\n", "\n", $_POST['custom_options'])); + + if (!$input_errors) { + write_config("DNS Resolver configured."); + mark_subsystem_dirty('unbound'); + } + } +} + +if ($_GET['act'] == "del") { + if ($_GET['type'] == 'host') { + if ($a_hosts[$_GET['id']]) { + unset($a_hosts[$_GET['id']]); + write_config(); + mark_subsystem_dirty('unbound'); + header("Location: services_unbound.php"); + exit; + } + } elseif ($_GET['type'] == 'doverride') { + if ($a_domainOverrides[$_GET['id']]) { + unset($a_domainOverrides[$_GET['id']]); + write_config(); + mark_subsystem_dirty('unbound'); + header("Location: services_unbound.php"); + exit; + } + } +} + +function build_if_list() { + $interface_addresses = get_possible_listen_ips(true); + $iflist = array('options' => array(), 'selected' => array()); + + $iflist['options']['all'] = "All"; + if (empty($pconfig['interface']) || empty($pconfig['interface'][0])) + array_push($iflist['selected'], "all"); + + foreach ($interface_addresses as $laddr => $ldescr) { + $iflist['options'][$laddr] = htmlspecialchars($ldescr); + + if ($pconfig['interface'] && in_array($laddr, $pconfig['interface'])) + array_push($iflist['selected'], $laddr); + } + + unset($interface_addresses); + + return($iflist); +} + +$closehead = false; +$pgtitle = array(gettext("Services"), gettext("DNS Resolver")); +$shortcut_section = "resolver"; + +include_once("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +$tab_array = array(); +$tab_array[] = array(gettext("General settings"), true, "services_unbound.php"); +$tab_array[] = array(gettext("Advanced settings"), false, "services_unbound_advanced.php"); +$tab_array[] = array(gettext("Access Lists"), false, "/services_unbound_acls.php"); +display_top_tabs($tab_array, true); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('General DNS Resolver Options'); + +$section->addInput(new Form_Checkbox( + 'enable', + 'Enable', + 'Enable DNS resolver', + $pconfig['enable'] +)); + +$section->addInput(new Form_Input( + 'port', + 'Listen Port', + 'text', + $pconfig['port'] +))->setHelp('The port used for responding to DNS queries. It should normally be left blank unless another service needs to bind to TCP/UDP port 53.'); + +$iflist = build_if_list(); + +$section->addInput(new Form_Select( + 'active_interface', + 'Network Interfaces', + $iflist['selected'], + $iflist['options'], + true +))->setHelp('Interface IPs used by the DNS Resolver for responding to queries from clients. If an interface has both IPv4 and IPv6 IPs, both are used. Queries to other interface IPs not selected below are discarded. ' . + 'The default behavior is to respond to queries on every available IPv4 and IPv6 address.'); + +$section->addInput(new Form_Select( + 'outgoing_interface', + 'Outgoing Network Interfaces', + $iflist['selected'], + $iflist['options'], + true +))->setHelp('Utilize different network interface(s) that the DNS Resolver will use to send queries to authoritative servers and receive their replies. By default all interfaces are used.'); + +$section->addInput(new Form_Checkbox( + 'dnssec', + 'DNSSEC', + 'Enable DNSSEC Support', + $pconfig['dnssec'] +)); + +$section->addInput(new Form_Checkbox( + 'forwarding', + 'DNS Query Forwarding', + 'Enable Forwarding Mode', + $pconfig['forwarding'] +)); + +$section->addInput(new Form_Checkbox( + 'regdhcp', + 'DHCP Registration', + 'Register DHCP leases in the DNS Resolver', + $pconfig['regdhcp'] +))->setHelp(sprintf('If this option is set, then machines that specify their hostname when requesting a DHCP lease will be registered'. + ' in the DNS Resolver, so that their name can be resolved.'. + ' You should also set the domain in %sSystem: General setup%s to the proper value.','<a href="system.php">','</a>')); + +$section->addInput(new Form_Checkbox( + 'regdhcpstatic', + 'Static DHCP', + 'Register DHCP static mappings in the DNS Resolver', + $pconfig['regdhcpstatic'] +))->setHelp(sprintf('If this option is set, then DHCP static mappings will be registered in the DNS Resolver, so that their name can be '. + 'resolved. You should also set the domain in %s'. + 'System: General setup%s to the proper value.','<a href="system.php">','</a>')); + +$section->addInput(new Form_Checkbox( + 'txtsupport', + 'TXT Comment Support', + 'Register DHCP static mappings in the DNS Resolver', + $pconfig['txtsupport'] +))->setHelp('Any descriptions associated with Host entries and DHCP Static mappings will create a corresponding TXT record.'); + +$btnadvdns = new Form_Button( + 'btnadvdns', + 'Advanced' +); + +$btnadvdns->removeClass('btn-primary')->addClass('btn-default btn-sm'); + +$section->addInput(new Form_StaticText( + 'Advanced', + $btnadvdns . ' ' . 'Show advanced optionss' +)); + +$section->addInput(new Form_TextArea ( + 'custom_options', + 'Custom options', + $pconfig['custom_options'] +))->setHelp('Enter any additional configuration parameters to add to the DNS Resolver configuration here, separated by a newline'); + +$form->add($section); +print($form); + +print_info_box(sprintf(gettext("If the DNS Resolver is enabled, the DHCP". +" service (if enabled) will automatically serve the LAN IP". +" address as a DNS server to DHCP clients so they will use". +" the DNS Resolver. If Forwarding, is enabled, the DNS Resolver will use the DNS servers". +" entered in %sSystem: General setup%s". +" or those obtained via DHCP or PPP on WAN if the "Allow". +" DNS server list to be overridden by DHCP/PPP on WAN"". +" is checked."),'<a href="system.php">','</a>')); +?> + +<script> +//<![CDATA[ +events.push(function(){ + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // If hte enable checkbox is not checked, disable the next three checkboxes + function disableDHCP() { + var hide = ! $('#enable').prop('checked'); + + disableInput('port', hide); + disableInput('active_interface', hide); + disableInput('outgoing_interface', hide); + disableInput('regdhcpstatic', hide); + disableInput('dnssec', hide); + disableInput('forwarding', hide); + disableInput('regdhcp', hide); + disableInput('regdhcpstatic', hide); + disableInput('txtsupport', hide); + disableInput('btnadvdns', hide); + } + + // Make the ‘aditional options’ button a plain button, not a submit button + $("#btnadvdns").prop('type','button'); + + // Un-hide aditional controls + $("#btnadvdns").click(function() { + hideInput('custom_options', false); + + }); + + // When 'enable' is clicked, diable/enable the following three checkboxes + $('#enable').click(function() { + disableDHCP(); + }); + + // On initial load + hideInput('custom_options', true); + disableDHCP(); + +}); +//]]> +</script> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Host Overrides")?></h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Host")?></th> + <th><?=gettext("Domain")?></th> + <th><?=gettext("IP")?></th> + <th><?=gettext("Description")?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php +$i = 0; +foreach ($a_hosts as $hostent): +?> + <tr> + <td> + <?=strtolower($hostent['host'])?> + </td> + <td> + <?=strtolower($hostent['domain'])?> + </td> + <td> + <?=$hostent['ip']?> + </td> + <td> + <?=htmlspecialchars($hostent['descr'])?> + </td> + <td> + <a href="services_dnsmasq_edit.php?id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext('Edit')?></a> + <a href="services_dnsmasq.php?type=host&act=del&id=<?=$i?>" class="btn btn-xs btn-danger"><?=gettext('Delete')?></a> + </td> + </tr> + +<?php + if ($hostent['aliases']['item'] && is_array($hostent['aliases']['item'])): + foreach ($hostent['aliases']['item'] as $alias): +?> + <tr> + <td> + <?=strtolower($alias['host'])?> + </td> + <td> + <?=strtolower($alias['domain'])?> + </td> + <td> + Alias for <?=$hostent['host'] ? $hostent['host'] . '.' . $hostent['domain'] : $hostent['domain']?> + </td> + <td> + <?=htmlspecialchars($alias['description'])?> + </td> + <td> + <a href="services_dnsmasq_edit.php?id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext('Edit')?></a> + </td> + </tr> +<?php + endforeach; + endif; + $i++; +endforeach; +?> + </tbody> + </table> + </div> +</div> + +<nav class="action-buttons"> + <a href="services_dnsmasq_edit.php" class="btn btn-sm btn-success"><?=gettext('Add')?></a> +</nav> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Domain Overrides")?></h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Domain")?></th> + <th><?=gettext("IP")?></th> + <th><?=gettext("Description")?></th> + <th></th> + </tr> + </thead> + + <tbody> +<?php +$i = 0; +foreach ($a_domainOverrides as $doment): +?> + <tr> + <td> + <?=strtolower($doment['domain'])?> + </td> + <td> + <?=$doment['ip']?> + </td> + <td> + <?=htmlspecialchars($doment['descr'])?> + </td> + <td> + <a href="services_dnsmasq_domainoverride_edit.php?id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext('Edit')?></a> + <a href="services_dnsmasq.php?act=del&type=doverride&id=<?=$i?>" class="btn btn-xs btn-danger"><?=gettext('Delete')?></a> + </td> + </tr> +<?php + $i++; +endforeach; +?> + </tbody> + </table> + </div> +</div> + +<nav class="action-buttons"> + <a href="services_dnsmasq_domainoverride_edit.php" class="btn btn-sm btn-success"><?=gettext('Add')?></a> +</nav> +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_unbound_acls.php b/src/usr/local/www/services_unbound_acls.php new file mode 100644 index 0000000..c0ca3b8 --- /dev/null +++ b/src/usr/local/www/services_unbound_acls.php @@ -0,0 +1,342 @@ +<?php +/* $Id$ */ +/* + services_unbound_acls.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2011 Warren Baker <warren@decoy.co.za> + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +require("guiconfig.inc"); +require("unbound.inc"); + +if (!is_array($config['unbound']['acls'])) { + $config['unbound']['acls'] = array(); +} + +$a_acls = &$config['unbound']['acls']; + +$id = $_GET['id']; + +if (isset($_POST['aclid'])) { + $id = $_POST['aclid']; +} + +if (!empty($id) && !is_numeric($id)) { + pfSenseHeader("services_unbound_acls.php"); + exit; +} + +$act = $_GET['act']; + +if (isset($_POST['act'])) { + $act = $_POST['act']; +} + +if ($act == "del") { + if (!$a_acls[$id]) { + pfSenseHeader("services_unbound_acls.php"); + exit; + } + + unset($a_acls[$id]); + write_config(); + mark_subsystem_dirty('unbound'); +} + +if ($act == "new") { + $id = unbound_get_next_id(); +} + +if ($act == "edit") { + if (isset($id) && $a_acls[$id]) { + $pconfig = $a_acls[$id]; + $networkacl = $a_acls[$id]['row']; + } +} + +// Add a row to the networks table +if($_GET && $_GET['addrow']) + array_push($networkacl, array('acl_network' => '', 'mask' => '32', 'description' => '')); + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + $deleting = false; + + // Delete a row from the networks table + for($idx = 0; $idx<50; $idx++) { + if($pconfig['dlt' . $idx] == 'Delete') { + unset($networkacl[$idx]); + $deleting = true; + break; + } + } + + if ($_POST['apply']) { + $retval = services_unbound_configure(); + $savemsg = get_std_save_message($retval); + if ($retval == 0) + clear_subsystem_dirty('unbound'); + } else if(!$deleting) { + + // input validation - only allow 50 entries in a single ACL + for ($x = 0; $x < 50; $x++) { + if (isset($pconfig["acl_network{$x}"])) { + $networkacl[$x] = array(); + $networkacl[$x]['acl_network'] = $pconfig["acl_network{$x}"]; + $networkacl[$x]['mask'] = $pconfig["mask{$x}"]; + $networkacl[$x]['description'] = $pconfig["description{$x}"]; + if (!is_ipaddr($networkacl[$x]['acl_network'])) { + $input_errors[] = gettext("You must enter a valid IP address for each row under Networks."); + } + + if (is_ipaddr($networkacl[$x]['acl_network'])) { + if (!is_subnet($networkacl[$x]['acl_network']."/".$networkacl[$x]['mask'])) { + $input_errors[] = gettext("You must enter a valid IPv4 netmask for each IPv4 row under Networks."); + } + } else if (function_exists("is_ipaddrv6")) { + if (!is_ipaddrv6($networkacl[$x]['acl_network'])) { + $input_errors[] = gettext("You must enter a valid IPv6 address for {$networkacl[$x]['acl_network']}."); + } else if (!is_subnetv6($networkacl[$x]['acl_network']."/".$networkacl[$x]['mask'])) { + $input_errors[] = gettext("You must enter a valid IPv6 netmask for each IPv6 row under Networks."); + } + } else { + $input_errors[] = gettext("You must enter a valid IP address for each row under Networks."); + } + } else if (isset($networkacl[$x])) { + unset($networkacl[$x]); + } + } + + if (!$input_errors) { + if ($pconfig['Submit'] == gettext("Save")) { + $acl_entry = array(); + $acl_entry['aclid'] = $pconfig['aclid']; + $acl_entry['aclname'] = $pconfig['aclname']; + $acl_entry['aclaction'] = $pconfig['aclaction']; + $acl_entry['description'] = $pconfig['description']; + $acl_entry['aclid'] = $pconfig['aclid']; + $acl_entry['row'] = array(); + foreach ($networkacl as $acl) { + $acl_entry['row'][] = $acl; + } + + if (isset($id) && $a_acls[$id]) { + $a_acls[$id] = $acl_entry; + } else { + $a_acls[] = $acl_entry; + } + + mark_subsystem_dirty("unbound"); + write_config(); + + pfSenseHeader("/services_unbound_acls.php"); + exit; + } + } + } +} + +$closehead = false; +$pgtitle = "Services: DNS Resolver: Access Lists"; +$shortcut_section = "resolver"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('unbound')) + print_info_box_np(gettext("The configuration of the DNS Resolver, has been changed") . ".<br />" . gettext("You must apply the changes in order for them to take effect.")); + +$tab_array = array(); +$tab_array[] = array(gettext("General Settings"), false, "/services_unbound.php"); +$tab_array[] = array(gettext("Advanced settings"), false, "services_unbound_advanced.php"); +$tab_array[] = array(gettext("Access Lists"), true, "/services_unbound_acls.php"); +display_top_tabs($tab_array, true); + +require('classes/Form.class.php'); + +if($act=="new" || $act=="edit") { + + $form = new Form(); + + $section = new Form_Section('New Access List'); + + $section->addInput(new Form_Input( + 'aclid', + null, + 'hidden', + $id + )); + + $section->addInput(new Form_Input( + 'act', + null, + 'hidden', + $act + )); + + $section->addInput(new Form_Input( + 'aclname', + 'Access LIst name', + 'text', + $pconfig['aclname'] + ))->setHelp('Provide an Access List name.'); + + $section->addInput(new Form_Select( + 'action', + 'Action', + strtolower($pconfig['aclaction']), + array('allow' => 'Allow','deny' => 'Deny','refuse' => 'Refuse','allow snoop' => 'Allow Snoop') + ))->setHelp('Choose what to do with DNS requests that match the criteria specified below.' . '<br />' . + 'Deny: Stops queries from hosts within the netblock defined below.' . '<br />' . + 'Refuse: Stops queries from hosts within the netblock defined below, but sends a DNS rcode REFUSED error message back to the client.' . '<br />' . + 'Allow: Allow queries from hosts within the netblock defined below.' . '<br />' . + 'Allow Snoop: Allow recursive and nonrecursive access from hosts within the netblock defined below. Used for cache snooping and ideally should only be configured for your administrative host.'); + + $counter = 0; + $numrows = count($networkacl) - 1; + + foreach($networkacl as $item) { + $network = $item['acl_network']; + $cidr = $item['mask']; + $description = $item['description']; + + $group = new Form_Group($counter == 0 ? 'Networks':null); + + $group->add(new Form_IpAddress( + 'acl_network' . $counter, + null, + $network + ))->setHelp(($counter == $numrows) ? 'Network':null); + + $group->add(new Form_Select( + 'mask' . $counter, + null, + $cidr, + array_combine(range(32, 1, -1), range(32, 1, -1)) + ))->setWidth(2)->setHelp(($counter == $numrows) ? 'Mask':null); + + $group->add(new Form_Input( + 'description' . $counter, + null, + 'text', + $description + ))->setWidth(3)->setHelp(($counter == $numrows) ? 'Description':null); + + $btndlt = new Form_Button( + 'dlt' . $counter, + 'Delete' + ); + + $btndlt->removeClass('btn-primary')->addClass('btn-sm btn-danger'); + + $group->add($btndlt); + + $section->add($group); + + $counter++; + } + + $btnadd = new Form_Button( + 'btnadd', + 'Add row', + 'services_unbound_acls.php?act=' . $act . '&addrow=yes' + ); + + $btnadd->removeClass(btn-primary)->addClass('btn-sm btn-success'); + + $section->addInput(new Form_StaticText( + 'Add row', + $btnadd + ))->setHelp('Remember to save after each Add or Delete'); + + $section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] + ))->setHelp('You may enter a description here for your reference.'); + + $form->add($section); + print($form); +} +else // NOT 'edit' or 'add' +{ +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Access Lists to control access to the DNS Resolver')?></h2></div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Access List Name"); ?></th> + <th><?=gettext("Action"); ?></th> + <th><?=gettext("Description"); ?></th> + <th> </th> + </tr> + </thead> + <tbody> +<?php + $i = 0; + foreach($a_acls as $acl): +?> + <tr ondblclick="document.location='services_unbound_acls.php?act=edit&id=<?=$i?>'"> + <td> + <?=htmlspecialchars($acl['aclname'])?> + </td> + <td> + <?=htmlspecialchars($acl['aclaction'])?> + </td> + <td> + <?=htmlspecialchars($acl['description'])?> + </td> + <td> + <a href="services_unbound_acls.php?act=edit&id=<?=$i?>" class="btn btn-xs btn-info" >Edit</a> + <a href="services_unbound_acls.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger">Delete</a> + </td> + </tr> +<?php + $i++; + endforeach; +?> + </tbody> + </table> + </div> + <nav class="action-buttons"> + <a href="services_unbound_acls.php?act=new" class="btn btn-sm btn-success">Add</a> + </nav> + </div> +</div> +<?php +} + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_unbound_advanced.php b/src/usr/local/www/services_unbound_advanced.php new file mode 100644 index 0000000..26a9732 --- /dev/null +++ b/src/usr/local/www/services_unbound_advanced.php @@ -0,0 +1,387 @@ +<?php +/* $Id$ */ +/* + services_unbound_advanced.php +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 22015 Warren Baker (warren@percol8.co.za) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ +/* + pfSense_MODULE: dnsresolver +*/ + +##|+PRIV +##|*IDENT=page-services-unbound +##|*NAME=Services: DNS Resolver Advanced page +##|*DESCR=Allow access to the 'Services: DNS Resolver Advanced' page. +##|*MATCH=services_unbound.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("unbound.inc"); + +if (!is_array($config['unbound'])) { + $config['unbound'] = array(); +} + +if (isset($config['unbound']['hideidentity'])) { + $pconfig['hideidentity'] = true; +} + +if (isset($config['unbound']['hideversion'])) { + $pconfig['hideversion'] = true; +} + +if (isset($config['unbound']['prefetch'])) { + $pconfig['prefetch'] = true; +} + +if (isset($config['unbound']['prefetchkey'])) { + $pconfig['prefetchkey'] = true; +} + +if (isset($config['unbound']['dnssecstripped'])) { + $pconfig['dnssecstripped'] = true; +} + +$pconfig['msgcachesize'] = $config['unbound']['msgcachesize']; +$pconfig['outgoing_num_tcp'] = isset($config['unbound']['outgoing_num_tcp']) ? $config['unbound']['outgoing_num_tcp'] : '10'; +$pconfig['incoming_num_tcp'] = isset($config['unbound']['incoming_num_tcp']) ? $config['unbound']['incoming_num_tcp'] : '10'; +$pconfig['edns_buffer_size'] = isset($config['unbound']['edns_buffer_size']) ? $config['unbound']['edns_buffer_size'] : '4096'; +$pconfig['num_queries_per_thread'] = $config['unbound']['num_queries_per_thread']; +$pconfig['jostle_timeout'] = isset($config['unbound']['jostle_timeout']) ? $config['unbound']['jostle_timeout'] : '200'; +$pconfig['cache_max_ttl'] = isset($config['unbound']['cache_max_ttl']) ? $config['unbound']['cache_max_ttl'] : '86400'; +$pconfig['cache_min_ttl'] = isset($config['unbound']['cache_min_ttl']) ? $config['unbound']['cache_min_ttl'] : '0'; +$pconfig['infra_host_ttl'] = isset($config['unbound']['infra_host_ttl']) ? $config['unbound']['infra_host_ttl'] : '900'; +$pconfig['infra_cache_numhosts'] = isset($config['unbound']['infra_cache_numhosts']) ? $config['unbound']['infra_cache_numhosts'] : '10000'; +$pconfig['unwanted_reply_threshold'] = isset($config['unbound']['unwanted_reply_threshold']) ? $config['unbound']['unwanted_reply_threshold'] : 'disabled'; +$pconfig['log_verbosity'] = isset($config['unbound']['log_verbosity']) ? $config['unbound']['log_verbosity'] : "1"; + +if (isset($config['unbound']['disable_auto_added_access_control'])) { + $pconfig['disable_auto_added_access_control'] = true; +} + +if (isset($config['unbound']['use_caps'])) { + $pconfig['use_caps'] = true; +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + if ($_POST['apply']) { + $retval = services_unbound_configure(); + $savemsg = get_std_save_message($retval); + if ($retval == 0) { + clear_subsystem_dirty('unbound'); + } + } else { + if (isset($_POST['msgcachesize']) && !in_array($_POST['msgcachesize'], array('4', '10', '20', '50', '100', '250', '512'), true)) { + $input_errors[] = "A valid value for Message Cache Size must be specified."; + } + if (isset($_POST['outgoing_num_tcp']) && !in_array($_POST['outgoing_num_tcp'], array('0', '10', '20', '30', '40', '50'), true)) { + $input_errors[] = "A valid value must be specified for Outgoing TCP Buffers."; + } + if (isset($_POST['outgoing_num_tcp']) && !in_array($_POST['incoming_num_tcp'], array('0', '10', '20', '30', '40', '50'), true)) { + $input_errors[] = "A valid value must be specified for Incoming TCP Buffers."; + } + if (isset($_POST['edns_buffer_size']) && !in_array($_POST['edns_buffer_size'], array('512', '1480', '4096'), true)) { + $input_errors[] = "A valid value must be specified for EDNS Buffer Size."; + } + if (isset($_POST['num_queries_per_thread']) && !in_array($_POST['num_queries_per_thread'], array('512', '1024', '2048'), true)) { + $input_errors[] = "A valid value must be specified for Number of queries per thread."; + } + if (isset($_POST['jostle_timeout']) && !in_array($_POST['jostle_timeout'], array('100', '200', '500', '1000'), true)) { + $input_errors[] = "A valid value must be specified for Jostle Timeout."; + } + if (isset($_POST['cache_max_ttl']) && (!is_numericint($_POST['cache_max_ttl']) || ($_POST['cache_max_ttl'] < 0))) { + $input_errors[] = "'Maximum TTL for RRsets and messages' must be a positive integer."; + } + if (isset($_POST['cache_min_ttl']) && (!is_numericint($_POST['cache_min_ttl']) || ($_POST['cache_min_ttl'] < 0))) { + $input_errors[] = "'Minimum TTL for RRsets and messages' must be a positive integer."; + } + if (isset($_POST['infra_host_ttl']) && !in_array($_POST['infra_host_ttl'], array('60', '120', '300', '600', '900'), true)) { + $input_errors[] = "A valid value must be specified for TTL for Host cache entries."; + } + if (isset($_POST['infra_cache_numhosts']) && !in_array($_POST['infra_cache_numhosts'], array('1000', '5000', '10000', '20000', '50000'), true)) { + $input_errors[] = "A valid value must be specified for Number of Hosts to cache."; + } + if (isset($_POST['unwanted_reply_threshold']) && !in_array($_POST['unwanted_reply_threshold'], array('disabled', '5000000', '10000000', '20000000', '40000000', '50000000'), true)) { + $input_errors[] = "A valid value must be specified for Unwanted Reply Threshold."; + } + if (isset($_POST['log_verbosity']) && !in_array($_POST['log_verbosity'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for Log level verbosity."; + } + if (isset($_POST['dnssecstripped']) && !isset($config['unbound']['dnssec'])) { + $input_errors[] = "Harden DNSSEC Data option can only be enabled if DNSSEC support is enabled."; + } + + if (!$input_errors) { + if (isset($_POST['hideidentity'])) { + $config['unbound']['hideidentity'] = true; + } else { + unset($config['unbound']['hideidentity']); + } + if (isset($_POST['hideversion'])) { + $config['unbound']['hideversion'] = true; + } else { + unset($config['unbound']['hideversion']); + } + if (isset($_POST['prefetch'])) { + $config['unbound']['prefetch'] = true; + } else { + unset($config['unbound']['prefetch']); + } + if (isset($_POST['prefetchkey'])) { + $config['unbound']['prefetchkey'] = true; + } else { + unset($config['unbound']['prefetchkey']); + } + if (isset($_POST['dnssecstripped'])) { + $config['unbound']['dnssecstripped'] = true; + } else { + unset($config['unbound']['dnssecstripped']); + } + $config['unbound']['msgcachesize'] = $_POST['msgcachesize']; + $config['unbound']['outgoing_num_tcp'] = $_POST['outgoing_num_tcp']; + $config['unbound']['incoming_num_tcp'] = $_POST['incoming_num_tcp']; + $config['unbound']['edns_buffer_size'] = $_POST['edns_buffer_size']; + $config['unbound']['num_queries_per_thread'] = $_POST['num_queries_per_thread']; + $config['unbound']['jostle_timeout'] = $_POST['jostle_timeout']; + $config['unbound']['cache_max_ttl'] = $_POST['cache_max_ttl']; + $config['unbound']['cache_min_ttl'] = $_POST['cache_min_ttl']; + $config['unbound']['infra_host_ttl'] = $_POST['infra_host_ttl']; + $config['unbound']['infra_cache_numhosts'] = $_POST['infra_cache_numhosts']; + $config['unbound']['unwanted_reply_threshold'] = $_POST['unwanted_reply_threshold']; + $config['unbound']['log_verbosity'] = $_POST['log_verbosity']; + + if (isset($_POST['disable_auto_added_access_control'])) { + $config['unbound']['disable_auto_added_access_control'] = true; + } else { + unset($config['unbound']['disable_auto_added_access_control']); + } + + if (isset($_POST['use_caps'])) { + $config['unbound']['use_caps'] = true; + } else { + unset($config['unbound']['use_caps']); + } + + write_config("DNS Resolver configured."); + + mark_subsystem_dirty('unbound'); + } + } +} + +$closehead = false; +$pgtitle = array(gettext("Services"), gettext("DNS Resolver"), gettext("Advanced")); +$shortcut_section = "resolver"; +include_once("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("General settings"), false, "services_unbound.php"); +$tab_array[] = array(gettext("Advanced settings"), true, "services_unbound_advanced.php"); +$tab_array[] = array(gettext("Access Lists"), false, "/services_unbound_acls.php"); +display_top_tabs($tab_array, true); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Advanced Resolver Options'); + +$section->addInput(new Form_Checkbox( + 'hideidentity', + 'Hide identity', + 'id.server and hostname.bind queries are refused', + $pconfig['hideidentity'] +)); + +$section->addInput(new Form_Checkbox( + 'hideversion', + 'Hide version', + 'version.server and version.bind queries are refused', + $pconfig['hideversion'] +)); + +$section->addInput(new Form_Checkbox( + 'prefetch', + 'Prefetch support', + 'Message cache elements are prefetched before they expire to help keep the cache up to date', + $pconfig['prefetch'] +))->setHelp('When enabled, this option can cause an increase of around 10% more DNS traffic and load on the server, but frequently requested items will not expire from the cache'); + +$section->addInput(new Form_Checkbox( + 'prefetchkey', + 'Prefetch DNS Key Support', + 'DNSKEYs are fetched earlier in the validation process when a Delegation signer is encountered', + $pconfig['prefetchkey'] +))->setHelp('This helps lower the latency of requests but does utilize a little more CPU. See: <a href="http://en.wikipedia.org/wiki/List_of_DNS_record_types">Wikipedia</a>'); + +$section->addInput(new Form_Checkbox( + 'dnssecstripped', + 'Harden DNSSEC data', + 'DNSSEC data is required for trust-anchored zones.', + $pconfig['dnssecstripped'] +))->setHelp('If such data is absent, the zone becomes bogus. If Disabled and no DNSSEC data is received, then the zone is made insecure. '); + +$section->addInput(new Form_Select( + 'msgcachesize', + 'Message Cache size', + $pconfig['msgcachesize'], + array_combine(array("4", "10", "20", "50", "100", "250", "512"), array("4 MB", "10 MB", "20 MB", "50 MB", "100 MB", "250 MB", "512 MB")) +))->setHelp('Size of the message cache. The message cache stores DNS rcodes and validation statuses. The RRSet cache will automatically be set to twice this amount. The RRSet cache contains the actual RR data. The default is 4 megabytes.'); + +$section->addInput(new Form_Select( + 'outgoing_num_tcp', + 'Outgoing TCP Buffers', + $pconfig['outgoing_num_tcp'], + array_combine(array("0", "10", "20", "30", "50", "50"), array("0", "10", "20", "30", "50", "50")) +))->setHelp('The number of outgoing TCP buffers to allocate per thread. The default value is 10. If 0 is selected then no TCP queries, to authoritative servers, are done.'); + +$section->addInput(new Form_Select( + 'incoming_num_tcp', + 'Incoming TCP Buffers', + $pconfig['incoming_num_tcp'], + array_combine(array("0", "10", "20", "30", "50", "50"), array("0", "10", "20", "30", "50", "50")) +))->setHelp('The number of outgoing TCP buffers to allocate per thread. The default value is 10. If 0 is selected then no TCP queries, to authoritative servers, are done.'); + +$section->addInput(new Form_Select( + 'edns_buffer_size', + 'EDNS Buffer size', + $pconfig['edns_buffer_size'], + array_combine(array("512", "1480", "4096"), array("512", "1480", "4096")) +))->setHelp('Number of bytes size to advertise as the EDNS reassembly buffer size. This is the value that is used in UDP datagrams sent to peers. ' . + 'RFC recommendation is 4096 (which is the default). If you have fragmentation reassemble problems, usually seen as timeouts, then a value of 1480 should help. ' . + 'The 512 value bypasses most MTU path problems, but it can generate an excessive amount of TCP fallback.'); + +$section->addInput(new Form_Select( + 'num_queries_per_thread', + 'Number of queries per thread', + $pconfig['num_queries_per_thread'], + array_combine(array("512", "1024", "2048"), array("512", "1024", "2048")) +))->setHelp('The number of queries that every thread will service simultaneously. If more queries arrive that need to be serviced, and no queries can be jostled, then these queries are dropped'); + +$section->addInput(new Form_Select( + 'jostle_timeout', + 'Jostle Timeout', + $pconfig['jostle_timeout'], + array_combine(array("100", "200", "500", "1000"), array("100", "200", "500", "1000")) +))->setHelp('This timeout is used for when the server is very busy. This protects against denial of service by slow queries or high query rates. The default value is 200 milliseconds. '); + +$section->addInput(new Form_Input( + 'cache_max_ttl', + 'Maximum TTL for RRsets and messages', + 'text', + $pconfig['cache_max_ttl'] +))->setHelp('Configure a maximum Time to live for RRsets and messages in the cache. The default is 86400 seconds (1 day). ' . + 'When the internal TTL expires the cache item is expired. This can be configured to force the resolver to query for data more often and not trust (very large) TTL values'); + +$section->addInput(new Form_Input( + 'cache_min_ttl', + 'Minimum TTL for RRsets and messages', + 'text', + $pconfig['cache_min_ttl'] +))->setHelp('Configure a minimum Time to live for RRsets and messages in the cache. ' . + 'The default is 0 seconds. If the minimum value kicks in, the data is cached for longer than the domain owner intended, and thus less queries are made to look up the data. ' . + 'The 0 value ensures the data in the cache is as the domain owner intended. High values can lead to trouble as the data in the cache might not match up with the actual data anymore.'); + +$section->addInput(new Form_Select( + 'infra_host_ttl', + 'TTL for Host Cache entries', + $pconfig['infra_host_ttl'], + array_combine(array("60", "120", "300", "600", "900"), array("1 minute", "2 minutes", "5 minutes", "10 minutes", "15 minutes")) +))->setHelp('This timeout is used for when the server is very busy. This protects against denial of service by slow queries or high query rates. The default value is 200 milliseconds. '); + +$section->addInput(new Form_Select( + 'infra_cache_numhosts', + 'Number of Hosts to Cache', + $pconfig['infra_cache_numhosts'], + array_combine(array("1000", "5000", "10000", "20000", "50000"), array("1000", "5000", "10000", "20000", "50000")) +))->setHelp('Number of hosts for which information is cached. The default is 10,000.'); + +$section->addInput(new Form_Select( + 'unwanted_reply_threshold', + 'Unwanted Reply Threshold', + $pconfig['unwanted_reply_threshold'], + array_combine(array("disabled", "5000000", "10000000", "20000000", "40000000", "50000000"), + array("Disabled", "5 million", "10 million", "20 million", "40 million", "50 million")) +))->setHelp('If enabled, a total number of unwanted replies is kept track of in every thread. When it reaches the threshold, a defensive action is taken ' . + 'and a warning is printed to the log file. This defensive action is to clear the RRSet and message caches, hopefully flushing away any poison. ' . + 'The default is disabled, but if enabled a value of 10 million is suggested.'); + +$section->addInput(new Form_Select( + 'log_verbosity', + 'Log level', + $pconfig['log_verbosity'], + array_combine(array("0", "1", "2", "3", "4", "5"), array("Level 0", "Level 1", "Level 2", "Level 3", "Level 4", "Level 5")) +))->setHelp('Select the log verbosity.'); + +$section->addInput(new Form_Checkbox( + 'disable_auto_added_access_control', + 'Disable auto-added access control', + 'disable the automatically-added access control entries', + $pconfig['hdisable_auto_added_access_control'] +))->setHelp('By default, IPv4 and IPv6 networks residing on internal interfaces of this system are permitted. ' . + 'Allowed networks must be manually configured on the Access Lists tab if the auto-added entries are disabled.'); + +$section->addInput(new Form_Checkbox( + 'use_caps', + 'Experimental Bit 0x20 Support', + 'Use 0x-20 encoded random bits in the DNS query to foil spoofing attempts.', + $pconfig['use_caps'] +))->setHelp('See the implementation <a href="https://tools.ietf.org/html/draft-vixie-dnsext-dns0x20-00">draft dns-0x20</a> for more information: '); + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_unbound_domainoverride_edit.php b/src/usr/local/www/services_unbound_domainoverride_edit.php new file mode 100644 index 0000000..2f8533c --- /dev/null +++ b/src/usr/local/www/services_unbound_domainoverride_edit.php @@ -0,0 +1,164 @@ +<?php +/* + services_unbound_domainoverride_edit.php + part of the pfSense project (https://www.pfsense.org) + Copyright (C) 2014 Warren Baker (warren@decoy.co.za) + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Copyright (C) 2003-2005 Bob Zoller <bob@kludgebox.com> and Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: dnsresolver +*/ + +##|+PRIV +##|*IDENT=page-services-dnsresolver-editdomainoverride +##|*NAME=Services: DNS Resolver: Edit Domain Override page +##|*DESCR=Allow access to the 'Services: DNS Resolver: Edit Domain Override' page. +##|*MATCH=services_unbound_domainoverride_edit.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['unbound']['domainoverrides'])) + $config['unbound']['domainoverrides'] = array(); + +$a_domainOverrides = &$config['unbound']['domainoverrides']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_domainOverrides[$id]) { + $pconfig['domain'] = $a_domainOverrides[$id]['domain']; + $pconfig['ip'] = $a_domainOverrides[$id]['ip']; + $pconfig['descr'] = $a_domainOverrides[$id]['descr']; +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "domain ip"); + $reqdfieldsn = array(gettext("Domain"), gettext("IP address")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + function String_Begins_With($needle, $haystack) { + return (substr($haystack, 0, strlen($needle)) == $needle); + } + + if (String_Begins_With(_msdcs, $_POST['domain'])) { + $subdomainstr = substr($_POST['domain'], 7); + if ($subdomainstr && !is_domain($subdomainstr)) { + $input_errors[] = gettext("A valid domain must be specified after _msdcs."); + } + } elseif ($_POST['domain'] && !is_domain($_POST['domain'])) { + $input_errors[] = gettext("A valid domain must be specified."); + } + + if ($_POST['ip']) { + if (strpos($_POST['ip'], '@') !== false) { + $ip_details = explode("@", $_POST['ip']); + if (!is_ipaddr($ip_details[0]) || !is_port($ip_details[1])) { + $input_errors[] = gettext("A valid IP address and port must be specified, for example 192.168.100.10@5353."); + } + } else if (!is_ipaddr($_POST['ip'])) { + $input_errors[] = gettext("A valid IP address must be specified, for example 192.168.100.10."); + } + } + + if (!$input_errors) { + $doment = array(); + $doment['domain'] = $_POST['domain']; + $doment['ip'] = $_POST['ip']; + $doment['descr'] = $_POST['descr']; + + if (isset($id) && $a_domainOverrides[$id]) { + $a_domainOverrides[$id] = $doment; + } else { + $a_domainOverrides[] = $doment; + } + + mark_subsystem_dirty('unbound'); + + write_config(); + + header("Location: services_unbound.php"); + exit; + } +} + +$pgtitle = array(gettext("Services"), gettext("DNS Resolver"), gettext("Edit Domain Override")); +$shortcut_section = "resolver"; +include("head.inc"); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Domain Override'); + +$section->addInput(new Form_Input( + 'domain', + 'Domain', + 'text', + $pconfig['domain'] +))->setHelp('Domain to override (NOTE: this does not have to be a valid TLD!) e.g.: testormycompany.localdomainor1.168.192.in-addr.arpa'); + +$section->addInput(new Form_IpAddress( + 'ip', + 'IP Address', + $pconfig['ip'] +))->setHelp('IP address of the authoritative DNS server for this domain. e.g.: 192.168.100.100' . '<br />' . + 'To use a nondefault port for communication, append an \'@\' with the port number.'); + +$section->addInput(new Form_Input( + 'descr', + 'GUI Log Entries', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +if (isset($id) && $a_domainOverrides[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); + +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_unbound_host_edit.php b/src/usr/local/www/services_unbound_host_edit.php new file mode 100644 index 0000000..ed3bbb2 --- /dev/null +++ b/src/usr/local/www/services_unbound_host_edit.php @@ -0,0 +1,315 @@ +<?php +/* $Id$ */ +/* + services_unbound_host_edit.php + part of the pfSense project (https://www.pfsense.org) + Copyright (C) 2014 Warren Baker (warren@decoy.co.za) + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Copyright (C) 2003-2004 Bob Zoller <bob@kludgebox.com> and Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: dnsresolver +*/ + +##|+PRIV +##|*IDENT=page-services-dnsresolver-edithost +##|*NAME=Services: DNS Resolver: Edit host page +##|*DESCR=Allow access to the 'Services: DNS Resolver: Edit host' page. +##|*MATCH=services_unbound_host_edit.php* +##|-PRIV + +function hostcmp($a, $b) { + return strcasecmp($a['host'], $b['host']); +} + +function hosts_sort() { + global $g, $config; + + if (!is_array($config['unbound']['hosts'])) { + return; + } + + usort($config['unbound']['hosts'], "hostcmp"); +} + +require("guiconfig.inc"); + +if (!is_array($config['unbound']['hosts'])) { + $config['unbound']['hosts'] = array(); +} + +$a_hosts = &$config['unbound']['hosts']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_hosts[$id]) { + $pconfig['host'] = $a_hosts[$id]['host']; + $pconfig['domain'] = $a_hosts[$id]['domain']; + $pconfig['ip'] = $a_hosts[$id]['ip']; + $pconfig['descr'] = $a_hosts[$id]['descr']; + $pconfig['aliases'] = $a_hosts[$id]['aliases']; +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "domain ip"); + $reqdfieldsn = array(gettext("Domain"), gettext("IP address")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ($_POST['host']) { + if (!is_hostname($_POST['host'])) { + $input_errors[] = gettext("The hostname can only contain the characters A-Z, 0-9, '_' and '-'. It may not start or end with '-'."); + } else { + if (!is_unqualified_hostname($_POST['host'])) { + $input_errors[] = gettext("A valid hostname is specified, but the domain name part should be omitted"); + } + } + } + + if (($_POST['domain'] && !is_domain($_POST['domain']))) { + $input_errors[] = gettext("A valid domain must be specified."); + } + + if (($_POST['ip'] && !is_ipaddr($_POST['ip']))) { + $input_errors[] = gettext("A valid IP address must be specified."); + } + + /* collect aliases */ + $aliases = array(); + foreach ($_POST as $key => $value) { + $entry = ''; + if (!substr_compare('aliashost', $key, 0, 9)) { + $entry = substr($key, 9); + $field = 'host'; + } elseif (!substr_compare('aliasdomain', $key, 0, 11)) { + $entry = substr($key, 11); + $field = 'domain'; + } elseif (!substr_compare('aliasdescription', $key, 0, 16)) { + $entry = substr($key, 16); + $field = 'description'; + } + if (ctype_digit($entry)) { + $aliases[$entry][$field] = $value; + } + } + + $pconfig['aliases']['item'] = $aliases; + + /* validate aliases */ + foreach ($aliases as $idx => $alias) { + $aliasreqdfields = array('aliasdomain' . $idx); + $aliasreqdfieldsn = array(gettext("Alias Domain")); + + do_input_validation($_POST, $aliasreqdfields, $aliasreqdfieldsn, $input_errors); + + if ($alias['host']) { + if (!is_hostname($alias['host'])) { + $input_errors[] = gettext("Hostnames in an alias list can only contain the characters A-Z, 0-9 and '-'. They may not start or end with '-'."); + } else { + if (!is_unqualified_hostname($alias['host'])) { + $input_errors[] = gettext("A valid alias hostname is specified, but the domain name part should be omitted"); + } + } + } + if (($alias['domain'] && !is_domain($alias['domain']))) { + $input_errors[] = gettext("A valid domain must be specified in alias list."); + } + } + + /* check for overlaps */ + foreach ($a_hosts as $hostent) { + if (isset($id) && ($a_hosts[$id]) && ($a_hosts[$id] === $hostent)) { + continue; + } + + if (($hostent['host'] == $_POST['host']) && + ($hostent['domain'] == $_POST['domain']) && + ((is_ipaddrv4($hostent['ip']) && is_ipaddrv4($_POST['ip'])) || (is_ipaddrv6($hostent['ip']) && is_ipaddrv6($_POST['ip'])))) { + $input_errors[] = gettext("This host/domain already exists."); + break; + } + } + + if (!$input_errors) { + $hostent = array(); + $hostent['host'] = $_POST['host']; + $hostent['domain'] = $_POST['domain']; + $hostent['ip'] = $_POST['ip']; + $hostent['descr'] = $_POST['descr']; + $hostent['aliases']['item'] = $aliases; + + if (isset($id) && $a_hosts[$id]) { + $a_hosts[$id] = $hostent; + } else { + $a_hosts[] = $hostent; + } + hosts_sort(); + + mark_subsystem_dirty('unbound'); + + write_config(); + + header("Location: services_unbound.php"); + exit; + } +} + +// Delete a row in the options table +if($_GET['act'] == "delopt") { + $idx = $_GET['id']; + + if($pconfig['aliases'] && is_array($pconfig['aliases']['item'][$idx])) { + unset($pconfig['aliases']['item'][$idx]); + } +} + +// Add an option row +if($_GET['act'] == "addopt") { + if(!is_array($pconfig['aliases']['item'])) + $pconfig['aliases']['item'] = array(); + + array_push($pconfig['aliases']['item'], array('host' => null, 'domain' => null, 'description' => null)); +} + +$pgtitle = array(gettext("Services"),gettext("DNS Resolver"),gettext("Edit host")); +$shortcut_section = "resolver"; +include("head.inc"); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Edit DNS Rersolver Entry'); + +$section->addInput(new Form_Input( + 'host', + 'Host', + 'text', + $pconfig['domain'] +))->setHelp('Name of the host, without the domain part' . '<br />' . + 'e.g.: "myhost"'); + +$section->addInput(new Form_Input( + 'domain', + 'Domain', + 'text', + $pconfig['domain'] +))->setHelp('Domain of the host' . '<br />' . + 'e.g.: "example.com"'); + +$section->addInput(new Form_IpAddress( + 'ip', + 'IP Address', + $pconfig['ip'] +))->setHelp('IP address of the host' . '<br />' . + 'e.g.: 192.168.100.100 or fd00:abcd::1'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +if (isset($id) && $a_hosts[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $pconfig['id'] + )); +} + +$form->add($section); + +$section = new Form_Section('Additional names for this host'); + +if( $pconfig['aliases']['item']) { + $counter = 0; + $last = count($pconfig['aliases']['item']) - 1; + + foreach($pconfig['aliases']['item'] as $item) { + $group = new Form_Group(null); + + $group->add(new Form_Input( + 'aliashost' . $counter, + null, + 'text', + $item['host'] + ))->setHelp($counter == $last ? 'Host name':null); + + $group->add(new Form_Input( + 'aliasdomain' . $counter, + null, + 'text', + $item['domain'] + ))->setHelp($counter == $last ? 'Value':null); + + $group->add(new Form_Input( + 'aliasdescription' . $counter, + null, + 'text', + $item['description'] + ))->setHelp($counter == $last ? 'Description':null); + + $btn = new Form_Button( + 'btn' . $counter, + 'Delete', + 'services_unbound_host_edit.php?act=delopt' . '&id=' . $counter + ); + + $btn->removeClass('btn-primary')->addClass('btn-danger btn-sm'); + $group->add($btn); + $section->add($group); + $counter++; + } +} + +$btnaddopt = new Form_Button( + 'btnaddopt', + 'Add Option', + 'services_unbound_host_edit.php?act=addopt' +); + +$btnaddopt->removeClass('btn-primary')->addClass('btn-success btn-sm'); + +$section->addInput($btnaddopt); + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_wol.php b/src/usr/local/www/services_wol.php new file mode 100644 index 0000000..40917d9 --- /dev/null +++ b/src/usr/local/www/services_wol.php @@ -0,0 +1,215 @@ +<?php +/* $Id$ */ +/* + services_wol.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/local/bin/wol + pfSense_MODULE: wol +*/ + +##|+PRIV +##|*IDENT=page-services-wakeonlan +##|*NAME=Services: Wake on LAN page +##|*DESCR=Allow access to the 'Services: Wake on LAN' page. +##|*MATCH=services_wol.php* +##|-PRIV + +require("guiconfig.inc"); +require('classes/Form.class.php'); + +if (!is_array($config['wol']['wolentry'])) { + $config['wol']['wolentry'] = array(); +} +$a_wol = &$config['wol']['wolentry']; + +if($_GET['wakeall'] != "") { + $i = 0; + $savemsg = ""; + foreach ($a_wol as $wolent) { + $mac = $wolent['mac']; + $if = $wolent['interface']; + $description = $wolent['descr']; + $ipaddr = get_interface_ip($if); + if (!is_ipaddr($ipaddr)) { + continue; + } + $bcip = gen_subnet_max($ipaddr, get_interface_subnet($if)); + /* Execute wol command and check return code. */ + if (!mwexec("/usr/local/bin/wol -i {$bcip} {$mac}")) { + $savemsg .= sprintf(gettext('Sent magic packet to %1$s (%2$s)%3$s'), $mac, $description, ".<br />"); + } else { + $savemsg .= sprintf(gettext('Please check the %1$ssystem log%2$s, the wol command for %3$s (%4$s) did not complete successfully%5$s'), '<a href="/diag_logs.php">', '</a>', $description, $mac, ".<br />"); + } + } +} + +if ($_POST || $_GET['mac']) { + unset($input_errors); + + if ($_GET['mac']) { + /* normalize MAC addresses - lowercase and convert Windows-ized hyphenated MACs to colon delimited */ + $_GET['mac'] = strtolower(str_replace("-", ":", $_GET['mac'])); + $mac = $_GET['mac']; + $if = $_GET['if']; + } else { + /* normalize MAC addresses - lowercase and convert Windows-ized hyphenated MACs to colon delimited */ + $_POST['mac'] = strtolower(str_replace("-", ":", $_POST['mac'])); + $mac = $_POST['mac']; + $if = $_POST['interface']; + } + + /* input validation */ + if (!$mac || !is_macaddr($mac)) { + $input_errors[] = gettext("A valid MAC address must be specified."); + } + if (!$if) { + $input_errors[] = gettext("A valid interface must be specified."); + } + + if (!$input_errors) { + /* determine broadcast address */ + $ipaddr = get_interface_ip($if); + if (!is_ipaddr($ipaddr)) { + $input_errors[] = gettext("A valid ip could not be found!"); + } else { + $bcip = gen_subnet_max($ipaddr, get_interface_subnet($if)); + /* Execute wol command and check return code. */ + if (!mwexec("/usr/local/bin/wol -i {$bcip} " . escapeshellarg($mac))) { + $savemsg .= sprintf(gettext("Sent magic packet to %s."), $mac); + } else { + $savemsg .= sprintf(gettext('Please check the %1$ssystem log%2$s, the wol command for %3$s did not complete successfully%4$s'), '<a href="/diag_logs.php">', '</a>', $mac, ".<br />"); + } + } + } +} + +if ($_GET['act'] == "del") { + if ($a_wol[$_GET['id']]) { + unset($a_wol[$_GET['id']]); + write_config(); + header("Location: services_wol.php"); + exit; + } +} + +$pgtitle = array(gettext("Services"), gettext("Wake on LAN")); +include("head.inc"); + +?> + +<p><?=gettext("This service can be used to wake up (power on) computers by sending special") . '\'' . gettext("Magic Packets") . '\'' . gettext("The NIC in the computer that is to be woken up must support Wake on LAN and has to be configured properly (WOL cable, BIOS settings). ")?></p> + +<?php + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg); + +$form = new Form('Send'); + +$section = new Form_Section('Wake on LAN'); + +$section->addInput(new Form_Select( + 'interface', + 'Interface', + (link_interface_to_bridge($if) ? null : $if), + get_configured_interface_with_descr() +))->setHelp('Choose which interface the host to be woken up is connected to.'); + +$section->addInput(new Form_Input( + 'mac', + 'MAC address', + 'text', + $mac +))->setHelp(gettext('Enter a MAC address in the following format: xx:xx:xx:xx:xx:xx')); + +$form->add($section); +print $form; +?> + +<div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title">Wake on LAN devices</h2> + </div> + + <div class="panel-body"> + <p><?=gettext("Click the MAC address to wake up an individual device.")?></p> + <div class="table-responsive"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th><?=gettext("Interface")?></th> + <th><?=gettext("MAC address")?></th> + <th><?=gettext("Description")?></th> + <th></th> + </tr> + </thead> + <tbody> + <?php foreach ($a_wol as $i => $wolent): ?> + <tr> + <td> + <?=convert_friendly_interface_to_friendly_descr($wolent['interface']);?> + </td> + <td> + <a href="?mac=<?=$wolent['mac'];?>&if=<?=$wolent['interface'];?>"><?=strtolower($wolent['mac']);?></a> + </td> + <td> + <?=htmlspecialchars($wolent['descr']);?> + </td> + <td> + <a class="btn btn-xs btn-primary" href="services_wol_edit.php?id=<?=$i?>"> + edit + </a> + <a class="btn btn-xs btn-danger" href="services_wol.php?act=del&id=<?=$i?>"> + delete + </a> + </td> + </tr> + <?php endforeach?> + </tbody> + </table> + </div> + </div> + <div class="panel-footer"> + <a class="btn btn-success" href="services_wol_edit.php"> + Add + </a> + + <a href="services_wol.php?wakeall=true" role="button" class="btn btn-primary"> + <?=gettext("Wake all devices")?> + </a> + </div> +</div> + +<?php + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/services_wol_edit.php b/src/usr/local/www/services_wol_edit.php new file mode 100644 index 0000000..2c750ed --- /dev/null +++ b/src/usr/local/www/services_wol_edit.php @@ -0,0 +1,159 @@ +<?php +/* $Id$ */ +/* + services_wol_edit.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: wol +*/ + +##|+PRIV +##|*IDENT=page-services-wakeonlan-edit +##|*NAME=Services: Wake on LAN: Edit page +##|*DESCR=Allow access to the 'Services: Wake on LAN: Edit' page. +##|*MATCH=services_wol_edit.php* +##|-PRIV + +function wolcmp($a, $b) { + return strcmp($a['descr'], $b['descr']); +} + +function wol_sort() { + global $config; + + usort($config['wol']['wolentry'], "wolcmp"); +} + +require("guiconfig.inc"); +require('classes/Form.class.php'); + +if (!is_array($config['wol']['wolentry'])) { + $config['wol']['wolentry'] = array(); +} +$a_wol = &$config['wol']['wolentry']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_wol[$id]) { + $pconfig['interface'] = $a_wol[$id]['interface']; + $pconfig['mac'] = $a_wol[$id]['mac']; + $pconfig['descr'] = $a_wol[$id]['descr']; +} else { + $pconfig['interface'] = $_GET['if']; + $pconfig['mac'] = $_GET['mac']; + $pconfig['descr'] = $_GET['descr']; +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "interface mac"); + $reqdfieldsn = array(gettext("Interface"), gettext("MAC address")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + /* normalize MAC addresses - lowercase and convert Windows-ized hyphenated MACs to colon delimited */ + $_POST['mac'] = strtolower(str_replace("-", ":", $_POST['mac'])); + + if (($_POST['mac'] && !is_macaddr($_POST['mac']))) { + $input_errors[] = gettext("A valid MAC address must be specified."); + } + + if (!$input_errors) { + $wolent = array(); + $wolent['interface'] = $_POST['interface']; + $wolent['mac'] = $_POST['mac']; + $wolent['descr'] = $_POST['descr']; + + if (isset($id) && $a_wol[$id]) { + $a_wol[$id] = $wolent; + } else { + $a_wol[] = $wolent; + } + wol_sort(); + + write_config(); + + header("Location: services_wol.php"); + exit; + } +} + +$pgtitle = array(gettext("Services"), gettext("Wake on LAN"), gettext("Edit")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +$form = new Form; + +if (isset($id) && $a_wol[$id]) { + $form->addGlobal(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$section = new Form_Section('Edit WOL entry'); + +$section->addInput(new Form_Select( + 'interface', + 'Interface', + (link_interface_to_bridge($pconfig['interface']) ? null : $pconfig['interface']), + get_configured_interface_with_descr() +))->setHelp('Choose which interface this host is connected to.'); + +$section->addInput(new Form_Input( + 'mac', + 'MAC address', + 'text', + $pconfig['mac'] +))->setHelp(gettext('Enter a MAC address in the following format: xx:xx:xx:xx:xx:xx')); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp(gettext('You may enter a description here for your reference (not parsed).')); + +$form->add($section); +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/shortcuts.inc b/src/usr/local/www/shortcuts.inc new file mode 100644 index 0000000..c4bf48e --- /dev/null +++ b/src/usr/local/www/shortcuts.inc @@ -0,0 +1,289 @@ +<?php +/* $Id$ */ +/* + Copyright (C) 2012 Jim Pingle + All rights reserved. + + Copyright (C) 2007, 2008 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*/ + +/* + pfSense_MODULE: system +*/ + +// On the page, add in like so: +// $shortcut_section = "relayd"; + +$shortcuts = array(); + +/* Load and process custom shortcuts. */ +function get_shortcut_files($directory) { + $dir_array = array(); + if (!is_dir($directory)) { + return; + } + if ($dh = opendir($directory)) { + while (($file = readdir($dh)) !== false) { + $canadd = 0; + if ($file == ".") { + $canadd = 1; + } + if ($file == "..") { + $canadd = 1; + } + if ($canadd == 0) { + array_push($dir_array, $file); + } + } + closedir($dh); + } + if (!is_array($dir_array)) { + return; + } + return $dir_array; +} + +function get_shortcut_by_service_name($servicename) { + global $shortcuts; + foreach ($shortcuts as $name => $shortcut) { + if (!empty($shortcut['service']) && ($shortcut['service'] == $servicename)) { + return $name; + } + } + return null; +} + +function get_shortcut_main_link($shortcut_section, $addspace = true, $service = array()) { + global $g, $shortcuts; + if (empty($shortcut_section)) { + return ""; + } + $space = ($addspace) ? " " : "" ; + switch ($shortcut_section) { + case "openvpn": + if (!empty($service['mode']) && is_numeric($service['id'])) { + $link = "vpn_openvpn_{$service['mode']}.php?act=edit&id={$service['id']}"; + } else { + $link = $shortcuts[$shortcut_section]['main']; + } + break; + case "captiveportal": + if (!empty($service['zone'])) { + $link = "services_captiveportal.php?zone={$service['zone']}"; + } else { + $link = $shortcuts[$shortcut_section]['main']; + } + break; + default: + $link = $shortcuts[$shortcut_section]['main']; + break; + } + if (!empty($link) && ($_SERVER['REQUEST_URI'] != "/{$link}")) { + return "{$space}<a href=\"{$link}\" title=\"" . gettext("Main page for this section") . "\"><img style=\"vertical-align:middle\" src=\"/themes/{$g['theme']}/images/icons/icon_plus.gif\" border=\"0\" alt=\"plus\" /></a>"; + } +} + +function get_shortcut_status_link($shortcut_section, $addspace = true, $service = array()) { + global $g, $shortcuts, $cpzone; + if (empty($shortcut_section)) { + return ""; + } + $space = ($addspace) ? " " : "" ; + if (!empty($cpzone)) { + $zone = $cpzone; + } elseif (!empty($service['zone'])) { + $zone = $service['zone']; + } + switch ($shortcut_section) { + case "captiveportal": + if (!empty($zone)) { + $link = "status_captiveportal.php?zone={$zone}"; + } else { + $link = $shortcuts[$shortcut_section]['status']; + } + break; + default: + $link = $shortcuts[$shortcut_section]['status']; + break; + } + if (!empty($link)) { + return "{$space}<a href=\"{$link}\" title=\"" . gettext("Status of items on this page") . "\"><img style=\"vertical-align:middle\" src=\"/themes/{$g['theme']}/images/icons/icon_service_status.gif\" border=\"0\" alt=\"status\" /></a>"; + } +} + +function get_shortcut_log_link($shortcut_section, $addspace = true) { + global $g, $shortcuts; + $space = ($addspace) ? " " : "" ; + if (!empty($shortcut_section) && !empty($shortcuts[$shortcut_section]['log'])) { + return "{$space}<a href=\"{$shortcuts[$shortcut_section]['log']}\" title=\"" . gettext("Log entries for items on this page") . "\"><img style=\"vertical-align:middle\" src=\"/themes/{$g['theme']}/images/icons/icon_logs.gif\" border=\"0\" alt=\"logs\" /></a>"; + } +} + +// Load shortcuts +$dir_array = get_shortcut_files("/usr/local/www/shortcuts"); +foreach ($dir_array as $file) { + if (!is_dir("/usr/local/www/shortcuts/{$file}") && stristr($file, ".inc")) { + include("/usr/local/www/shortcuts/{$file}"); + } +} +if (is_dir("/usr/local/pkg/shortcuts")) { + $dir_array = get_shortcut_files("/usr/local/pkg/shortcuts"); + foreach ($dir_array as $file) { + if (!is_dir("/usr/local/pkg/shortcuts/{$file}") && stristr($file, ".inc")) { + include("/usr/local/pkg/shortcuts/{$file}"); + } + } +} + +$shortcuts['relayd'] = array(); +$shortcuts['relayd']['main'] = "load_balancer_pool.php"; +$shortcuts['relayd']['log'] = "diag_logs_relayd.php"; +$shortcuts['relayd']['status'] = "status_lb_pool.php"; +$shortcuts['relayd']['service'] = "relayd"; + +$shortcuts['relayd-virtualservers'] = array(); +$shortcuts['relayd-virtualservers']['main'] = "load_balancer_virtual_server.php"; +$shortcuts['relayd-virtualservers']['log'] = "diag_logs_relayd.php"; +$shortcuts['relayd-virtualservers']['status'] = "status_lb_vs.php"; +$shortcuts['relayd-virtualservers']['service'] = "relayd"; + +$shortcuts['captiveportal'] = array(); +$shortcuts['captiveportal']['main'] = "services_captiveportal_zones.php"; +$shortcuts['captiveportal']['log'] = "diag_logs_auth.php"; +$shortcuts['captiveportal']['status'] = "status_captiveportal.php"; +$shortcuts['captiveportal']['service'] = "captiveportal"; + +$shortcuts['captiveportal-vouchers'] = array(); +$shortcuts['captiveportal-vouchers']['log'] = "diag_logs_auth.php"; +$shortcuts['captiveportal-vouchers']['status'] = "status_captiveportal_vouchers.php"; +$shortcuts['captiveportal-vouchers']['service'] = "captiveportal"; + +$shortcuts['dhcp'] = array(); +$shortcuts['dhcp']['main'] = "services_dhcp.php"; +$shortcuts['dhcp']['log'] = "diag_logs_dhcp.php"; +$shortcuts['dhcp']['status'] = "status_dhcp_leases.php"; +$shortcuts['dhcp']['service'] = "dhcpd"; + +$shortcuts['dhcp6'] = array(); +$shortcuts['dhcp6']['main'] = "services_dhcpv6.php"; +$shortcuts['dhcp6']['log'] = "diag_logs_dhcp.php"; +$shortcuts['dhcp6']['status'] = "status_dhcpv6_leases.php"; + + +$shortcuts['ipsec'] = array(); +$shortcuts['ipsec']['main'] = "vpn_ipsec.php"; +$shortcuts['ipsec']['log'] = "diag_logs_ipsec.php"; +$shortcuts['ipsec']['status'] = "diag_ipsec.php"; +$shortcuts['ipsec']['service'] = "ipsec"; + +$shortcuts['openvpn'] = array(); +$shortcuts['openvpn']['main'] = "vpn_openvpn_server.php"; +$shortcuts['openvpn']['log'] = "diag_logs_openvpn.php"; +$shortcuts['openvpn']['status'] = "status_openvpn.php"; +$shortcuts['openvpn']['service'] = "openvpn"; + +$shortcuts['firewall'] = array(); +$shortcuts['firewall']['main'] = "firewall_rules.php"; +$shortcuts['firewall']['log'] = "diag_logs_filter.php"; +$shortcuts['firewall']['status'] = "status_filter_reload.php"; + +$shortcuts['routing'] = array(); +$shortcuts['routing']['main'] = "system_routes.php"; +$shortcuts['routing']['log'] = "diag_logs_routing.php"; +$shortcuts['routing']['status'] = "diag_routes.php"; + +$shortcuts['gateways'] = array(); +$shortcuts['gateways']['main'] = "system_gateways.php"; +$shortcuts['gateways']['log'] = "diag_logs_gateways.php"; +$shortcuts['gateways']['status'] = "status_gateways.php"; +$shortcuts['gateways']['service'] = "apinger"; + +$shortcuts['gateway-groups'] = array(); +$shortcuts['gateway-groups']['main'] = "system_gateway_groups.php"; +$shortcuts['gateway-groups']['log'] = "diag_logs_gateways.php"; +$shortcuts['gateway-groups']['status'] = "status_gateway_groups.php"; + +$shortcuts['interfaces'] = array(); +$shortcuts['interfaces']['main'] = "interfaces_assign.php"; +$shortcuts['interfaces']['status'] = "status_interfaces.php"; + +$shortcuts['trafficshaper'] = array(); +$shortcuts['trafficshaper']['main'] = "firewall_shaper.php"; +$shortcuts['trafficshaper']['status'] = "status_queues.php"; + +$shortcuts['trafficshaper-limiters'] = array(); +$shortcuts['trafficshaper-limiters']['main'] = "firewall_shaper_vinterface.php"; +$shortcuts['trafficshaper-limiters']['status'] = "diag_limiter_info.php"; + +$shortcuts['forwarder'] = array(); +$shortcuts['forwarder']['main'] = "services_dnsmasq.php"; +$shortcuts['forwarder']['log'] = "diag_logs_resolver.php"; +$shortcuts['forwarder']['service'] = "dnsmasq"; + +$shortcuts['resolver'] = array(); +$shortcuts['resolver']['main'] = "services_unbound.php"; +$shortcuts['resolver']['log'] = "diag_logs_resolver.php"; +$shortcuts['resolver']['service'] = "unbound"; + +$shortcuts['wireless'] = array(); +$shortcuts['wireless']['main'] = "interfaces_wireless.php"; +$shortcuts['wireless']['log'] = "diag_logs_wireless.php"; +$shortcuts['wireless']['status'] = "status_wireless.php"; + +$shortcuts['ntp'] = array(); +$shortcuts['ntp']['main'] = "services_ntpd.php"; +$shortcuts['ntp']['log'] = "diag_logs_ntpd.php"; +$shortcuts['ntp']['status'] = "status_ntpd.php"; +$shortcuts['ntp']['service'] = "ntpd"; + +$shortcuts['pptps'] = array(); +$shortcuts['pptps']['main'] = "vpn_pptp.php"; +$shortcuts['pptps']['log'] = "diag_logs_vpn.php"; + +$shortcuts['pppoes'] = array(); +$shortcuts['pppoes']['main'] = "vpn_pppoe.php"; +$shortcuts['pppoes']['log'] = "diag_logs_vpn.php?vpntype=poes"; + +$shortcuts['l2tps'] = array(); +$shortcuts['l2tps']['main'] = "vpn_l2tp.php"; +$shortcuts['l2tps']['log'] = "diag_logs_vpn.php?vpntype=l2tp"; + +$shortcuts['carp'] = array(); +$shortcuts['carp']['main'] = "system_hasync.php"; +$shortcuts['carp']['status'] = "carp_status.php"; + +$shortcuts['snmp'] = array(); +$shortcuts['snmp']['main'] = "services_snmp.php"; +$shortcuts['snmp']['service'] = "bsnmpd"; + +$shortcuts['authentication'] = array(); +$shortcuts['authentication']['main'] = "system_authservers.php"; +$shortcuts['authentication']['status'] = "diag_authentication.php"; + +$shortcuts['aliases'] = array(); +$shortcuts['aliases']['main'] = "firewall_aliases.php"; +$shortcuts['aliases']['status'] = "diag_tables.php"; +?> diff --git a/src/usr/local/www/shortcuts/pgk_upnp.php b/src/usr/local/www/shortcuts/pgk_upnp.php new file mode 100644 index 0000000..3ee7f81 --- /dev/null +++ b/src/usr/local/www/shortcuts/pgk_upnp.php @@ -0,0 +1,11 @@ +<?php + +global $shortcuts; + +$shortcuts['upnp'] = array(); +$shortcuts['upnp']['main'] = "pkg_edit.php?xml=miniupnpd.xml"; +$shortcuts['upnp']['log'] = "diag_logs_routing.php"; +$shortcuts['upnp']['status'] = "status_upnp.php"; +$shortcuts['upnp']['service'] = "miniupnpd"; + +?>
\ No newline at end of file diff --git a/src/usr/local/www/stats.php b/src/usr/local/www/stats.php new file mode 100644 index 0000000..6f34673 --- /dev/null +++ b/src/usr/local/www/stats.php @@ -0,0 +1,46 @@ +<?php +/* + $Id$ + part of pfSense (https://www.pfsense.org) + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2007 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-cpuutilization +##|*NAME=Diagnostics: CPU Utilization page +##|*DESCR=Allow access to the 'Diagnostics: CPU Utilization' page. +##|*MATCH=stats.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("includes/functions.inc.php"); + +$cpu = cpu_usage(); + +echo $cpu; +exit; + +?> diff --git a/src/usr/local/www/status.php b/src/usr/local/www/status.php new file mode 100755 index 0000000..e8c92fe --- /dev/null +++ b/src/usr/local/www/status.php @@ -0,0 +1,232 @@ +<?php +/* $Id$ */ +/* Run various commands and collect their output into HTML tables. + * Jim McBeath <jimmc@macrovision.com> Nov 2003 + * + * (modified for m0n0wall by Manuel Kasper <mk@neon1.net>) + * (modified for pfSense by Scott Ullrich geekgod@pfsense.com) + * + */ +/* + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/bin/vmstat /usr/bin/netstat /sbin/dmesg /sbin/mount /sbin/setkey /usr/local/sbin/pftop + pfSense_BUILDER_BINARIES: /sbin/pfctl /sbin/sysctl /usr/bin/top /usr/bin/netstat /sbin/pfctl /sbin/ifconfig + pfSense_MODULE: support +*/ + +##|+PRIV +##|*IDENT=page-hidden-detailedstatus +##|*NAME=Hidden: Detailed Status page +##|*DESCR=Allow access to the 'Hidden: Detailed Status' page. +##|*MATCH=status.php* +##|-PRIV + +/* Execute a command, with a title, and generate an HTML table + * showing the results. + */ + +/* include all configuration functions */ +require_once("guiconfig.inc"); +require_once("functions.inc"); +$output_path = "/tmp/status_output/"; +$output_file = "/tmp/status_output.tgz"; + +function doCmdT($title, $command) { + global $output_path, $output_file; + /* Fixup output directory */ + + $rubbish = array('|', '-', '/', '.', ' '); /* fixes the <a> tag to be W3C compliant */ + echo "\n<a name=\"" . str_replace($rubbish, '', $title) . "\" id=\"" . str_replace($rubbish, '', $title) . "\"></a>\n"; + echo "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" summary=\"" . $title . "\">\n"; + echo "\t<tr><td class=\"listtopic\">" . $title . "</td></tr>\n"; + echo "\t<tr>\n\t\t<td class=\"listlr\">\n\t\t\t<pre>"; /* no newline after pre */ + + print('<div class="panel panel-default">'); + print( '<div class="panel-heading">' . $title . '</div>'); + print( '<div class="panel-body">'); + print( '<pre>'); + + if ($command == "dumpconfigxml") { + $ofd = @fopen("{$output_path}/config-sanitized.xml", "w"); + $fd = @fopen("/conf/config.xml", "r"); + if ($fd) { + while (!feof($fd)) { + $line = fgets($fd); + /* remove sensitive contents */ + $line = preg_replace("/<password>.*?<\\/password>/", "<password>xxxxx</password>", $line); + $line = preg_replace("/<pre-shared-key>.*?<\\/pre-shared-key>/", "<pre-shared-key>xxxxx</pre-shared-key>", $line); + $line = preg_replace("/<rocommunity>.*?<\\/rocommunity>/", "<rocommunity>xxxxx</rocommunity>", $line); + $line = preg_replace("/<prv>.*?<\\/prv>/", "<prv>xxxxx</prv>", $line); + $line = preg_replace("/<shared_key>.*?<\\/shared_key>/", "<shared_key>xxxxx</shared_key>", $line); + $line = preg_replace("/<tls>.*?<\\/tls>/", "<tls>xxxxx</tls>", $line); + $line = preg_replace("/<ipsecpsk>.*?<\\/ipsecpsk>/", "<ipsecpsk>xxxxx</ipsecpsk>", $line); + $line = preg_replace("/<md5-hash>.*?<\\/md5-hash>/", "<md5-hash>xxxxx</md5-hash>", $line); + $line = preg_replace("/<md5password>.*?<\\/md5password>/", "<md5password>xxxxx</md5password>", $line); + $line = preg_replace("/<radius_secret>.*?<\\/radius_secret>/", "<radius_secret>xxxxx</radius_secret>", $line); + $line = preg_replace("/<ldap_bindpw>.*?<\\/ldap_bindpw>/", "<ldap_bindpw>xxxxx</ldap_bindpw>", $line); + $line = preg_replace("/<passwordagain>.*?<\\/passwordagain>/", "<passwordagain>xxxxx</passwordagain>", $line); + $line = preg_replace("/<crypto_password>.*?<\\/crypto_password>/", "<crypto_password>xxxxx</crypto_password>", $line); + $line = preg_replace("/<crypto_password2>.*?<\\/crypto_password2>/", "<crypto_password2>xxxxx</crypto_password2>", $line); + $line = str_replace("\t", " ", $line); + echo htmlspecialchars($line, ENT_NOQUOTES); + fwrite($ofd, $line); + } + } + fclose($fd); + fclose($ofd); + } else { + $ofd = @fopen("{$output_path}/{$title}.txt", "w"); + $execOutput = ""; + $execStatus = ""; + exec ($command . " 2>&1", $execOutput, $execStatus); + for ($i = 0; isset($execOutput[$i]); $i++) { + if ($i > 0) { + echo "\n"; + } + echo htmlspecialchars($execOutput[$i], ENT_NOQUOTES); + fwrite($ofd, $execOutput[$i] . "\n"); + } + fclose($ofd); + } + + print( '</pre>'); + print( '</div>'); + print('</div>'); +} + +/* Define a command, with a title, to be executed later. */ +function defCmdT($title, $command) { + global $commands; + $title = htmlspecialchars($title, ENT_NOQUOTES); + $commands[] = array($title, $command); +} + +/* List all of the commands as an index. */ +function listCmds() { + global $currentDate; + global $commands; + + $rubbish = array('|', '-', '/', '.', ' '); /* fixes the <a> tag to be W3C compliant */ + + print('<div class="panel panel-default">'); + print( '<div class="panel-heading">' . gettext("System status on ") . $currentDate . '</div>'); + print( '<div class="panel-body">'); + + print("\n<p>" . gettext("This status page includes the following information") . ":\n"); + print("<ul>\n"); + for ($i = 0; isset($commands[$i]); $i++ ) { + print("\t<li><strong><a href=\"#" . str_replace($rubbish,'',$commands[$i][0]) . "\">" . $commands[$i][0] . "</a></strong></li>\n"); + } + + print("</ul>\n"); + print(' </div>'); + print('</div>'); +} + +/* Execute all of the commands which were defined by a call to defCmd. */ +function execCmds() { + global $commands; + for ($i = 0; isset($commands[$i]); $i++) { + doCmdT($commands[$i][0], $commands[$i][1]); + } +} + +global $g, $config; + +/* Set up all of the commands we want to execute. */ +defCmdT("System uptime","uptime"); +defCmdT("Interfaces","/sbin/ifconfig -a"); +defCmdT("PF Info","/sbin/pfctl -s info"); +defCmdT("Routing tables","netstat -nr"); +defCmdT("top | head -n5", "/usr/bin/top | /usr/bin/head -n5"); +defCmdT("sysctl hw.physmem","/sbin/sysctl hw.physmem"); + +if (isset($config['captiveportal']) && is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $cpZone => $cpdata) { + if (isset($cpdata['enable'])) { + defCmdT("IPFW rules for {$cpdata['zoneid']}", "/sbin/ipfw -x " . escapeshellarg($cpdata['zoneid']) . " show"); + } + } +} + +/* Configuration Files */ +defCmdT("Contents of /var/run", "/bin/ls /var/run"); +defCmdT("Contents of /conf", "/bin/ls /conf"); +defCmdT("config.xml", "dumpconfigxml"); +defCmdT("resolv.conf", "/bin/cat /etc/resolv.conf"); +defCmdT("DHCP Configuration", "/bin/cat /var/dhcpd/etc/dhcpd.conf"); +defCmdT("DHCPv6 Configuration", "/bin/cat /var/dhcpd/etc/dhcpdv6.conf"); +defCmdT("strongSwan config", "/bin/cat /var/etc/ipsec/strongswan.conf"); +defCmdT("IPsec config", "/bin/cat /var/etc/ipsec/ipsec.conf"); +defCmdT("SPD", "/sbin/setkey -DP"); +defCmdT("SAD", "/sbin/setkey -D"); +if (file_exists("/cf/conf/upgrade_log.txt")) { + defCmdT("Upgrade Log", "/bin/cat /cf/conf/upgrade_log.txt"); +} +if (file_exists("/boot/loader.conf")) { + defCmdT("Loader Configuration", "/bin/cat /boot/loader.conf"); +} +if (file_exists("/boot/loader.conf.local")) { + defCmdT("Loader Configuration (Local)", "/bin/cat /boot/loader.conf.local"); +} +if (file_exists("/var/run/apinger.status")) { + defCmdT("Gateway Status", "/bin/cat /var/run/apinger.status"); +} +if (file_exists("/var/etc/apinger.conf")) { + defCmdT("Gateway Monitoring Config", "/bin/cat /var/etc/apinger.conf"); +} +if (file_exists("/var/etc/filterdns.conf")) { + defCmdT("Filter DNS Daemon Config", "/bin/cat /var/etc/filterdns.conf"); +} +if (isset($config['system']['usefifolog'])) { + defCmdT("last 200 system log entries", "/usr/sbin/fifolog_reader /var/log/system.log 2>&1 | tail -n 200"); + defCmdT("last 50 filter log entries", "/usr/sbin/fifolog_reader /var/log/filter.log 2>&1 | tail -n 50"); +} else { + defCmdT("last 200 system log entries", "/usr/local/sbin/clog /var/log/system.log 2>&1 | tail -n 200"); + defCmdT("last 50 filter log entries", "/usr/local/sbin/clog /var/log/filter.log 2>&1 | tail -n 50"); +} +if (file_exists("/tmp/PHP_errors.log")) { + defCmdT("PHP Error Log", "/bin/cat /tmp/PHP_errors.log"); +} +defCmdT("System Message Buffer", "/sbin/dmesg -a"); +defCmdT("System Message Buffer (Boot)", "/bin/cat /var/log/dmesg.boot"); +defCmdT("sysctl values", "/sbin/sysctl -a"); + +exec("/bin/date", $dateOutput, $dateStatus); +$currentDate = $dateOutput[0]; + +$pgtitle = array("{$g['product_name']}", "status"); +include("head.inc"); + +print_info_box(gettext("Make sure all sensitive information is removed! (Passwords, maybe also IP addresses) before posting " . + "information from this page in public places (like mailing lists)") . '<br />' . + gettext("Passwords in config.xml have been automatically removed")); + +listCmds(); +execCmds(); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_captiveportal.php b/src/usr/local/www/status_captiveportal.php new file mode 100644 index 0000000..efe6851 --- /dev/null +++ b/src/usr/local/www/status_captiveportal.php @@ -0,0 +1,255 @@ +<?php +/* $Id$ */ +/* + status_captiveportal.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-status-captiveportal +##|*NAME=Status: Captive portal page +##|*DESCR=Allow access to the 'Status: Captive portal' page. +##|*MATCH=status_captiveportal.php* +##|-PRIV + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + + +$pgtitle = array(gettext("Status: Captive portal")); +$shortcut_section = "captiveportal"; + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +if (count($a_cp) == 1) { + $cpzone = current(array_keys($a_cp)); +} + +/* If the zone does not exist, do not display the invalid zone */ +if (!array_key_exists($cpzone, $a_cp)) { + $cpzone = ""; +} + +if (isset($cpzone) && !empty($cpzone) && isset($a_cp[$cpzone]['zoneid'])) { + $cpzoneid = $a_cp[$cpzone]['zoneid']; +} + +if ($_GET['act'] == "del" && !empty($cpzone) && isset($cpzoneid) && isset($_GET['id'])) { + captiveportal_disconnect_client($_GET['id']); + header("Location: status_captiveportal.php?zone={$cpzone}"); + exit; +} + +include("head.inc"); + +flush(); + +function clientcmp($a, $b) { + global $order; + return strcmp($a[$order], $b[$order]); +} + +if (!empty($cpzone)) { + $cpdb = captiveportal_read_db(); + + if ($_GET['order']) { + if ($_GET['order'] == "ip") { + $order = 2; + } else if ($_GET['order'] == "mac") { + $order = 3; + } else if ($_GET['order'] == "user") { + $order = 4; + } else if ($_GET['order'] == "lastact") { + $order = 5; + } else { + $order = 0; + } + usort($cpdb, "clientcmp"); + } +} + +if (!empty($cpzone) && isset($config['voucher'][$cpzone]['enable'])): + $tab_array = array(); + $tab_array[] = array(gettext("Active Users"), true, "status_captiveportal.php?zone=" . htmlspecialchars($cpzone)); + $tab_array[] = array(gettext("Active Vouchers"), false, "status_captiveportal_vouchers.php?zone=" . htmlspecialchars($cpzone)); + $tab_array[] = array(gettext("Voucher Rolls"), false, "status_captiveportal_voucher_rolls.php?zone=" . htmlspecialchars($cpzone)); + $tab_array[] = array(gettext("Test Vouchers"), false, "status_captiveportal_test.php?zone=" . htmlspecialchars($cpzone)); + $tab_array[] = array(gettext("Expire Vouchers"), false, "status_captiveportal_expire.php?zone=" . htmlspecialchars($cpzone)); + display_top_tabs($tab_array); +endif; + +// Load MAC-Manufacturer table +$mac_man = load_mac_manufacturer_table(); + +require('classes/Form.class.php'); + +if (count($a_cp) > 1) { + $form = new Form(); + + $section = new Form_Section('Captive Portal Zone'); + + $zonelist = array("" => 'None'); + + foreach ($a_cp as $cpkey => $cp) + $zonelist[$cpkey] = $cp['zone']; + + $section->addInput(new Form_Select( + 'zone', + 'Where to show rule descriptions', + $cpzone, + $zonelist + )); + + $form->add($section); + print($form); +} +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Captive Portal Status (")?><?=$a_cp[$cpzone]['zone']?>)</h2></div> + <div class="panel-body table-responsive"> + + <table class="table table-striped table-hover table-condensed"> + +<?php +if (!empty($cpzone)): ?> + + <tr> + <th> + <a href="?zone=<?=htmlspecialchars($cpzone)?>&order=ip&showact=<?=htmlspecialchars($_GET['showact'])?>"><?=gettext("IP address")?></a> + </th> + <th> + <a href="?zone=<?=htmlspecialchars($cpzone)?>&order=mac&showact=<?=htmlspecialchars($_GET['showact'])?>"><?=gettext("MAC address")?></a> + </th> + <th> + <a href="?zone=<?=htmlspecialchars($cpzone)?>&order=user&showact=<?=htmlspecialchars($_GET['showact'])?>"><?=gettext("Username")?></a> + </th> + <th> + <a href="?zone=<?=htmlspecialchars($cpzone)?>&order=start&showact=<?=htmlspecialchars($_GET['showact'])?>"><?=gettext("Session start")?></a> + </th> + +<?php + if ($_GET['showact']): +?> + <th> + <a href="?zone=<?=htmlspecialchars($cpzone)?>&order=lastact&showact=<?=htmlspecialchars($_GET['showact'])?>"><?=gettext("Last activity")?></a> + </th> +<?php + endif; +?> + <th></th> + </tr> +<?php + + foreach ($cpdb as $cpent): ?> + <tr> + <td> + <?=$cpent[2]?> + </td> + <td> +<?php + $mac=trim($cpent[3]); + if (!empty($mac)) { + $mac_hi = strtoupper($mac[0] . $mac[1] . $mac[3] . $mac[4] . $mac[6] . $mac[7]); + print htmlentities($mac); + if(isset($mac_man[$mac_hi])) { + print "<br /><font size=\"-2\"><i>{$mac_man[$mac_hi]}</i></font>"; + } + } +?> + </td> + <td> + <?=htmlspecialchars($cpent[4])?> + </td> +<?php + if ($_GET['showact']): + $last_act = captiveportal_get_last_activity($cpent[2], $cpent[3]); ?> + <td> + <?=htmlspecialchars(date("m/d/Y H:i:s", $cpent[0]))?> + </td> + <td> +<?php + if ($last_act != 0) + echo htmlspecialchars(date("m/d/Y H:i:s", $last_act))?> + </td> +<?php + else: +?> + <td colspan="2"> + <?=htmlspecialchars(date("m/d/Y H:i:s", $cpent[0]))?> + </td> +<?php + endif; +?> + <td> + <a href="?zone=<?=htmlspecialchars($cpzone)?>&order=<?=$_GET['order']?>&showact=<?=htmlspecialchars($_GET['showact'])?>&act=del&id=<?=$cpent[5]?>" class="btn btn-xs brn-danger"><?=gettext("Disconnect")?></a> + </td> + </tr> +<?php + endforeach; +endif; +?> + +</table> + +<form action="status_captiveportal.php" method="get" style="margin: 14px;"> + <input type="hidden" name="order" value="<?=htmlspecialchars($_GET['order'])?>" /> + +<?php +if (!empty($cpzone)): + if ($_GET['showact']): ?> + <input type="hidden" name="showact" value="0" /> + <input type="submit" class="btn btn-default" value="<?=gettext("Don't show last activity")?>" /> +<?php + else: +?> + <input type="hidden" name="showact" value="1" /> + <input type="submit" class="btn btn-default" value="<?=gettext("Show last activity")?>" /> +<?php + endif; +?> + <input type="hidden" name="zone" value="<?=htmlspecialchars($cpzone)?>" /> +<?php +endif; +?> +</form> +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_captiveportal_expire.php b/src/usr/local/www/status_captiveportal_expire.php new file mode 100644 index 0000000..0e0631f --- /dev/null +++ b/src/usr/local/www/status_captiveportal_expire.php @@ -0,0 +1,105 @@ +<?php +/* + Copyright (C) 2007 Marcel Wiget <mwiget@mac.com>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-status-captiveportal-expire +##|*NAME=Status: Captive portal Expire Vouchers page +##|*DESCR=Allow access to the 'Status: Captive portal Expire Vouchers' page. +##|*MATCH=status_captiveportal_expire.php* +##|-PRIV + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); +require_once("voucher.inc"); + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + +if (empty($cpzone)) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +$pgtitle = array(gettext("Status"), gettext("Captive portal"), gettext("Expire Vouchers"), $a_cp[$cpzone]['zone']); + +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("Active Users"), false, "status_captiveportal.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Active Vouchers"), false, "status_captiveportal_vouchers.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Voucher Rolls"), false, "status_captiveportal_voucher_rolls.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Test Vouchers"), false, "status_captiveportal_test.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Expire Vouchers"), true, "status_captiveportal_expire.php?zone={$cpzone}"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form; + +$section = new Form_Section('Expire Vouchers'); + +$section->addInput(new Form_Textarea( + 'vouchers', + 'Vouchers', + $_POST['vouchers'] +))->setHelp('Enter multiple vouchers separated by space or newline. All valid vouchers will be marked as expired.'); + +$section->addInput(new Form_Input( + 'zone', + null, + 'hidden', + $cpzone +)); + +$form->add($section); +print($form); + +if ($_POST) { + if ($_POST['vouchers']) { + if(voucher_expire($_POST['vouchers'])) + print_info_box(gettext('Voucher successfully marked'), 'success'); + else + print_info_box(gettext('Error: Voucher could not be processed'), 'danger'); + } +} + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_captiveportal_test.php b/src/usr/local/www/status_captiveportal_test.php new file mode 100644 index 0000000..6a7993e --- /dev/null +++ b/src/usr/local/www/status_captiveportal_test.php @@ -0,0 +1,114 @@ +<?php +/* + status_captiveportal_test.php + Copyright (C) 2007 Marcel Wiget <mwiget@mac.com>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-status-captiveportal-test +##|*NAME=Status: Captive portal test Vouchers page +##|*DESCR=Allow access to the 'Status: Captive portal Test Vouchers' page. +##|*MATCH=status_captiveportal_test.php* +##|-PRIV + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); +require_once("voucher.inc"); + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + +if (empty($cpzone)) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +$pgtitle = array(gettext("Status"), gettext("Captive portal"), gettext("Test Vouchers"), $a_cp[$cpzone]['zone']); +$shortcut_section = "captiveportal-vouchers"; + +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("Active Users"), false, "status_captiveportal.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Active Vouchers"), false, "status_captiveportal_vouchers.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Voucher Rolls"), false, "status_captiveportal_voucher_rolls.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Test Vouchers"), true, "status_captiveportal_test.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Expire Vouchers"), false, "status_captiveportal_expire.php?zone={$cpzone}"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form; + +$section = new Form_Section('Test Vouchers'); + +$section->addInput(new Form_Textarea( + 'vouchers', + 'Vouchers', + $_POST['vouchers'] +))->setHelp('Enter multiple vouchers separated by space or newline. The remaining time, if valid, will be shown for each voucher.'); + +$section->addInput(new Form_Input( + 'zone', + null, + 'hidden', + $cpzone +)); + +$form->add($section); +print($form); + +if ($_POST) { + if ($_POST['vouchers']) { + $test_results = voucher_auth($_POST['vouchers'], 1); + $output = ""; + + foreach ($test_results as $result) { + if (strpos($result, " good ") || strpos($result, " granted ")) { + $output .= '<font color="green">' . $result . '</font>' . '<br />'; + } else { + $output .= '<font color="red">' . $result . '</font>' . '<br />'; + } + } + + print_info_box($output); + } +} + +include("foot.inc"); diff --git a/src/usr/local/www/status_captiveportal_voucher_rolls.php b/src/usr/local/www/status_captiveportal_voucher_rolls.php new file mode 100644 index 0000000..f7f3ac9 --- /dev/null +++ b/src/usr/local/www/status_captiveportal_voucher_rolls.php @@ -0,0 +1,137 @@ +<?php +/* + status_captiveportal_voucher_rolls.php + + Copyright (C) 2007 Marcel Wiget <mwiget@mac.com>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-status-captiveportal-voucher-rolls +##|*NAME=Status: Captive portal Voucher Rolls page +##|*DESCR=Allow access to the 'Status: Captive portal Voucher Rolls' page. +##|*MATCH=status_captiveportal_voucher_rolls.php* +##|-PRIV + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); +require_once("voucher.inc"); + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + +if (empty($cpzone)) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; +$pgtitle = array(gettext("Status"), gettext("Captive portal"), gettext("Voucher Rolls"), $a_cp[$cpzone]['zone']); +$shortcut_section = "captiveportal-vouchers"; + +if (!is_array($config['voucher'][$cpzone]['roll'])) { + $config['voucher'][$cpzone]['roll'] = array(); +} + +$a_roll = &$config['voucher'][$cpzone]['roll']; + +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("Active Users"), false, "status_captiveportal.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Active Vouchers"), false, "status_captiveportal_vouchers.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Voucher Rolls"), true, "status_captiveportal_voucher_rolls.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Test Vouchers"), false, "status_captiveportal_test.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Expire Vouchers"), false, "status_captiveportal_expire.php?zone={$cpzone}"); +display_top_tabs($tab_array); +?> + +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Roll#"); ?></th> + <th><?=gettext("Minutes/Ticket"); ?></th> + <th><?=gettext("# of Tickets"); ?></th> + <th><?=gettext("Comment"); ?></th> + <th><?=gettext("used"); ?></th> + <th><?=gettext("active"); ?></th> + <th><?=gettext("ready"); ?></th> + </tr> + </thead> + <tbody> +<?php + $voucherlck = lock("vouche{$cpzone}r"); + $i = 0; + foreach($a_roll as $rollent): + $used = voucher_used_count($rollent['number']); + $active = count(voucher_read_active_db($rollent['number']),$rollent['minutes']); + $ready = $rollent['count'] - $used; + /* used also count active vouchers, remove them */ + $used = $used - $active; +?> + <tr> + <td> + <?=htmlspecialchars($rollent['number'])?> + </td> + <td> + <?=htmlspecialchars($rollent['minutes'])?> + </td> + <td> + <?=htmlspecialchars($rollent['count'])?> + </td> + <td> + <?=htmlspecialchars($rollent['comment'])?> + </td> + <td> + <?=htmlspecialchars($used)?> + </td> + <td> + <?=htmlspecialchars($active)?> + </td> + <td> + <?=htmlspecialchars($ready)?> + </td> + </tr> +<?php + $i++; + endforeach; + + unlock($voucherlck)?> + </tbody> + </table> +</div> +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_captiveportal_vouchers.php b/src/usr/local/www/status_captiveportal_vouchers.php new file mode 100644 index 0000000..8ff5c20 --- /dev/null +++ b/src/usr/local/www/status_captiveportal_vouchers.php @@ -0,0 +1,146 @@ +<?php +/* + status_captiveportal_vouchers.php + Copyright (C) 2007 Marcel Wiget <mwiget@mac.com>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: captiveportal +*/ + +##|+PRIV +##|*IDENT=page-status-captiveportal-vouchers +##|*NAME=Status: Captive portal Vouchers page +##|*DESCR=Allow access to the 'Status: Captive portal Vouchers' page. +##|*MATCH=status_captiveportal_vouchers.php* +##|-PRIV + +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require("captiveportal.inc"); +require_once("voucher.inc"); + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + +if (empty($cpzone)) { + header("Location: services_captiveportal_zones.php"); + exit; +} + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; +$pgtitle = array(gettext("Status"), gettext("Captive portal"), gettext("Vouchers"), $a_cp[$cpzone]['zone']); +$shortcut_section = "captiveportal-vouchers"; + +function clientcmp($a, $b) { + global $order; + return strcmp($a[$order], $b[$order]); +} + +if (!is_array($config['voucher'][$cpzone]['roll'])) { + $config['voucher'][$cpzone]['roll'] = array(); +} + +$a_roll = $config['voucher'][$cpzone]['roll']; + +$db = array(); + +foreach ($a_roll as $rollent) { + $roll = $rollent['number']; + $minutes = $rollent['minutes']; + + if (!file_exists("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db")) { + continue; + } + + $active_vouchers = file("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + foreach ($active_vouchers as $voucher => $line) { + list($voucher, $timestamp, $minutes) = explode(",", $line); + $remaining = (($timestamp + 60*$minutes) - time()); + + if ($remaining > 0) { + $dbent[0] = $voucher; + $dbent[1] = $roll; + $dbent[2] = $timestamp; + $dbent[3] = intval($remaining/60); + $dbent[4] = $timestamp + 60*$minutes; // expires at + $db[] = $dbent; + } + } +} + +if ($_GET['order']) { + $order = $_GET['order']; + usort($db, "clientcmp"); +} + +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("Active Users"), false, "status_captiveportal.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Active Vouchers"), true, "status_captiveportal_vouchers.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Voucher Rolls"), false, "status_captiveportal_voucher_rolls.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Test Vouchers"), false, "status_captiveportal_test.php?zone={$cpzone}"); +$tab_array[] = array(gettext("Expire Vouchers"), false, "status_captiveportal_expire.php?zone={$cpzone}"); +display_top_tabs($tab_array); +?> + +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><a href="?order=0&showact=<?=htmlspecialchars($_GET['showact'])?>"><?=gettext("Voucher"); ?></a></th> + <th><a href="?order=1&showact=<?=htmlspecialchars($_GET['showact'])?>"><?=gettext("Roll"); ?></a></th> + <th><a href="?order=2&showact=<?=htmlspecialchars($_GET['showact'])?>"><?=gettext("Activated at"); ?></a></th> + <th><a href="?order=3&showact=<?=htmlspecialchars($_GET['showact'])?>"><?=gettext("Expires in"); ?></a></th> + <th><a href="?order=4&showact=<?=htmlspecialchars($_GET['showact'])?>"><?=gettext("Expires at"); ?></a></th> + </tr> + </thead> + <tbody> +<?php +foreach ($db as $dbent): +?> + <tr> + <td><?=$dbent[0]?></td> + <td><?=$dbent[1]?></td> + <td><?=htmlspecialchars(date("m/d/Y H:i:s", $dbent[2]))?></td> + <td><?=$dbent[3]?><?=gettext("min"); ?></td> + <td><?=htmlspecialchars(date("m/d/Y H:i:s", $dbent[4]))?></td> + </tr> +<?php +endforeach; +?> + </tbody> + </table> +</div> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_dhcp_leases.php b/src/usr/local/www/status_dhcp_leases.php new file mode 100644 index 0000000..c961eca --- /dev/null +++ b/src/usr/local/www/status_dhcp_leases.php @@ -0,0 +1,447 @@ +<?php +/* $Id$ */ +/* + status_dhcp_leases.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2004-2009 Scott Ullrich + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + pfSense_BUILDER_BINARIES: /usr/bin/awk /bin/cat /usr/sbin/arp /usr/bin/wc /usr/bin/grep + pfSense_MODULE: dhcpserver +*/ + +##|+PRIV +##|*IDENT=page-status-dhcpleases +##|*NAME=Status: DHCP leases page +##|*DESCR=Allow access to the 'Status: DHCP leases' page. +##|*MATCH=status_dhcp_leases.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("config.inc"); + +$pgtitle = array(gettext("Status"), gettext("DHCP leases")); +$shortcut_section = "dhcp"; + +$leasesfile = "{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases"; + +if (($_GET['deleteip']) && (is_ipaddr($_GET['deleteip']))) { + /* Stop DHCPD */ + killbyname("dhcpd"); + + /* Read existing leases */ + /* $leases_contents has the lines of the file, including the newline char at the end of each line. */ + $leases_contents = file($leasesfile); + $newleases_contents = array(); + $i = 0; + while ($i < count($leases_contents)) { + /* Find the lease(s) we want to delete */ + if ($leases_contents[$i] == "lease {$_GET['deleteip']} {\n") { + /* Skip to the end of the lease declaration */ + do { + $i++; + } while ($leases_contents[$i] != "}\n"); + } else { + /* It's a line we want to keep, copy it over. */ + $newleases_contents[] = $leases_contents[$i]; + } + $i++; + } + + /* Write out the new leases file */ + $fd = fopen($leasesfile, 'w'); + fwrite($fd, implode("\n", $newleases_contents)); + fclose($fd); + + /* Restart DHCP Service */ + services_dhcpd_configure(); + header("Location: status_dhcp_leases.php?all={$_GET['all']}"); +} + +// Load MAC-Manufacturer table +$mac_man = load_mac_manufacturer_table(); + +include("head.inc"); + +function leasecmp($a, $b) { + return strcmp($a[$_GET['order']], $b[$_GET['order']]); +} + +function adjust_gmt($dt) { + global $config; + $dhcpd = $config['dhcpd']; + foreach ($dhcpd as $dhcpditem) { + $dhcpleaseinlocaltime = $dhcpditem['dhcpleaseinlocaltime']; + if ($dhcpleaseinlocaltime == "yes") { + break; + } + } + if ($dhcpleaseinlocaltime == "yes") { + $ts = strtotime($dt . " GMT"); + if ($ts !== false) { + return strftime("%Y/%m/%d %I:%M:%S%p", $ts); + } + } + /* If we did not need to convert to local time or the conversion failed, just return the input. */ + return $dt; +} + +function remove_duplicate($array, $field) { + foreach ($array as $sub) { + $cmp[] = $sub[$field]; + } + $unique = array_unique(array_reverse($cmp, true)); + foreach ($unique as $k => $rien) { + $new[] = $array[$k]; + } + return $new; +} + +$awk = "/usr/bin/awk"; +/* this pattern sticks comments into a single array item */ +$cleanpattern = "'{ gsub(\"#.*\", \"\");} { gsub(\";\", \"\"); print;}'"; +/* We then split the leases file by } */ +$splitpattern = "'BEGIN { RS=\"}\";} {for (i=1; i<=NF; i++) printf \"%s \", \$i; printf \"}\\n\";}'"; + +/* stuff the leases file in a proper format into a array by line */ +exec("/bin/cat {$leasesfile} | {$awk} {$cleanpattern} | {$awk} {$splitpattern}", $leases_content); +$leases_count = count($leases_content); +exec("/usr/sbin/arp -an", $rawdata); +$arpdata_ip = array(); +$arpdata_mac = array(); +foreach ($rawdata as $line) { + $elements = explode(' ', $line); + if ($elements[3] != "(incomplete)") { + $arpent = array(); + $arpdata_ip[] = trim(str_replace(array('(', ')'), '', $elements[1])); + $arpdata_mac[] = strtolower(trim($elements[3])); + } +} +unset($rawdata); +$pools = array(); +$leases = array(); +$i = 0; +$l = 0; +$p = 0; + +// Put everything together again +foreach ($leases_content as $lease) { + /* split the line by space */ + $data = explode(" ", $lease); + /* walk the fields */ + $f = 0; + $fcount = count($data); + /* with less than 20 fields there is nothing useful */ + if ($fcount < 20) { + $i++; + continue; + } + while ($f < $fcount) { + switch ($data[$f]) { + case "failover": + $pools[$p]['name'] = trim($data[$f+2], '"'); + $pools[$p]['name'] = "{$pools[$p]['name']} (" . convert_friendly_interface_to_friendly_descr(substr($pools[$p]['name'], 5)) . ")"; + $pools[$p]['mystate'] = $data[$f+7]; + $pools[$p]['peerstate'] = $data[$f+14]; + $pools[$p]['mydate'] = $data[$f+10]; + $pools[$p]['mydate'] .= " " . $data[$f+11]; + $pools[$p]['peerdate'] = $data[$f+17]; + $pools[$p]['peerdate'] .= " " . $data[$f+18]; + $p++; + $i++; + continue 3; + case "lease": + $leases[$l]['ip'] = $data[$f+1]; + $leases[$l]['type'] = "dynamic"; + $f = $f+2; + break; + case "starts": + $leases[$l]['start'] = $data[$f+2]; + $leases[$l]['start'] .= " " . $data[$f+3]; + $f = $f+3; + break; + case "ends": + if ($data[$f+1] == "never") { + // Quote from dhcpd.leases(5) man page: + // If a lease will never expire, date is never instead of an actual date. + $leases[$l]['end'] = gettext("Never"); + $f = $f+1; + } else { + $leases[$l]['end'] = $data[$f+2]; + $leases[$l]['end'] .= " " . $data[$f+3]; + $f = $f+3; + } + break; + case "tstp": + $f = $f+3; + break; + case "tsfp": + $f = $f+3; + break; + case "atsfp": + $f = $f+3; + break; + case "cltt": + $f = $f+3; + break; + case "binding": + switch ($data[$f+2]) { + case "active": + $leases[$l]['act'] = "active"; + break; + case "free": + $leases[$l]['act'] = "expired"; + $leases[$l]['online'] = "offline"; + break; + case "backup": + $leases[$l]['act'] = "reserved"; + $leases[$l]['online'] = "offline"; + break; + } + $f = $f+1; + break; + case "next": + /* skip the next binding statement */ + $f = $f+3; + break; + case "rewind": + /* skip the rewind binding statement */ + $f = $f+3; + break; + case "hardware": + $leases[$l]['mac'] = $data[$f+2]; + /* check if it's online and the lease is active */ + if (in_array($leases[$l]['ip'], $arpdata_ip)) { + $leases[$l]['online'] = 'online'; + } else { + $leases[$l]['online'] = 'offline'; + } + $f = $f+2; + break; + case "client-hostname": + if ($data[$f+1] <> "") { + $leases[$l]['hostname'] = preg_replace('/"/', '', $data[$f+1]); + } else { + $hostname = gethostbyaddr($leases[$l]['ip']); + if ($hostname <> "") { + $leases[$l]['hostname'] = $hostname; + } + } + $f = $f+1; + break; + case "uid": + $f = $f+1; + break; + } + $f++; + } + $l++; + $i++; + /* slowly chisel away at the source array */ + array_shift($leases_content); +} +/* remove the old array */ +unset($lease_content); + +/* remove duplicate items by mac address */ +if (count($leases) > 0) { + $leases = remove_duplicate($leases, "ip"); +} + +if (count($pools) > 0) { + $pools = remove_duplicate($pools, "name"); + asort($pools); +} + +foreach ($config['interfaces'] as $ifname => $ifarr) { + if (is_array($config['dhcpd'][$ifname]) && + is_array($config['dhcpd'][$ifname]['staticmap'])) { + $staticmap_array_index = 0; + foreach ($config['dhcpd'][$ifname]['staticmap'] as $static) { + $slease = array(); + $slease['ip'] = $static['ipaddr']; + $slease['type'] = "static"; + $slease['mac'] = $static['mac']; + $slease['if'] = $ifname; + $slease['start'] = ""; + $slease['end'] = ""; + $slease['hostname'] = htmlentities($static['hostname']); + $slease['act'] = "static"; + $slease['online'] = in_array(strtolower($slease['mac']), $arpdata_mac) ? 'online' : 'offline'; + $slease['staticmap_array_index'] = $staticmap_array_index; + $leases[] = $slease; + $staticmap_array_index++; + } + } +} + +if ($_GET['order']) { + usort($leases, "leasecmp"); +} + +/* only print pool status when we have one */ +if (count($pools) > 0) { +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Pool status')?></h2></div> + <div class="panel-body"> + <table class="table"> + <thead> + <tr> + <th><?=gettext("Failover Group")?></a></th> + <th><?=gettext("My State")?></a></th> + <th><?=gettext("Since")?></a></th> + <th><?=gettext("Peer State")?></a></th> + <th><?=gettext("Since")?></a></th> + </tr> + </thead> + <tbody> +<? foreach ($pools as $data):?> + <tr> + <td><?=$data['name']?></td> + <td><?=$data['mystate']?></td> + <td><?=adjust_gmt($data['mydate'])?></td> + <td><?=$data['peerstate']?></td> + <td><?=adjust_gmt($data['peerdate'])?></td> + </tr> +<? endforeach?> + </tbody> + </table> + </div> +</div> +<?php +/* only print pool status when we have one */ +} +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Leases')?></h2></div> + <div class="panel-body"> + <table class="table"> + <thead> + <tr> + <th><!-- icon --></th> + <th><?=gettext("IP address")?></th> + <th><?=gettext("MAC address")?></th> + <th><?=gettext("Hostname")?></th> + <th><?=gettext("Start")?></th> + <th><?=gettext("End")?></th> + <th><?=gettext("Online")?></th> + <th><?=gettext("Lease Type")?></th> + </tr> + </thead> + <tbody> +<?php +foreach ($leases as $data): + if ($data['act'] != "active" && $data['act'] != "static" && $_GET['all'] != 1) + continue; + + if ($data['act'] == 'active') + $icon = 'icon-ok-circle'; + elseif ($data['act'] == 'expired') + $icon = 'icon-ban-circle'; + else + $icon = 'icon-remove-circle'; + + $lip = ip2ulong($data['ip']); + if ($data['act'] != "static") { + foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf) { + if (!is_array($dhcpifconf['range'])) + continue; + if (($lip >= ip2ulong($dhcpifconf['range']['from'])) && ($lip <= ip2ulong($dhcpifconf['range']['to']))) { + $data['if'] = $dhcpif; + break; + } + } + } + + $mac = $data['mac']; + $mac_hi = strtoupper($mac[0] . $mac[1] . $mac[3] . $mac[4] . $mac[6] . $mac[7]); +?> + <tr> + <td><i class="icon <?=$icon?>"></i></td> + <td><?=$data['ip']?></td> + <td> + <?=$mac?> + + <? if(isset($mac_man[$mac_hi])):?> + (<?=$mac_man[$mac_hi]?>) + <?endif?> + </td> + <td><?=htmlentities($data['hostname'])?></td> +<? if ($data['type'] != "static"):?> + <td><?=adjust_gmt($data['start'])?></td> + <td><?=adjust_gmt($data['end'])?></td> +<? else: ?> + <td>n/a</td> + <td>n/a</td> +<? endif; ?> + <td><?=$data['online']?></td> + <td><?=$data['act']?></td> + <td> +<? if ($data['type'] == "dynamic"): ?> + <a class="btn btn-xs btn-primary" href="services_dhcp_edit.php?if=<?=$data['if']?>&mac=<?=$data['mac']?>&hostname=<?=htmlspecialchars($data['hostname'])?>"> + <?=gettext("add static mapping")?> + </a> +<? else: ?> + <a class="btn btn-xs btn-primary" href="services_dhcp_edit.php?if=<?=$data['if']?>&id=<?=$data['staticmap_array_index']?>"> + <?=gettext("edit static mapping")?> + </a> +<? endif; ?> + + <a class="btn btn-xs btn-success" href="services_wol_edit.php?if=<?=$data['if']?>&mac=<?=$data['mac']?>&descr=<?=htmlentities($data['hostname'])?>"> + add WOL mapping + </a> + +<? if ($data['online'] != "online"):?> + <a class="btn btn-xs btn-warning" href="services_wol.php?if=<?=$data['if']?>&mac=<?=$data['mac']?>"> + send WOL packet + </a> +<? endif; ?> + +<? if ($data['type'] == "dynamic" && $data['online'] != "online"):?> + <a class="btn btn-xs btn-danger" href="status_dhcp_leases.php?deleteip=<?=$data['ip']?>&all=<?=intval($_GET['all'])?>"> + delete lease + </a> +<? endif?> + </td> +<? endforeach; ?> + </tr> + </tbody> + </table> + </div> +</div> + +<?php if ($_GET['all']): ?> + <a class="btn btn-default" href="status_dhcp_leases.php?all=0"><?=gettext("Show active and static leases only")?></a> +<?php else: ?> + <a class="btn btn-default" href="status_dhcp_leases.php?all=1"><?=gettext("Show all configured leases")?></a> +<?php endif; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_dhcpv6_leases.php b/src/usr/local/www/status_dhcpv6_leases.php new file mode 100644 index 0000000..925f2da --- /dev/null +++ b/src/usr/local/www/status_dhcpv6_leases.php @@ -0,0 +1,595 @@ +<?php +/* $Id$ */ +/* + status_dhcpv6_leases.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2011 Seth Mos + Copyright (C) 2004-2009 Scott Ullrich + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + pfSense_BUILDER_BINARIES: /usr/bin/awk /bin/cat /usr/sbin/ndp /usr/bin/wc /usr/bin/grep + pfSense_MODULE: dhcpserver +*/ + +##|+PRIV +##|*IDENT=page-status-dhcpv6leases +##|*NAME=Status: DHCPv6 leases page +##|*DESCR=Allow access to the 'Status: DHCPv6 leases' page. +##|*MATCH=status_dhcpv6_leases.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("config.inc"); + +$pgtitle = array(gettext("Status"), gettext("DHCPv6 leases")); +$shortcut_section = "dhcp6"; + +$leasesfile = "{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases"; + +if (($_GET['deleteip']) && (is_ipaddr($_GET['deleteip']))) { + /* Stop DHCPD */ + killbyname("dhcpd"); + + /* Read existing leases */ + $leases_contents = explode("\n", file_get_contents($leasesfile)); + $newleases_contents = array(); + $i = 0; + while ($i < count($leases_contents)) { + /* Find the lease(s) we want to delete */ + if ($leases_contents[$i] == " iaaddr {$_GET['deleteip']} {") { + /* The iaaddr line is two lines down from the start of the lease, so remove those two lines. */ + array_pop($newleases_contents); + array_pop($newleases_contents); + /* Skip to the end of the lease declaration */ + do { + $i++; + } while ($leases_contents[$i] != "}"); + } else { + /* It's a line we want to keep, copy it over. */ + $newleases_contents[] = $leases_contents[$i]; + } + $i++; + } + + /* Write out the new leases file */ + $fd = fopen($leasesfile, 'w'); + fwrite($fd, implode("\n", $newleases_contents)); + fclose($fd); + + /* Restart DHCP Service */ + services_dhcpd_configure(); + header("Location: status_dhcpv6_leases.php?all={$_GET['all']}"); +} + +// Load MAC-Manufacturer table +$mac_man = load_mac_manufacturer_table(); + +include("head.inc"); + +function leasecmp($a, $b) { + return strcmp($a[$_GET['order']], $b[$_GET['order']]); +} + +function adjust_gmt($dt) { + global $config; + + $dhcpv6leaseinlocaltime == "no"; + if (is_array($config['dhcpdv6'])) { + $dhcpdv6 = $config['dhcpdv6']; + foreach ($dhcpdv6 as $dhcpv6leaseinlocaltime) { + $dhcpv6leaseinlocaltime = $dhcpv6leaseinlocaltime['dhcpv6leaseinlocaltime']; + if ($dhcpv6leaseinlocaltime == "yes") { + break; + } + } + } + + $timezone = $config['system']['timezone']; + $ts = strtotime($dt . " GMT"); + if ($dhcpv6leaseinlocaltime == "yes") { + $this_tz = new DateTimeZone($timezone); + $dhcp_lt = new DateTime(strftime("%I:%M:%S%p", $ts), $this_tz); + $offset = $this_tz->getOffset($dhcp_lt); + $ts = $ts + $offset; + return strftime("%Y/%m/%d %I:%M:%S%p", $ts); + } else { + return strftime("%Y/%m/%d %H:%M:%S", $ts); + } +} + +function remove_duplicate($array, $field) { + foreach ($array as $sub) { + $cmp[] = $sub[$field]; + } + $unique = array_unique(array_reverse($cmp, true)); + foreach ($unique as $k => $rien) { + $new[] = $array[$k]; + } + return $new; +} + +function parse_duid($duid_string) { + $parsed_duid = array(); + for ($i = 0; $i < strlen($duid_string); $i++) { + $s = substr($duid_string, $i, 1); + if ($s == '\\') { + $n = substr($duid_string, $i+1, 1); + if (($n == '\\') || ($n == '"')) { + $parsed_duid[] = sprintf("%02x", ord($n)); + } elseif (is_numeric($n)) { + $parsed_duid[] = sprintf("%02x", octdec(substr($duid_string, $i+1, 3))); + $i += 3; + } + } else { + $parsed_duid[] = sprintf("%02x", ord($s)); + } + } + $iaid = array_slice($parsed_duid, 0, 4); + $duid = array_slice($parsed_duid, 4); + return array($iaid, $duid); +} + +$awk = "/usr/bin/awk"; + +/* this pattern sticks comments into a single array item */ +$cleanpattern = "'{ gsub(\"^#.*\", \"\");} { gsub(\"^server-duid.*\", \"\");} { gsub(\";$\", \"\"); print;}'"; +/* We then split the leases file by } */ +$splitpattern = "'BEGIN { RS=\"}\";} {for (i=1; i<=NF; i++) printf \"%s \", \$i; printf \"}\\n\";}'"; + +/* stuff the leases file in a proper format into a array by line */ +exec("/bin/cat {$leasesfile} | {$awk} {$cleanpattern} | {$awk} {$splitpattern} | /usr/bin/grep '^ia-.. '", $leases_content); +$leases_count = count($leases_content); +exec("/usr/sbin/ndp -an", $rawdata); +$ndpdata = array(); +foreach ($rawdata as $line) { + $elements = preg_split('/\s+/ ', $line); + if ($elements[1] != "(incomplete)") { + $ndpent = array(); + $ip = trim(str_replace(array('(', ')'), '', $elements[0])); + $ndpent['mac'] = trim($elements[1]); + $ndpent['interface'] = trim($elements[2]); + $ndpdata[$ip] = $ndpent; + } +} + +$pools = array(); +$leases = array(); +$prefixes = array(); +$mappings = array(); +$i = 0; +$l = 0; +$p = 0; + +// Put everything together again +while ($i < $leases_count) { + $entry = array(); + /* split the line by space */ + $duid_split = array(); + preg_match('/ia-.. "(.*)" { (.*)/ ', $leases_content[$i], $duid_split); + if (!empty($duid_split[1])) { + $iaid_duid = parse_duid($duid_split[1]); + $entry['iaid'] = hexdec(implode("", array_reverse($iaid_duid[0]))); + $entry['duid'] = implode(":", $iaid_duid[1]); + $data = explode(" ", $duid_split[2]); + } else { + $data = explode(" ", $leases_content[$i]); + } + /* walk the fields */ + $f = 0; + $fcount = count($data); + /* with less then 12 fields there is nothing useful */ + if ($fcount < 12) { + $i++; + continue; + } + while ($f < $fcount) { + switch ($data[$f]) { + case "failover": + $pools[$p]['name'] = $data[$f+2]; + $pools[$p]['mystate'] = $data[$f+7]; + $pools[$p]['peerstate'] = $data[$f+14]; + $pools[$p]['mydate'] = $data[$f+10]; + $pools[$p]['mydate'] .= " " . $data[$f+11]; + $pools[$p]['peerdate'] = $data[$f+17]; + $pools[$p]['peerdate'] .= " " . $data[$f+18]; + $p++; + $i++; + continue 3; + case "ia-pd": + $is_prefix = true; + case "ia-na": + $entry['iaid'] = $tmp_iaid; + $entry['duid'] = $tmp_duid; + if ($data[$f+1][0] == '"') { + $duid = ""; + /* FIXME: This needs a safety belt to prevent an infinite loop */ + while ($data[$f][strlen($data[$f])-1] != '"') { + $duid .= " " . $data[$f+1]; + $f++; + } + $entry['duid'] = $duid; + } else { + $entry['duid'] = $data[$f+1]; + } + $entry['type'] = "dynamic"; + $f = $f+2; + break; + case "iaaddr": + $entry['ip'] = $data[$f+1]; + $entry['type'] = "dynamic"; + if (in_array($entry['ip'], array_keys($ndpdata))) { + $entry['online'] = 'online'; + } else { + $entry['online'] = 'offline'; + } + $f = $f+2; + break; + case "iaprefix": + $is_prefix = true; + $entry['prefix'] = $data[$f+1]; + $entry['type'] = "dynamic"; + $f = $f+2; + break; + case "starts": + $entry['start'] = $data[$f+2]; + $entry['start'] .= " " . $data[$f+3]; + $f = $f+3; + break; + case "ends": + $entry['end'] = $data[$f+2]; + $entry['end'] .= " " . $data[$f+3]; + $f = $f+3; + break; + case "tstp": + $f = $f+3; + break; + case "tsfp": + $f = $f+3; + break; + case "atsfp": + $f = $f+3; + break; + case "cltt": + $entry['start'] = $data[$f+2]; + $entry['start'] .= " " . $data[$f+3]; + $f = $f+3; + break; + case "binding": + switch ($data[$f+2]) { + case "active": + $entry['act'] = "active"; + break; + case "free": + $entry['act'] = "expired"; + $entry['online'] = "offline"; + break; + case "backup": + $entry['act'] = "reserved"; + $entry['online'] = "offline"; + break; + case "released": + $entry['act'] = "released"; + $entry['online'] = "offline"; + } + $f = $f+1; + break; + case "next": + /* skip the next binding statement */ + $f = $f+3; + break; + case "hardware": + $f = $f+2; + break; + case "client-hostname": + if($data[$f+1] != "") { + $entry['hostname'] = preg_replace('/"/','',$data[$f+1]); + } else { + $hostname = gethostbyaddr($entry['ip']); + if($hostname != "") { + $entry['hostname'] = $hostname; + } + } + $f = $f+1; + break; + case "uid": + $f = $f+1; + break; + } + $f++; + } + if ($is_prefix) { + $prefixes[] = $entry; + } else { + $leases[] = $entry; + $mappings[$entry['iaid'] . $entry['duid']] = $entry['ip']; + } + $l++; + $i++; + $is_prefix = false; +} + +if (count($leases) > 0) { + $leases = remove_duplicate($leases, "ip"); +} + +if (count($prefixes) > 0) { + $prefixes = remove_duplicate($prefixes, "prefix"); +} + +if (count($pools) > 0) { + $pools = remove_duplicate($pools, "name"); + asort($pools); +} + +foreach ($config['interfaces'] as $ifname => $ifarr) { + if (is_array($config['dhcpdv6'][$ifname]) && + is_array($config['dhcpdv6'][$ifname]['staticmap'])) { + foreach ($config['dhcpdv6'][$ifname]['staticmap'] as $static) { + $slease = array(); + $slease['ip'] = $static['ipaddrv6']; + $slease['type'] = "static"; + $slease['duid'] = $static['duid']; + $slease['start'] = ""; + $slease['end'] = ""; + $slease['hostname'] = htmlentities($static['hostname']); + $slease['act'] = "static"; + if (in_array($slease['ip'], array_keys($ndpdata))) { + $slease['online'] = 'online'; + } else { + $slease['online'] = 'offline'; + } + + $leases[] = $slease; + } + } +} + +if ($_GET['order']) { + usort($leases, "leasecmp"); +} + +/* only print pool status when we have one */ +if (count($pools) > 0) { +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Pool status')?></h2></div> + <div class="panel-body"> + <table class="table"> + <thead> + <tr> + <th><?=gettext("Failover Group")?></a></th> + <th><?=gettext("My State")?></a></th> + <th><?=gettext("Since")?></a></th> + <th><?=gettext("Peer State")?></a></th> + <th><?=gettext("Since")?></a></th> + </tr> + </thead> + <tbody> +<? foreach ($pools as $data):?> + <tr> + <td><?=$data['name']?></td> + <td><?=$data['mystate']?></td> + <td><?=adjust_gmt($data['mydate'])?></td> + <td><?=$data['peerstate']?></td> + <td><?=adjust_gmt($data['peerdate'])?></td> + </tr> +<? endforeach?> + </tbody> + </table> + </div> +</div> +<?php +/* only print pool status when we have one */ +} + +if (empty($leases)) + print '<div class="alert alert-warning" role="alert">'. gettext("No leases file found. Is the DHCP server active?") .'</div>'; + +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Leases')?></h2></div> + <div class="panel-body"> + <table class="table"> + <thead> + <tr> + <th><!-- icon --></th> + <th><?=gettext("IPv6 address")?></th> + <th><?=gettext("IAID")?></th> + <th><?=gettext("DUID")?></th> + <th><?=gettext("MAC address")?></th> + <th><?=gettext("Hostname")?></th> + <th><?=gettext("Start")?></th> + <th><?=gettext("End")?></th> + <th><?=gettext("Online")?></th> + <th><?=gettext("Lease Type")?></th> + </tr> + </thead> + <tbody> +<?php +foreach ($leases as $data): + if ($data['act'] != "active" && $data['act'] != "static" && $_GET['all'] != 1) + continue; + + if ($data['act'] == 'active') + $icon = 'icon-ok-circle'; + elseif ($data['act'] == 'expired') + $icon = 'icon-ban-circle'; + else + $icon = 'icon-remove-circle'; + + if ($data['act'] == "static") { + foreach ($config['dhcpdv6'] as $dhcpif => $dhcpifconf) { + if(is_array($dhcpifconf['staticmap'])) { + foreach ($dhcpifconf['staticmap'] as $staticent) { + if ($data['ip'] == $staticent['ipaddr']) { + $data['if'] = $dhcpif; + break; + } + } + } + /* exit as soon as we have an interface */ + if ($data['if'] != "") + break; + } + } else { + $data['if'] = convert_real_interface_to_friendly_interface_name(guess_interface_from_ip($data['ip'])); + } + + $mac = trim($ndpdata[$data['ip']]['mac']); + $mac_hi = strtoupper($mac[0] . $mac[1] . $mac[3] . $mac[4] . $mac[6] . $mac[7]); +?> + <tr> + <td><i class="icon <?=$icon?>"></i></td> + <td><?=$data['ip']?></td> + <td><?=$data['iaid']?></td> + <td><?=$data['duid']?></td> + <td> + <?=$mac?> + + <? if(isset($mac_man[$mac_hi])):?> + (<?=$mac_man[$mac_hi]?>) + <?endif?> + </td> + <td><?=htmlentities($data['hostname'])?></td> +<? if ($data['type'] != "static"):?> + <td><?=adjust_gmt($data['start'])?></td> + <td><?=adjust_gmt($data['end'])?></td> +<? else: ?> + <td>n/a</td> + <td>n/a</td> +<? endif; ?> + <td><?=$data['online']?></td> + <td><?=$data['act']?></td> + <td> +<? if ($data['type'] == "dynamic"): ?> + <a class="btn btn-xs btn-primary" href="services_dhcpv6_edit.php?if=<?=$data['if']?>&duid=<?=$data['duid']?>&hostname=<?=htmlspecialchars($data['hostname'])?>"> + <?=gettext("add static mapping")?> + </a> +<? endif; ?> + + <a class="btn btn-xs btn-success" href="services_wol_edit.php?if=<?=$data['if']?>&mac=<?=$data['mac']?>&descr=<?=htmlentities($data['hostname'])?>"> + add WOL mapping + </a> + +<? if ($data['type'] == "dynamic" && $data['online'] != "online"):?> + <a class="btn btn-xs btn-danger" href="status_dhcpv6_leases.php?deleteip=<?=$data['ip']?>&all=<?=intval($_GET['all'])?>"> + delete lease + </a> +<? endif?> + </td> +<? endforeach; ?> + </tr> + </tbody> + </table> + </div> +</div> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Delegated Prefixes')?></h2></div> + <div class="panel-body"> + <table class="table"> + <thead> + <tr> + <th><?=gettext("IPv6 Prefix")?></th> + <th><?=gettext("IAID")?></th> + <th><?=gettext("DUID")?></th> + <th><?=gettext("Start")?></th> + <th><?=gettext("End")?></th> + <th><?=gettext("State")?></th> + </tr> + </thead> + <tbody> +<?php +foreach ($prefixes as $data): + if ($data['act'] != "active" && $data['act'] != "static" && $_GET['all'] != 1) + continue; + + if ($data['act'] == 'active') + $icon = 'icon-ok-circle'; + elseif ($data['act'] == 'expired') + $icon = 'icon-ban-circle'; + else + $icon = 'icon-remove-circle'; + + if ($data['act'] == "static") { + foreach ($config['dhcpdv6'] as $dhcpif => $dhcpifconf) { + if(is_array($dhcpifconf['staticmap'])) { + foreach ($dhcpifconf['staticmap'] as $staticent) { + if ($data['ip'] == $staticent['ipaddr']) { + $data['if'] = $dhcpif; + break; + } + } + } + /* exit as soon as we have an interface */ + if ($data['if'] != "") + break; + } + } else { + $data['if'] = convert_real_interface_to_friendly_interface_name(guess_interface_from_ip($data['ip'])); + } + + { + $dip = ""; + } +?> + <tr> + <td><i class="icon <?=$icon?>"></i></td> + <td> + <?=$data['prefix']?> +<? if ($mappings[$data['iaid'] . $data['duid']]): ?> + <br /> + <?=gettext('Routed To')?>: <?=$mappings[$data['iaid'] . $data['duid']]?> +<? endif; ?> + </td> + <td><?=$data['iaid']?></td> + <td><?=$data['duid']?></td> +<? if ($data['type'] != "static"):?> + <td><?=adjust_gmt($data['start'])?></td> + <td><?=adjust_gmt($data['end'])?></td> +<? else: ?> + <td>n/a</td> + <td>n/a</td> +<? endif; ?> + <td><?=$data['act']?></td> +<? endforeach; ?> + </tr> + </tbody> + </table> + </div> +</div> + +<?php if ($_GET['all']): ?> + <a class="btn btn-default" href="status_dhcpv6_leases.php?all=0"><?=gettext("Show active and static leases only")?></a> +<?php else: ?> + <a class="btn btn-default" href="status_dhcpv6_leases.php?all=1"><?=gettext("Show all configured leases")?></a> +<?php endif; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_filter_reload.php b/src/usr/local/www/status_filter_reload.php new file mode 100644 index 0000000..434336a --- /dev/null +++ b/src/usr/local/www/status_filter_reload.php @@ -0,0 +1,185 @@ +<?php +/* $Id$ */ +/* + status_filter_reload.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2006 Scott Ullrich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: filter +*/ + +##|+PRIV +##|*IDENT=page-status-filterreloadstatus +##|*NAME=Status: Filter Reload Status page +##|*DESCR=Allow access to the 'Status: Filter Reload Status' page. +##|*MATCH=status_filter_reload.php* +##|-PRIV + +require_once("globals.inc"); +require_once("guiconfig.inc"); +require_once("functions.inc"); + +$pgtitle = array(gettext("Status"), gettext("Filter Reload Status")); +$shortcut_section = "firewall"; + +if (file_exists("{$g['varrun_path']}/filter_reload_status")) { + $status = file_get_contents("{$g['varrun_path']}/filter_reload_status"); +} + +if ($_GET['getstatus']) { + echo "|{$status}|"; + exit; +} +if ($_POST['reloadfilter']) { + send_event("filter reload"); + header("Location: status_filter_reload.php"); + exit; +} +if ($_POST['syncfilter']) { + send_event("filter sync"); + header("Location: status_filter_reload.php"); + exit; +} + +include("head.inc"); +?> + + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">Filter Reload</h2></div> + <div class="panel-body"> + <form action="status_filter_reload.php" method="post" name="filter"> + <input type="submit" class="btn btn-success" value="Reload Filter" name="reloadfilter" id="reloadfilter" /> +<?php +if ($config['hasync'] && $config['hasync']["synchronizetoip"] != ""): ?> + < input type="submit" class="btn btn-default" value="Force Config Sync" name="syncfilter" id="syncfilter" /> +<?php +endif; +?> + </form> + + <br /> + + <div id="status" class="panel panel-default"> + <?=$status; ?> + </div> + + <div id="doneurl"> + </div> + + <br/> + + <div id="reloadinfo"><?=gettext("This page will automatically refresh every 3 seconds until the filter is done reloading"); ?>.</div> + + </div> +</div> + +<script type="text/javascript"> +//<![CDATA[ +/* init update "thread */ +function update_status_thread() { + getURL('status_filter_reload.php?getstatus=true', update_data); +} + +function update_data(obj) { + var result_text = obj.content; + var result_text_split = result_text.split("|"); + result_text = result_text_split[1]; + result_text = result_text.replace("\n", ""); + result_text = result_text.replace("\r", ""); + if (result_text) { + jQuery('#status').html('<img src="/themes/<?=$g['theme']?>/images/misc/loader.gif" alt="loader" /> ' + result_text + '...'); + } else { + jQuery('#status').html('<img src="/themes/<?=$g['theme']?>/images/misc/loader.gif" alt="loader" /> Obtaining filter status...'); + } + if (result_text == "Initializing") { + jQuery('#status').html('<img src="/themes/<?=$g['theme'];?>/images/misc/loader.gif" alt="loader" /> Initializing...'); + } else if (result_text == "Done") { + jQuery('#status').effect('highlight'); + jQuery('#status').html('Done. The filter rules have been reloaded.'); + jQuery('#reloadinfo').css("visibility", "hidden"); + jQuery('#doneurl').css("visibility", "visible"); + jQuery('#doneurl').html("<p><a href='status_queues.php'>Queue Status<\/a><\/p>"); + } + window.setTimeout('update_status_thread()', 2500); +} +//]]> +</script> + +<script type="text/javascript"> +//<![CDATA[ +/* + * getURL is a proprietary Adobe function, but it's simplicity has made it very + * popular. If getURL is undefined we spin our own by wrapping XMLHttpRequest. + */ +if (typeof getURL == 'undefined') { + getURL = function(url, callback) { + if (!url) { + throw 'No URL for getURL'; + } + + try { + if (typeof callback.operationComplete == 'function') { + callback = callback.operationComplete; + } + } catch (e) {} + if (typeof callback != 'function') { + throw 'No callback function for getURL'; + } + + var http_request = null; + if (typeof XMLHttpRequest != 'undefined') { + http_request = new XMLHttpRequest(); + } else if (typeof ActiveXObject != 'undefined') { + try { + http_request = new ActiveXObject('Msxml2.XMLHTTP'); + } catch (e) { + try { + http_request = new ActiveXObject('Microsoft.XMLHTTP'); + } catch (e) {} + } + } + if (!http_request) { + throw 'Both getURL and XMLHttpRequest are undefined'; + } + + http_request.onreadystatechange = function() { + if (http_request.readyState == 4) { + callback( { success : true, + content : http_request.responseText, + contentType : http_request.getResponseHeader("Content-Type") } ); + } + } + http_request.open('GET', url, true); + http_request.send(null); + } +} + +window.setTimeout('update_status_thread()', 2500); +//]]> +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_gateway_groups.php b/src/usr/local/www/status_gateway_groups.php new file mode 100755 index 0000000..fa57d66 --- /dev/null +++ b/src/usr/local/www/status_gateway_groups.php @@ -0,0 +1,182 @@ +<?php +/* $Id$ */ +/* + status_gateway_groups.php + part of pfSense (https://www.pfsense.org) + + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-status-gatewaygroups +##|*NAME=Status: Gateway Groups page +##|*DESCR=Allow access to the 'Status: Gateway Groups' page. +##|*MATCH=status_gateway_groups.php* +##|-PRIV + +define('COLOR', true); +define('LIGHTGREEN', '#90EE90'); +define('LIGHTCORAL', '#F08080'); +define('KHAKI', '#F0E68C'); +define('LIGHTGRAY', '#D3D3D3'); +define('LIGHTBLUE', '#ADD8E6'); +define('WHITE', '#FFFFFF'); + +require("guiconfig.inc"); + +if (!is_array($config['gateways']['gateway_group'])) { + $config['gateways']['gateway_group'] = array(); +} + +$a_gateway_groups = &$config['gateways']['gateway_group']; +$changedesc = gettext("Gateway Groups") . ": "; + +$gateways_status = return_gateways_status(); + +$pgtitle = array(gettext("Status"), gettext("Gateway Groups")); +$shortcut_section = "gateway-groups"; +include("head.inc"); + +$tab_array = array(); +$tab_array[0] = array(gettext("Gateways"), false, "status_gateways.php"); +$tab_array[1] = array(gettext("Gateway Groups"), true, "status_gateway_groups.php"); +display_top_tabs($tab_array); +?> + +<div class="table-responsive"> + <table class="table table-hover table-condensed table-striped"> + <thead> + <tr> + <th><?=gettext("Group Name"); ?></th> + <th><?=gettext("Gateways"); ?></th> + <th><?=gettext("Description"); ?></th> + </tr> + </thead> + <tbody> + <?php foreach ($a_gateway_groups as $gateway_group): ?> + <tr> + <td> + <?=htmlspecialchars($gateway_group['name'])?> + </td> + <td> + <table class="table table-bordered table-condensed"> +<?php + /* process which priorities we have */ + $priorities = array(); + foreach($gateway_group['item'] as $item) { + $itemsplit = explode("|", $item); + $priorities[$itemsplit[1]] = true; + } + $priority_count = count($priorities); + ksort($priorities); +?> + <thead> + <tr> +<?php + // Make a column for each tier + foreach($priorities as $number => $tier) { + echo "<th>" . sprintf(gettext("Tier %s"), $number) . "</th>"; + } +?> + </tr> + </thead> + <tbody> +<?php + /* inverse gateway group to gateway priority */ + $priority_arr = array(); + foreach($gateway_group['item'] as $item) { + $itemsplit = explode("|", $item); + $priority_arr[$itemsplit[1]][] = $itemsplit[0]; + } + ksort($priority_arr); + $p = 1; + foreach($priority_arr as $number => $tier) { + /* for each priority process the gateways */ + foreach($tier as $member) { + /* we always have $priority_count fields */ +?> + <tr> +<?php + $c = 1; + while($c <= $priority_count) { + $monitor = lookup_gateway_monitor_ip_by_name($member); + if($p == $c) { + $status = $gateways_status[$monitor]['status']; + if (stristr($status, "down")) { + $online = gettext("Offline"); + $bgcolor = LIGHTCORAL; + } elseif (stristr($status, "loss")) { + $online = gettext("Warning, Packetloss"); + $bgcolor = KHAKI; + } elseif (stristr($status, "delay")) { + $online = gettext("Warning, Latency"); + $bgcolor = KHAKI; + } elseif ($status == "none") { + $online = gettext("Online"); + $bgcolor = LIGHTGREEN; + } else { + $online = gettext("Gathering data"); + $bgcolor = LIGHTBLUE; + } + + if(!COLOR) + $bgcolor = WHITE; +?> + <td bgcolor="<?=$bgcolor?>"> + <?=htmlspecialchars($member);?>,<br /><?=$online?> + </td> + +<?php + } else { +?> + <td> + </td> +<?php } + $c++; + } +?> + </tr> +<?php + } + $p++; + } +?> + </tbody> + </table> + </td> + <td> + <?=htmlspecialchars($gateway_group['descr'])?> + </td> + </tr> + <?php endforeach; ?> + </tbody> + </table> +</div> + +<?php include("foot.inc"); diff --git a/src/usr/local/www/status_gateways.php b/src/usr/local/www/status_gateways.php new file mode 100644 index 0000000..71d25d2 --- /dev/null +++ b/src/usr/local/www/status_gateways.php @@ -0,0 +1,166 @@ +<?php +/* $Id$ */ +/* + status_gateways.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-status-gateways +##|*NAME=Status: Gateways page +##|*DESCR=Allow access to the 'Status: Gateways' page. +##|*MATCH=status_gateways.php* +##|-PRIV + +require("guiconfig.inc"); + +define('COLOR', true); +define('LIGHTGREEN', '#90EE90'); +define('LIGHTCORAL', '#F08080'); +define('KHAKI', '#F0E68C'); +define('LIGHTGRAY', '#D3D3D3'); +define('WHITE', '#FFFFFF'); + +$a_gateways = return_gateways_array(); +$gateways_status = array(); +$gateways_status = return_gateways_status(true); + +$now = time(); +$year = date("Y"); + +$pgtitle = array(gettext("Status"), gettext("Gateways")); +$shortcut_section = "gateways"; +include("head.inc"); + +/* active tabs */ +$tab_array = array(); +$tab_array[] = array(gettext("Gateways"), true, "status_gateways.php"); +$tab_array[] = array(gettext("Gateway Groups"), false, "status_gateway_groups.php"); +display_top_tabs($tab_array); +?> + +<div class="table-responsive"> + <table class="table table-hover table-compact table-striped"> + <thead> + <tr> + <th><?=gettext("Name"); ?></th> + <th><?=gettext("Gateway"); ?></th> + <th><?=gettext("Monitor"); ?></th> + <th><?=gettext("RTT"); ?></th> + <th><?=gettext("Loss"); ?></th> + <th><?=gettext("Status"); ?></th> + <th><?=gettext("Description"); ?></th> + </tr> + </thead> + <tbody> +<?php foreach ($a_gateways as $gname => $gateway) { +?> + <tr> + <td> + <?=$gateway['name'];?> + </td> + <td> + <?php echo lookup_gateway_ip_by_name($gname);?> + </td> + <td> +<?php if ($gateways_status[$gname]) + echo $gateways_status[$gname]['monitorip']; + else + echo $gateway['monitorip']; +?> + </td> + <td> +<?php if ($gateways_status[$gname]) + echo $gateways_status[$gname]['delay']; + else + echo gettext("Pending"); +?> + <?php $counter++; ?> + </td> + <td> +<?php if ($gateways_status[$gname]) + echo $gateways_status[$gname]['loss']; + else + echo gettext("Pending"); + + $counter++; +?> + </td> +<?php + if ($gateways_status[$gname]) { + $status = $gateways_status[$gname]; + if (stristr($status['status'], "force_down")) { + $online = gettext("Offline (forced)"); + $bgcolor = LIGHTCORAL; + } elseif (stristr($status['status'], "down")) { + $online = gettext("Offline"); + $bgcolor = LIGHTCORAL; + } elseif (stristr($status['status'], "loss")) { + $online = gettext("Warning, Packetloss").': '.$status['loss']; + $bgcolor = KHAKI; + } elseif (stristr($status['status'], "delay")) { + $online = gettext("Warning, Latency").': '.$status['delay']; + $bgcolor = KHAKI; + } elseif ($status['status'] == "none") { + $online = gettext("Online"); + $bgcolor = LIGHTGREEN; + } + } else if (isset($gateway['monitor_disable'])) { + $online = gettext("Online"); + $bgcolor = LIGHTGREEN; + } else { + $online = gettext("Pending"); + $bgcolor = LIGHTGRAY; + } + + $lastchange = $gateways_status[$gname]['lastcheck']; + + if(!COLOR) + $bgcolor = WHITE; +?> + + <td bgcolor="<?=$bgcolor?>"> + <strong><?=$online?></strong> <?php + if(!empty($lastchange)) { ?> + <br /><i>Last checked <?=$lastchange?></i> +<?php } ?> + </td> + + <td> + <?=$gateway['descr']; ?> + </td> + </tr> +<?php } ?> <!-- End-of-foreach --> + </tbody> + </table> +</div> + +<?php include("foot.inc"); ?> diff --git a/src/usr/local/www/status_graph.php b/src/usr/local/www/status_graph.php new file mode 100644 index 0000000..5f2dbbb --- /dev/null +++ b/src/usr/local/www/status_graph.php @@ -0,0 +1,263 @@ +<?php +/* $Id$ */ +/* + status_graph.php + Part of pfSense + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2004 Scott Ullrich + All rights reserved. + + Originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-status-trafficgraph +##|*NAME=Status: Traffic Graph page +##|*DESCR=Allow access to the 'Status: Traffic Graph' page. +##|*MATCH=status_graph.php* +##|*MATCH=bandwidth_by_ip.php* +##|*MATCH=graph.php* +##|*MATCH=ifstats.php* +##|-PRIV + +require("guiconfig.inc"); + +if ($_POST['width']) { + $width = $_POST['width']; +} else { + $width = "100%"; +} + +if ($_POST['height']) { + $height = $_POST['height']; +} else { + $height = "200"; +} + +// Get configured interface list +$ifdescrs = get_configured_interface_with_descr(); +if (isset($config['ipsec']['enable'])) { + $ifdescrs['enc0'] = "IPsec"; +} +foreach (array('server', 'client') as $mode) { + if (is_array($config['openvpn']["openvpn-{$mode}"])) { + foreach ($config['openvpn']["openvpn-{$mode}"] as $id => $setting) { + if (!isset($setting['disable'])) { + $ifdescrs['ovpn' . substr($mode, 0, 1) . $setting['vpnid']] = gettext("OpenVPN") . " ".$mode.": ".htmlspecialchars($setting['description']); + } + } + } +} + +if ($_POST['if']) { + $curif = $_POST['if']; + $found = false; + foreach ($ifdescrs as $descr => $ifdescr) { + if ($descr == $curif) { + $found = true; + break; + } + } + if ($found === false) { + header("Location: status_graph.php"); + exit; + } +} else { + if (empty($ifdescrs["wan"])) { + /* Handle the case when WAN has been disabled. Use the first key in ifdescrs. */ + reset($ifdescrs); + $curif = key($ifdescrs); + } else { + $curif = "wan"; + } +} +if ($_POST['sort']) { + $cursort = $_POST['sort']; +} else { + $cursort = ""; +} +if ($_POST['filter']) { + $curfilter = $_POST['filter']; +} else { + $curfilter = ""; +} +if ($_POST['hostipformat']) { + $curhostipformat = $_POST['hostipformat']; +} else { + $curhostipformat = ""; +} + +function iflist() { + global $ifdescrs; + + $iflist = array(); + + foreach ($ifdescrs as $ifn => $ifd) { + $iflist[$ifn] = $ifd; + } + + return($iflist); +} + +$pgtitle = array(gettext("Status"),gettext("Traffic Graph")); + +include("head.inc"); + +require('classes/Form.class.php'); + +$form = new Form(false); +$form->addClass('auto-submit'); + +$section = new Form_Section('Graph settings'); + +$group = new Form_Group(''); + +$group->add(new Form_Select( + 'if', + null, + $curif, + iflist() +))->setHelp('Interface'); + +$group->add(new Form_Select( + 'sort', + null, + $cursort, + array ( + 'in' => 'Bandwidth In', + 'out' => 'Bandwidth Out' + ) +))->setHelp('Sort by'); + +$group->add(new Form_Select( + 'filter', + null, + $curfilter, + array ( + 'local' => 'Local', + 'remote'=> 'Remote', + 'all' => 'All' + ) +))->setHelp('Filter'); + +$group->add(new Form_Select( + 'hostipformat', + null, + $curhostipformat, + array ( + '' => 'IP Address', + 'hostname' => 'Host Name', + 'fqdn' => 'FQDN' + ) +))->setHelp('Display'); + +$section->add($group); + +$form->add($section); +print $form; + +?> +<script> + +function updateBandwidth(){ + $.ajax( + '/bandwidth_by_ip.php', + { + type: 'get', + data: $(document.forms[0]).serialize(), + success: function (data) { + var hosts_split = data.split("|"); + + $('#top10-hosts').empty(); + + //parse top ten bandwidth abuser hosts + for (var y=0; y<10; y++){ + if ((y < hosts_split.length) && (hosts_split[y] != "") && (hosts_split[y] != "no info")) { + hostinfo = hosts_split[y].split(";"); + + $('#top10-hosts').append('<tr>'+ + '<td>'+ hostinfo[0] +'</td>'+ + '<td>'+ hostinfo[1] +' Bits/sec</td>'+ + '<td>'+ hostinfo[2] +' Bits/sec</td>'+ + '</tr>'); + } + } + }, + }); +} + +events.push(function(){ + $('form.auto-submit').on('change', function(){ + $(this).submit(); + }); + + setInterval('updateBandwidth()', 1000); + + updateBandwidth(); +}); +</script> +<?php + +/* link the ipsec interface magically */ +if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enable'])) { + $ifdescrs['enc0'] = "IPsec"; +} + +?> +<div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title">Traffic graph</h2> + </div> + <div class="panel-body"> + <div class="col-sm-6"> + <object data="graph.php?ifnum=<?=htmlspecialchars($curif);?>&ifname=<?=rawurlencode($ifdescrs[htmlspecialchars($curif)]);?>"> + <param name="id" value="graph" /> + <param name="type" value="image/svg+xml" /> + <param name="width" value="<? echo $width; ?>" /> + <param name="height" value="<? echo $height; ?>" /> + <param name="pluginspage" value="http://www.adobe.com/svg/viewer/install/auto" /> + </object> + </div> + <div class="col-sm-6"> + <table class="table table-striped table-condensed"> + <thead> + <tr> + <th><?=(($curhostipformat=="") ? gettext("Host IP") : gettext("Host Name or IP")); ?></th> + <th><?=gettext("Bandwidth In"); ?></th> + <th><?=gettext("Bandwidth Out"); ?></th> + </tr> + </thead> + <tbody id="top10-hosts"> + <!-- to be added by javascript --> + </tbody> + </table> + </div> + </div> +</div> +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_graph_cpu.php b/src/usr/local/www/status_graph_cpu.php new file mode 100644 index 0000000..43c37e0 --- /dev/null +++ b/src/usr/local/www/status_graph_cpu.php @@ -0,0 +1,62 @@ +<?php +/* + $Id$ + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-status-cpuload +##|*NAME=Status: CPU load page +##|*DESCR=Allow access to the 'Status: CPU load' page. +##|*MATCH=status_graph_cpu.php* +##|-PRIV + +$pgtitle = array(gettext("Status"), gettext("CPU load")); +require("guiconfig.inc"); +include("head.inc"); + +$pgtitle = gettext("Status: CPU Graph"); + +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">CPU Load graph</h2></div> + <div class="panel-body" align="center"> + <embed src="graph_cpu.php" type="image/svg+xml" + width="550" height="275" pluginspage="http://www.adobe.com/svg/viewer/install/auto" /> + </div> + + <p align="center"><strong><?=gettext("Note"); ?>:</strong><?=gettext("if you can't see the graph, you may have to install the")?> + <a href="http://www.adobe.com/svg/viewer/install/" target="_blank"><?=gettext("Adobe SVG viewer"); ?></a> + </p> +</div> + +<?php +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_interfaces.php b/src/usr/local/www/status_interfaces.php new file mode 100644 index 0000000..017c205 --- /dev/null +++ b/src/usr/local/www/status_interfaces.php @@ -0,0 +1,201 @@ +<?php +/* $Id$ */ +/* + status_interfaces.php + part of pfSense + Copyright (C) 2009 Scott Ullrich <sullrich@gmail.com>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-status-interfaces +##|*NAME=Status: Interfaces page +##|*DESCR=Allow access to the 'Status: Interfaces' page. +##|*MATCH=status_interfaces.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("shaper.inc"); +require_once("filter.inc"); + +if ($_POST['if'] && $_POST['submit']) { + $interface = $_POST['if']; + if ($_POST['status'] == "up") { + interface_bring_down($interface); + } else { + interface_configure($interface); + } + header("Location: status_interfaces.php"); + exit; +} + +$formtemplate = '<form name="%s" action="status_interfaces.php" method="post">' . + '<input type="hidden" name="if" value="%s" />' . + '<input type="hidden" name="status" value="%s" />' . + '%s' . + '<input type="submit" name="submit" class="btn btn-warning btn-xs" value="%s" />' . + '</form>'; + +// Display a term/definition pair +function showDef($show, $term, $def) { + if($show) { + print('<dt>' . $term . '</dt>'); + print('<dd>' . htmlspecialchars($def) . '</dd>'); + } +} + +// Display a term/definition pair with a button +function showDefBtn($show, $term, $def, $ifval, $btnlbl) { + global $formtemplate; + + if($show) { + print('<dt>' . $term . '</dt>'); + print('<dd>'); + printf($formtemplate, $term, $ifvalue, $show, htmlspecialchars($def) . ' ', $btnlbl); + print('</dd>'); + } +} + +$pgtitle = array(gettext("Status"),gettext("Interfaces")); +$shortcut_section = "interfaces"; +include("head.inc"); + + $ifdescrs = get_configured_interface_with_descr(false, true); + + foreach ($ifdescrs as $ifdescr => $ifname): + $ifinfo = get_interface_info($ifdescr); + $mac_man = load_mac_manufacturer_table(); +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=htmlspecialchars($ifname)?><?=gettext(" interface "); ?>(<?=htmlspecialchars($ifdescr)?>, <?=htmlspecialchars($ifinfo['hwif'])?>)</h2></div> + <div class="panel-body"> + <dl class="dl-horizontal"> +<?php + showDef(true, gettext("Status"), $ifinfo['status']); + showDefBtn($ifinfo['dhcplink'], 'DHCP', $ifinfo['dhcplink'], $ifdescr, $ifinfo['dhcplink'] == "up" ? gettext("Release") : gettext("Renew")); + showDefBtn($ifinfo['dhcp6link'], 'DHCP6', $ifinfo['dhcp6link'], $ifdescr, $ifinfo['dhcp6link'] == "up" ? gettext("Release") : gettext("Renew")); + showDefBtn($ifinfo['pppoelink'], 'PPPoE', $ifinfo['pppoelink'], $ifdescr, $ifinfo['pppoelink'] == "up" ? gettext("Disconnect") : gettext("Connect")); + showDefBtn($ifinfo['pptplink'], 'PPTP', $ifinfo['pptplink'], $ifdescr, $ifinfo['pptplink'] == "up" ? gettext("Disconnect") : gettext("Connect")); + showDefBtn($ifinfo['l2tplink'], 'L2TP', $ifinfo['l2tplink'], $ifdescr, $ifinfo['l2tplink'] == "up" ? gettext("Disconnect") : gettext("Connect")); + showDefBtn($ifinfo['ppplink'], 'L2TP', $ifinfo['ppplink'], $ifdescr, ($ifinfo['ppplink'] == "up" && !$ifinfo['nodevice']) ? gettext("Disconnect") : gettext("Connect")); + showDef($ifinfo['ppp_uptime'] || $ifinfo['ppp_uptime_accumulated'], gettext("Uptime ") . $ifinfo['ppp_uptime_accumulated'] ? '(historical)':'', $ifinfo['ppp_uptime'] . $ifinfo['ppp_uptime_accumulated']); + showDef($ifinfo['cell_rssi'], gettext("Cell Signal (RSSI)"), $ifinfo['cell_rssi']); + showDef($ifinfo['cell_mode'], gettext("Cell Mode"), $ifinfo['cell_mode']); + showDef($ifinfo['cell_simstate'], gettext("Cell SIM State"), $ifinfo['cell_simstate']); + showDef($ifinfo['cell_service'], gettext("Cell Service"), $ifinfo['cell_service']); + showDef($ifinfo['cell_bwupstream'], gettext("Cell Upstream"), $ifinfo['cell_bwupstream']); + showDef($ifinfo['cell_bwdownstream'], gettext("Cell Downstream"), $ifinfo['cell_bwdownstream']); + showDef($ifinfo['cell_upstream'], gettext("Cell Current Up"), $ifinfo['cell_upstream']); + showDef($ifinfo['cell_downstream'], gettext("Cell Current Down"), $ifinfo['cell_downstream']); + + if ($ifinfo['macaddr']) { + $mac=$ifinfo['macaddr']; + $mac_hi = strtoupper($mac[0] . $mac[1] . $mac[3] . $mac[4] . $mac[6] . $mac[7]); + showDef(isset($mac_man[$mac_hi]), gettext('MAC Address'), ' - ' . $mac_man[$mac_hi]); + } + + if ($ifinfo['status'] != "down") { + if ($ifinfo['dhcplink'] != "down" && $ifinfo['pppoelink'] != "down" && $ifinfo['pptplink'] != "down") { + showDef($ifinfo['ipaddr'], gettext('IPv4 Address'), $ifinfo['ipaddr']); + showDef($ifinfo['subnet'], gettext('Subnet mask IPv4'), $ifinfo['subnet']); + showDef($ifinfo['gateway'], gettext('Gateway IPv4'), $ifinfo['gateway']); + showDef($ifinfo['linklocal'], gettext('IPv6 Link Local'), $ifinfo['linklocal']); + showDef($ifinfo['ipaddrv6'], gettext('IPv6 Address'), $ifinfo['ipaddrv6']); + showDef($ifinfo['subnetv6'], gettext('Subnet mask IPv6'), $ifinfo['subnetv6']); + showDef($ifinfo['gatewayv6'], gettext("Gateway IPv6"), $config['interfaces'][$ifdescr]['gatewayv6'] . $ifinfo['gatewayv6']); + + if ($ifdescr == "wan" && file_exists("{$g['varetc_path']}/resolv.conf")) { + $dns_servers = get_dns_servers(); + $dnscnt = 0; + foreach($dns_servers as $dns) { + showDef(true, $dnscnt == 0 ? gettext('ISP DNS servers'):'', $dns); + $dnscnt++; + } + } + } + + showDef($ifinfo['mtu'], gettext("MTU"), $ifinfo['mtu']); + showDef($ifinfo['media'], gettext("Media"), $ifinfo['media']); + showDef($ifinfo['laggproto'], gettext("LAGG Protocol"), $ifinfo['laggproto']); + showDef($ifinfo['laggport'],gettext("LAGG Ports"),$laggport); + showDef($ifinfo['channel'],gettext("Channel"),$ifinfo['channel']); + showDef($ifinfo['ssid'],gettext("SSID"),$ifinfo['ssid']); + showDef($ifinfo['bssid'],gettext("BSSID"),$ifinfo['bssid']); + showDef($ifinfo['rate'],gettext("Rate"),$ifinfo['rate']); + showDef($ifinfo['rssi'],gettext("RSSI"),$ifinfo['rssi']); + showDef(true,gettext("In/out packets"),$ifinfo['inpkts'] . '/' . $ifinfo['outpkts']); + showDef(true,gettext("In/out packets (pass)"),$ifinfo['inpktspass'] . "/" . $ifinfo['outpktspass']); + showDef(true,gettext("In/out packets (block)"),$ifinfo['inpktsblock'] . "/" . $ifinfo['outpktsblock']); + showDef(isset($ifinfo['inerrs']),gettext("In/out errors"),$ifinfo['inerrs'] . "/" . $ifinfo['outerrs']); + showDef(isset($ifinfo['collisions']),gettext("Collisions"),$ifinfo['collisions']); + } // e-o-if ($ifinfo['status'] != "down") + + showDef($ifinfo['bridge'], gettext('Bridge (') . $ifinfo['bridgeint'] . ')', $ifinfo['bridge']); + + if(file_exists("/usr/bin/vmstat")) { + $real_interface = ""; + $interrupt_total = ""; + $interrupt_sec = ""; + $real_interface = $ifinfo['hwif']; + $interrupt_total = `vmstat -i | grep $real_interface | awk '{ print $3 }'`; + $interrupt_sec = `vmstat -i | grep $real_interface | awk '{ print $4 }'`; + + if(strstr($interrupt_total, "hci")) { + $interrupt_total = `vmstat -i | grep $real_interface | awk '{ print $4 }'`; + $interrupt_sec = `vmstat -i | grep $real_interface | awk '{ print $5 }'`; + } + + unset($interrupt_total); + + showDef($interrupt_total, gettext('Total interrupts'), $interrupt_total); + showDef($interrupt_total, '', $interrupt_sec . " " . $interrupt_total); + } +?> + </dl> + </div> +</div> + +<?php + break; + endforeach; +?> + +<div class="alert alert-warning" role="alert"> + <?=gettext("Using dial-on-demand will bring the connection up again if any packet ". + "triggers it. To substantiate this point: disconnecting manually ". + "will <strong>not</strong> prevent dial-on-demand from making connections ". + "to the outside! Don't use dial-on-demand if you want to make sure that the line ". + "is kept disconnected.");?> +</div> + +<?php include("foot.inc"); ?> diff --git a/src/usr/local/www/status_lb_pool.php b/src/usr/local/www/status_lb_pool.php new file mode 100644 index 0000000..50d42e8 --- /dev/null +++ b/src/usr/local/www/status_lb_pool.php @@ -0,0 +1,257 @@ +<?php +/* $Id$ */ +/* + status_lb_pool.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-status-loadbalancer-pool +##|*NAME=Status: Load Balancer: Pool page +##|*DESCR=Allow access to the 'Status: Load Balancer: Pool' page. +##|*MATCH=status_lb_pool.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("vslb.inc"); + +define('COLOR', true); +define('LIGHTGREEN', '#90EE90'); +define('LIGHTCORAL', '#F08080'); +define('KHAKI', '#F0E68C'); +define('LIGHTGRAY', '#D3D3D3'); +define('WHITE', '#FFFFFF'); + +if (!is_array($config['load_balancer']['lbpool'])) { + $config['load_balancer']['lbpool'] = array(); +} + +$a_pool = &$config['load_balancer']['lbpool']; + +$lb_logfile = "{$g['varlog_path']}/relayd.log"; + +$nentries = $config['syslog']['nentries']; + +if (!$nentries) + $nentries = 50; + +$now = time(); +$year = date("Y"); + +$pgtitle = array(gettext("Status"), gettext("Load Balancer"), gettext("Pool")); +$shortcut_section = "relayd"; + +include("head.inc"); + +$relay_hosts = get_lb_summary(); + +if ($_POST) { + if ($_POST['apply']) { + $retval = 0; + $retval |= filter_configure(); + $retval |= relayd_configure(); + $savemsg = get_std_save_message($retval); + clear_subsystem_dirty('loadbalancer'); + } else { + /* Keep a list of servers we find in POST variables */ + $newservers = array(); + + foreach ($_POST as $name => $value) { + /* Look through the POST vars to find the pool data */ + if (strpos($name, '|') !== false) { + list($poolname, $ip) = explode("|", $name); + $ip = str_replace('_', '.', $ip); + $newservers[$poolname][] = $ip; + } elseif (is_ipaddr($value)) { + $newservers[$name][] = $value; + } + } + + foreach ($a_pool as & $pool) { + if (is_array($pool['servers']) && is_array($pool['serversdisabled'])) { + $oldservers = array_merge($pool['servers'], $pool['serversdisabled']); + } elseif (is_array($pool['servers'])) { + $oldservers = $pool['servers']; + } elseif (is_array($pool['serversdisabled'])) { + $oldservers = $pool['serversdisabled']; + } else { + $oldservers = array(); + } + if (is_array($newservers[$pool['name']])) { + $pool['servers'] = $newservers[$pool['name']]; + $pool['serversdisabled'] = array_diff($oldservers, $newservers[$pool['name']]); + } + } + + mark_subsystem_dirty('loadbalancer'); + write_config("Updated load balancer pools via status screen."); + } +} + +if (is_subsystem_dirty('loadbalancer')) + print_info_box_np('The load balancer configuration has been changed You must apply the changes in order for them to take effect.'); + +/* active tabs */ +$tab_array = array(); +$tab_array[] = array(gettext("Pools"), true, "status_lb_pool.php"); +$tab_array[] = array(gettext("Virtual Servers"), false, "status_lb_vs.php"); +display_top_tabs($tab_array); + +$rowsprinted = 0; +?> + +<form action="status_lb_pool.php" method="post"> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">Load Balancer Pools</h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Name")?></th> + <th><?=gettext("Mode")?></th> + <th><?=gettext("Servers")?></th> + <th><?=gettext("Monitor")?></th> + <th><?=gettext("Description")?></th> + </tr> + </thead> + <tbody> +<?php +foreach ($a_pool as $pool): + $rowsprinted++; +?> + <tr> + <td> + <?=$pool['name']?> + </td> + <td> +<?php + switch($pool['mode']) { + case "loadbalance": + echo "Load balancing"; + break; + case "failover": + echo "Manual failover"; + break; + default: + echo "(default)"; + } +?> + </td> + <td> + <table> <!-- Mini-table allows manipulation of cell colors--> +<?php + $pool_hosts=array(); + + foreach ((array) $pool['servers'] as $server) { + $svr['ip']['addr']=$server; + $svr['ip']['state']=$relay_hosts[$pool['name'].":".$pool['port']][$server]['state']; + $svr['ip']['avail']=$relay_hosts[$pool['name'].":".$pool['port']][$server]['avail']; + $pool_hosts[]=$svr; + } + + foreach ((array) $pool['serversdisabled'] as $server) { + $svr['ip']['addr']="$server"; + $svr['ip']['state']='disabled'; + $svr['ip']['avail']='disabled'; + $pool_hosts[]=$svr; + } + + asort($pool_hosts); + + foreach ((array) $pool_hosts as $server) { + if($server['ip']['addr']!="") { + switch ($server['ip']['state']) { + case 'up': + $bgcolor = LIGHTGREEN; // lightgreen + $checked = "checked=\"checked\""; + break; + case 'disabled': + $bgcolor = WHITE; + $checked = ""; + break; + default: + $bgcolor = LIGHTCORAL; // lightcoral + $checked = "checked=\"checked\""; + } +?> + <tr> +<?php + switch ($pool['mode']) { + case 'loadbalance': + print("<td><input type=\"checkbox\" name=\"{$pool['name']}|" . str_replace('.', '_', $server['ip']['addr']) . "\" {$checked} /></td>\n"); + break; + case 'failover': + print("<td><input type=\"radio\" name=\"{$pool['name']}\" value=\"{$server['ip']['addr']}\" {$checked} /></td>\n"); + break; + } + + print("<td bgcolor=\"{$bgcolor}\"> {$server['ip']['addr']}:{$pool['port']} </td><td bgcolor=\"{$bgcolor}\"> "); + + if($server['ip']['avail']) + print(" ({$server['ip']['avail']}) "); +?> + </td> + </tr> +<?php + } + } +?> + </table> + </td> + <td > + <?=$pool['monitor']; ?> + </td> + <td> + <?=$pool['descr']?> + </td> + </tr> +<?php +endforeach; +?> + </tbody> + </table> +<?php +if($rowsprinted > 0) { +?> + <nav class="action-buttons"> + <input name="Submit" type="submit" class="btn btn-primary" value="<?= gettext("Save"); ?>" /> + <input name="Reset" type="reset" class="btn btn-danger" value="<?= gettext("Reset"); ?>" /> + </nav> +<?php +} +?> + </div> + </div> +</form> +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_lb_vs.php b/src/usr/local/www/status_lb_vs.php new file mode 100644 index 0000000..953c710 --- /dev/null +++ b/src/usr/local/www/status_lb_vs.php @@ -0,0 +1,153 @@ +<?php +/* $Id$ */ +/* + status_lb_vs.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/local/sbin/relayctl + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-status-loadbalancer-virtualserver +##|*NAME=Status: Load Balancer: Virtual Server page +##|*DESCR=Allow access to the 'Status: Load Balancer: Virtual Server' page. +##|*MATCH=status_lb_vs.php* +##|-PRIV + +define('COLOR', true); +define('LIGHTGREEN', '#90EE90'); +define('LIGHTCORAL', '#F08080'); +define('KHAKI', '#F0E68C'); +define('LIGHTGRAY', '#D3D3D3'); +define('WHITE', '#FFFFFF'); + +require_once("guiconfig.inc"); +require_once("vslb.inc"); + +if (!is_array($config['load_balancer']['lbpool'])) { + $config['load_balancer']['lbpool'] = array(); +} +if (!is_array($config['load_balancer']['virtual_server'])) { + $config['load_balancer']['virtual_server'] = array(); +} +$a_vs = &$config['load_balancer']['virtual_server']; +$a_pool = &$config['load_balancer']['lbpool']; +$rdr_a = get_lb_redirects(); + +$pgtitle = array(gettext("Status"), gettext("Load Balancer"), gettext("Virtual Server")); +include("head.inc"); + +/* active tabs */ +$tab_array = array(); +$tab_array[] = array(gettext("Pools"), false, "status_lb_pool.php"); +$tab_array[] = array(gettext("Virtual Servers"), true, "status_lb_vs.php"); +display_top_tabs($tab_array); + +if(empty($a_vs)) + print('<div class="alert alert-danger">No load balancers have been configured!</div>'); +else { +?> + <div class="table-responsive"></div> + <table class="table table-striped table-hover table-condensed"> + <tr> + <td><?=gettext("Name"); ?></td> + <td><?=gettext("Address"); ?></td> + <td><?=gettext("Servers"); ?></td> + <td><?=gettext("Status"); ?></td> + <td><?=gettext("Description"); ?></td> + </tr> +<?php + $i = 0; + foreach ($a_vs as $vsent): ?> + <tr> + <td> + <?=$vsent['name']?> + </td> + <td> + <?=$vsent['ipaddr']." : ".$vsent['port']?><br /> + </td> + <td> + + <?php + foreach ($a_pool as $vipent) { + if ($vipent['name'] == $vsent['poolname']) { + foreach ((array) $vipent['servers'] as $server) { ?> + <?=$server?> <br /> +<?php + } + } + } +?> + </td> + <?php + switch (trim($rdr_a[$vsent['name']]['status'])) { + case 'active': + $bgcolor = LIGHTGREEN; + $rdr_a[$vsent['name']]['status'] = "Active"; + break; + case 'down': + $bgcolor = LIGHTCORAL; + $rdr_a[$vsent['name']]['status'] = "Down"; + break; + default: + $bgcolor = LIGHTGRAY; + $rdr_a[$vsent['name']]['status'] = 'Unknown - relayd not running?'; + } + + if(!COLOR) + $bgcolor = WHITE; +?> + <td bgcolor="<?=$bgcolor?>"> + <?=$rdr_a[$vsent['name']]['status']?> + </td> + <td> +<?php + if (!empty($rdr_a[$vsent['name']]['total'])) { ?> + Total Sessions: <?=$rdr_a[$vsent['name']]['total']?><br>/><?php + } + if (!empty($rdr_a[$vsent['name']]['last'])) { ?> + Last: <?=$rdr_a[$vsent['name']]['last']?><br>/><?php + } + if (!empty($rdr_a[$vsent['name']]['average'])) { ?> + Average: <?=$rdr_a[$vsent['name']]['average']?><?php + } ?> + </td> + <td> + <?=$vsent['descr']?> + </td> + </tr> + +<?php $i++; endforeach; ?> + </table> + </div> + +<?php } + +include("foot.inc"); ?>
\ No newline at end of file diff --git a/src/usr/local/www/status_ntpd.php b/src/usr/local/www/status_ntpd.php new file mode 100644 index 0000000..bf00ced --- /dev/null +++ b/src/usr/local/www/status_ntpd.php @@ -0,0 +1,304 @@ +<?php +/* $Id$ */ +/* + status_ntpd.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2013 Dagorlad + Copyright (C) 2012 Jim Pingle + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/local/sbin/ntpd /usr/local/sbin/ntpq + pfSense_MODULE: ntpd +*/ + +##|+PRIV +##|*IDENT=page-status-ntp +##|*NAME=Status: NTP page +##|*DESCR=Allow access to the 'Status: NTP' page. +##|*MATCH=status_ntpd.php* +##|-PRIV + +require_once("guiconfig.inc"); + +if (!isset($config['ntpd']['noquery'])) { + if (isset($config['system']['ipv6allow'])) { + $inet_version = ""; + } else { + $inet_version = " -4"; + } + + exec("/usr/local/sbin/ntpq -pn $inet_version | /usr/bin/tail +3", $ntpq_output); + + $ntpq_servers = array(); + foreach ($ntpq_output as $line) { + $server = array(); + + switch (substr($line, 0, 1)) { + case " ": + $server['status'] = "Unreach/Pending"; + break; + case "*": + $server['status'] = "Active Peer"; + break; + case "+": + $server['status'] = "Candidate"; + break; + case "o": + $server['status'] = "PPS Peer"; + break; + case "#": + $server['status'] = "Selected"; + break; + case ".": + $server['status'] = "Excess Peer"; + break; + case "x": + $server['status'] = "False Ticker"; + break; + case "-": + $server['status'] = "Outlier"; + break; + } + + $line = substr($line, 1); + $peerinfo = preg_split("/[\s\t]+/", $line); + + $server['server'] = $peerinfo[0]; + $server['refid'] = $peerinfo[1]; + $server['stratum'] = $peerinfo[2]; + $server['type'] = $peerinfo[3]; + $server['when'] = $peerinfo[4]; + $server['poll'] = $peerinfo[5]; + $server['reach'] = $peerinfo[6]; + $server['delay'] = $peerinfo[7]; + $server['offset'] = $peerinfo[8]; + $server['jitter'] = $peerinfo[9]; + + $ntpq_servers[] = $server; + } + + exec("/usr/local/sbin/ntpq -c clockvar $inet_version", $ntpq_clockvar_output); + foreach ($ntpq_clockvar_output as $line) { + if (substr($line, 0, 9) == "timecode=") { + $tmp = explode('"', $line); + $tmp = $tmp[1]; + if (substr($tmp, 0, 6) == '$GPRMC') { + $gps_vars = explode(",", $tmp); + $gps_ok = ($gps_vars[2] == "A"); + $gps_lat_deg = substr($gps_vars[3], 0, 2); + $gps_lat_min = substr($gps_vars[3], 2) / 60.0; + $gps_lon_deg = substr($gps_vars[5], 0, 3); + $gps_lon_min = substr($gps_vars[5], 3) / 60.0; + $gps_lat = $gps_lat_deg + $gps_lat_min; + $gps_lat = $gps_lat * (($gps_vars[4] == "N") ? 1 : -1); + $gps_lon = $gps_lon_deg + $gps_lon_min; + $gps_lon = $gps_lon * (($gps_vars[6] == "E") ? 1 : -1); + } elseif (substr($tmp, 0, 6) == '$GPGGA') { + $gps_vars = explode(",", $tmp); + $gps_ok = $gps_vars[6]; + $gps_lat_deg = substr($gps_vars[2], 0, 2); + $gps_lat_min = substr($gps_vars[2], 2) / 60.0; + $gps_lon_deg = substr($gps_vars[4], 0, 3); + $gps_lon_min = substr($gps_vars[4], 3) / 60.0; + $gps_lat = $gps_lat_deg + $gps_lat_min; + $gps_lat = $gps_lat * (($gps_vars[3] == "N") ? 1 : -1); + $gps_lon = $gps_lon_deg + $gps_lon_min; + $gps_lon = $gps_lon * (($gps_vars[5] == "E") ? 1 : -1); + $gps_alt = $gps_vars[9]; + $gps_alt_unit = $gps_vars[10]; + $gps_sat = $gps_vars[7]; + } elseif (substr($tmp, 0, 6) == '$GPGLL') { + $gps_vars = explode(",", $tmp); + $gps_ok = ($gps_vars[6] == "A"); + $gps_lat_deg = substr($gps_vars[1], 0, 2); + $gps_lat_min = substr($gps_vars[1], 2) / 60.0; + $gps_lon_deg = substr($gps_vars[3], 0, 3); + $gps_lon_min = substr($gps_vars[3], 3) / 60.0; + $gps_lat = $gps_lat_deg + $gps_lat_min; + $gps_lat = $gps_lat * (($gps_vars[2] == "N") ? 1 : -1); + $gps_lon = $gps_lon_deg + $gps_lon_min; + $gps_lon = $gps_lon * (($gps_vars[4] == "E") ? 1 : -1); + } + } + } +} + +if (isset($config['ntpd']['gps']['type']) && ($config['ntpd']['gps']['type'] == 'SureGPS') && (isset($gps_ok))) { + //GSV message is only enabled by init commands in services_ntpd_gps.php for SureGPS board + $gpsport = fopen("/dev/gps0", "r+"); + while ($gpsport) { + $buffer = fgets($gpsport); + if (substr($buffer, 0, 6) == '$GPGSV') { + //echo $buffer."\n"; + $gpgsv = explode(',', $buffer); + $gps_satview = $gpgsv[3]; + break; + } + } +} + +$pgtitle = array(gettext("Status"), gettext("NTP")); +$shortcut_section = "ntp"; + +include("head.inc"); +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">Network Time Protocol Status</h2></div> + <div class="panel-body"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Status"); ?></th> + <th><?=gettext("Server"); ?></th> + <th><?=gettext("Ref ID"); ?></th> + <th><?=gettext("Stratum"); ?></th> + <th><?=gettext("Type"); ?></th> + <th><?=gettext("When"); ?></th> + <th><?=gettext("Poll"); ?></th> + <th><?=gettext("Reach"); ?></th> + <th><?=gettext("Delay"); ?></th> + <th><?=gettext("Offset"); ?></th> + <th><?=gettext("Jitter"); ?></th> + </tr> + </thead> + <tbody> + <?php if (isset($config['ntpd']['noquery'])): ?> + <tr> + <td class="warning" colspan="11"> + Statistics unavailable because ntpq and ntpdc queries are disabled in the <a href="services_ntpd.php">NTP service settings</a>. + </td> + </tr> + <?php elseif (count($ntpq_servers) == 0): ?> + <tr> + <td class="warning" colspan="11"> + No peers found, <a href="status_services.php">is the ntp service running?</a> + </td> + </tr> + <?php else: + + $i = 0; + foreach ($ntpq_servers as $server): ?> + <tr> + <td><?=$server['status']?></td> + <td><?=$server['server']?></td> + <td><?=$server['refid']?></td> + <td><?=$server['stratum']?></td> + <td><?=$server['type']?></td> + <td><?=$server['when']?></td> + <td><?=$server['poll']?></td> + <td><?=$server['reach']?></td> + <td><?=$server['delay']?></td> + <td><?=$server['offset']?></td> + <td><?=$server['jitter']?></td> + </tr> <?php + $i++; + endforeach; + endif; +?> + </tbody> + </table> + </div> +</div> + + +<?php + +// GPS satellite information (if available) +if (($gps_ok) && ($gps_lat) && ($gps_lon)): + $gps_goo_lnk = 2; ?> + + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title">GPS information</h2></div> + <div class="panel-body"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th> + <?=gettext("Clock Latitude"); ?> + </th> + <th> + <?=gettext("Clock Longitude"); ?> + </th> + <?php if (isset($gps_alt)) { ?> + <th> + <?=gettext("Clock Altitude")?> + </th> + <?php $gps_goo_lnk++; + } + + if (isset($gps_sat) || isset($gps_satview)) { ?> + <th> + <?=gettext("Satellites")?> + </th> <?php + $gps_goo_lnk++; + }?> + </tr> + </thead> + + <tbody> + <tr> + <td> + <?=printf("%.5f", $gps_lat); ?> (<?=printf("%d", $gps_lat_deg); ?>° <?=printf("%.5f", $gps_lat_min*60); ?><?=$gps_vars[4]; ?>) + </td> + <td> + <?=printf("%.5f", $gps_lon); ?> (<?=printf("%d", $gps_lon_deg); ?>° <?=printf("%.5f", $gps_lon_min*60); ?><?=$gps_vars[6]; ?>) + </td> + + <?php if (isset($gps_alt)) { ?> + <td> + <?=$gps_alt . ' ' . $gps_alt_unit?> + </td> + } + + if (isset($gps_sat) || isset($gps_satview)) { ?> + <td align="center"> <?php + if (isset($gps_satview)) { + print('in view ' . intval($gps_satview)); + } + + if (isset($gps_sat) && isset($gps_satview)) { + print(', '); + } + if (isset($gps_sat)) { + print('in use ' . $gps_sat); + } ?> + </td> <?php + } + ?> + </tr> + <tr> + <td colspan="<?=$gps_goo_lnk; ?>"><a target="_gmaps" href="http://maps.google.com/?q=<?=$gps_lat; ?>,<?=$gps_lon; ?>">Google Maps Link</a></td> + </tr> + </tbody> + </table> + </div> + </div> + +<?php endif; + +include("foot.inc"); ?>
\ No newline at end of file diff --git a/src/usr/local/www/status_openvpn.php b/src/usr/local/www/status_openvpn.php new file mode 100644 index 0000000..8c765d9 --- /dev/null +++ b/src/usr/local/www/status_openvpn.php @@ -0,0 +1,443 @@ +<?php +/* + status_openvpn.php + + Copyright (C) 2005 Scott Ullrich, Colin Smith + Copyright (C) 2008 Shrew Soft Inc. + Copyright (C) 2010 Jim Pingle + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + AJAX bits borrowed from diag_dump_states.php + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: openvpn +*/ + +##|+PRIV +##|*IDENT=page-status-openvpn +##|*NAME=Status: OpenVPN page +##|*DESCR=Allow access to the 'Status: OpenVPN' page. +##|*MATCH=status_openvpn.php* +##|-PRIV + +$pgtitle = array(gettext("Status"), gettext("OpenVPN")); +$shortcut_section = "openvpn"; + +require("guiconfig.inc"); +require_once("openvpn.inc"); +require_once("shortcuts.inc"); +require_once("service-utils.inc"); + +/* Handle AJAX */ +if($_GET['action']) { + if($_GET['action'] == "kill") { + $port = $_GET['port']; + $remipp = $_GET['remipp']; + if (!empty($port) and !empty($remipp)) { + $retval = kill_client($port, $remipp); + echo htmlentities("|{$port}|{$remipp}|{$retval}|"); + } else { + echo gettext("invalid input"); + } + exit; + } +} + + +function kill_client($port, $remipp) { + global $g; + + //$tcpsrv = "tcp://127.0.0.1:{$port}"; + $tcpsrv = "unix://{$g['varetc_path']}/openvpn/{$port}.sock"; + $errval = null; + $errstr = null; + + /* open a tcp connection to the management port of each server */ + $fp = @stream_socket_client($tcpsrv, $errval, $errstr, 1); + $killed = -1; + if ($fp) { + stream_set_timeout($fp, 1); + fputs($fp, "kill {$remipp}\n"); + while (!feof($fp)) { + $line = fgets($fp, 1024); + + $info = stream_get_meta_data($fp); + if ($info['timed_out']) { + break; + } + + /* parse header list line */ + if (strpos($line, "INFO:") !== false) { + continue; + } + if (strpos($line, "SUCCESS") !== false) { + $killed = 0; + } + break; + } + fclose($fp); + } + return $killed; +} + +$servers = openvpn_get_active_servers(); +$sk_servers = openvpn_get_active_servers("p2p"); +$clients = openvpn_get_active_clients(); + +include("head.inc"); ?> + +<body link="#0000CC" vlink="#0000CC" alink="#0000CC" onload="<?=$jsevents["body"]["onload"];?>"> +<?php include("fbegin.inc"); ?> +<form action="status_openvpn.php" method="get" name="iform"> +<script type="text/javascript"> +//<![CDATA[ + function killClient(mport, remipp) { + var busy = function(index,icon) { + jQuery(icon).bind("onclick",""); + jQuery(icon).attr('src',jQuery(icon).attr('src').replace("\.gif", "_d.gif")); + jQuery(icon).css("cursor","wait"); + } + + jQuery('img[name="i:' + mport + ":" + remipp + '"]').each(busy); + + jQuery.ajax( + "<?=$_SERVER['SCRIPT_NAME'];?>" + + "?action=kill&port=" + mport + "&remipp=" + remipp, + { type: "get", complete: killComplete } + ); + } + + function killComplete(req) { + var values = req.responseText.split("|"); + if(values[3] != "0") { + alert('<?=gettext("An error occurred.");?>' + ' (' + values[3] + ')'); + return; + } + + jQuery('tr[name="r:' + values[1] + ":" + values[2] + '"]').each( + function(index,row) { jQuery(row).fadeOut(1000); } + ); + } +//]]> +</script> +<?php + $i = 0; + foreach ($servers as $server): +?> + +<table style="padding-top:0px; padding-bottom:0px; padding-left:0px; padding-right:0px" width="100%" border="0" cellpadding="0" cellspacing="0" summary="status openvpn"> + <tr> + <td colspan="6" class="listtopic"> + <?=$server['name'];?> <?=gettext("Client connections"); ?> + </td> + </tr> + <tr> + <td> + <table style="padding-top:0px; padding-bottom:0px; padding-left:0px; padding-right:0px" class="tabcont sortable" width="100%" border="0" cellpadding="0" cellspacing="0" summary="connections"> + <tr> + <td class="listhdrr"><?=gettext("Common Name"); ?></td> + <td class="listhdrr"><?=gettext("Real Address"); ?></td> + <td class="listhdrr"><?=gettext("Virtual Address"); ?></td> + <td class="listhdrr"><?=gettext("Connected Since"); ?></td> + <td class="listhdrr"><?=gettext("Bytes Sent"); ?></td> + <td class="listhdrr"><?=gettext("Bytes Received"); ?></td> + </tr> +<?php + foreach ($server['conns'] as $conn): +?> + <tr id="<?php echo "r:{$server['mgmt']}:{$conn['remote_host']}"; ?>"> + <td class="listlr"> + <?=$conn['common_name'];?> + </td> + <td class="listr"> + <?=$conn['remote_host'];?> + </td> + <td class="listr"> + <?=$conn['virtual_addr'];?> + </td> + <td class="listr"> + <?=$conn['connect_time'];?> + </td> + <td class="listr"> + <?=format_bytes($conn['bytes_sent']);?> + </td> + <td class="listr"> + <?=format_bytes($conn['bytes_recv']);?> + </td> + <td class="list"> + <img src="/themes/<?php echo $g['theme']; ?>/images/icons/icon_x.gif" height="17" width="17" border="0" + onclick="killClient('<?php echo $server['mgmt']; ?>', '<?php echo $conn['remote_host']; ?>');" style="cursor:pointer;" + id="<?php echo "i:{$server['mgmt']}:{$conn['remote_host']}"; ?>" + title="<?php echo gettext("Kill client connection from") . " " . $conn['remote_host']; ?>" alt="delete" /> + </td> + </tr> +<?php + endforeach; +?> + <tfoot> + <tr> + <td colspan="2" class="list" height="12"> + <table> + <tr> + <td><?php $ssvc = find_service_by_openvpn_vpnid($server['vpnid']); ?> + <?= get_service_status_icon($ssvc, true, true); ?> + <?= get_service_control_links($ssvc, true); ?></td> + </tr> + </table> + </td> + <td colspan="4" class="list" height="12"> </td> + </tr> + </tfoot> + </table> + </td> + </tr> +</table> +<?php + if (is_array($server['routes']) && count($server['routes'])): +?> +<div id="shroutebut-<?= $i ?>"> +<input type="button" onClick="show_routes('tabroute-<?= $i ?>','shroutebut-<?= $i ?>')" value="<?php echo gettext("Show Routing Table"); ?>" /> - <?= gettext("Display OpenVPN's internal routing table for this server.") ?> +<br /><br /> +</div> +<table style="display: none; padding-top:0px; padding-bottom:0px; padding-left:0px; padding-right:0px" width="100%" border="0" cellpadding="0" cellspacing="0" id="tabroute-<?= $i ?>" summary="routing table"> + <tr> + <td colspan="6" class="listtopic"> + <?=$server['name'];?> <?=gettext("Routing Table"); ?> + </td> + </tr> + <tr> + <td> + <table style="padding-top:0px; padding-bottom:0px; padding-left:0px; padding-right:0px" class="tabcont sortable" width="100%" border="0" cellpadding="0" cellspacing="0" summary="results"> + <tr> + <td class="listhdrr"><?=gettext("Common Name"); ?></td> + <td class="listhdrr"><?=gettext("Real Address"); ?></td> + <td class="listhdrr"><?=gettext("Target Network"); ?></td> + <td class="listhdrr"><?=gettext("Last Used"); ?></td> + </tr> + +<?php + foreach ($server['routes'] as $conn): +?> + <tr id="<?php echo "r:{$server['mgmt']}:{$conn['remote_host']}"; ?>"> + <td class="listlr"> + <?=$conn['common_name'];?> + </td> + <td class="listr"> + <?=$conn['remote_host'];?> + </td> + <td class="listr"> + <?=$conn['virtual_addr'];?> + </td> + <td class="listr"> + <?=$conn['last_time'];?> + </td> + </tr> +<?php + endforeach; +?> + <tfoot> + <tr> + <td colspan="6" class="list" height="12"><?= gettext("An IP address followed by C indicates a host currently connected through the VPN.") ?></td> + </tr> + </tfoot> + </table> + </td> + </tr> +</table> +<?php + endif; +?> +<br /> +<?php + $i++; + endforeach; +?> +<br /> + +<?php + if (!empty($sk_servers)) { +?> +<table style="padding-top:0px; padding-bottom:0px; padding-left:0px; padding-right:0px" width="100%" border="0" cellpadding="0" cellspacing="0" summary="peer to peer stats"> + <tr> + <td colspan="6" class="listtopic"> + <?=gettext("Peer to Peer Server Instance Statistics"); ?> + </td> + </tr> + <tr> + <td> + <table style="padding-top:0px; padding-bottom:0px; padding-left:0px; padding-right:0px" class="tabcont sortable" width="100%" border="0" cellpadding="0" cellspacing="0" summary="results"> + <tr> + <td class="listhdrr"><?=gettext("Name"); ?></td> + <td class="listhdrr"><?=gettext("Status"); ?></td> + <td class="listhdrr"><?=gettext("Connected Since"); ?></td> + <td class="listhdrr"><?=gettext("Virtual Addr"); ?></td> + <td class="listhdrr"><?=gettext("Remote Host"); ?></td> + <td class="listhdrr"><?=gettext("Bytes Sent"); ?></td> + <td class="listhdrr"><?=gettext("Bytes Rcvd"); ?></td> + <td class="listhdrr"><?=gettext("Service"); ?></td> + </tr> + +<?php + foreach ($sk_servers as $sk_server): +?> + <tr id="<?php echo "r:{$sk_server['port']}:{$sk_server['vpnid']}"; ?>"> + <td class="listlr"> + <?=$sk_server['name'];?> + </td> + <td class="listr"> + <?=$sk_server['status'];?> + </td> + <td class="listr"> + <?=$sk_server['connect_time'];?> + </td> + <td class="listr"> + <?=$sk_server['virtual_addr'];?> + </td> + <td class="listr"> + <?=$sk_server['remote_host'];?> + </td> + <td class="listr"> + <?=format_bytes($sk_server['bytes_sent']);?> + </td> + <td class="listr"> + <?=format_bytes($sk_server['bytes_recv']);?> + </td> + <td class="listr"> + <table> + <tr> + <td><?php $ssvc = find_service_by_openvpn_vpnid($sk_server['vpnid']); ?> + <?= get_service_status_icon($ssvc, false, true); ?> + <?= get_service_control_links($ssvc, true); ?> + </td> + </tr> + </table> + </td> + </tr> +<?php + endforeach; +?> + </table> + </td> + </tr> +</table> + +<?php + } +?> +<br /> +<?php + if (!empty($clients)) { +?> +<table style="padding-top:0px; padding-bottom:0px; padding-left:0px; padding-right:0px" width="100%" border="0" cellpadding="0" cellspacing="0" summary="client stats"> + <tr> + <td colspan="6" class="listtopic"> + <?=gettext("Client Instance Statistics"); ?> + </td> + </tr> + <tr> + <td> + <table style="padding-top:0px; padding-bottom:0px; padding-left:0px; padding-right:0px" class="tabcont sortable" width="100%" border="0" cellpadding="0" cellspacing="0" summary="results"> + <tr> + <td class="listhdrr"><?=gettext("Name"); ?></td> + <td class="listhdrr"><?=gettext("Status"); ?></td> + <td class="listhdrr"><?=gettext("Connected Since"); ?></td> + <td class="listhdrr"><?=gettext("Virtual Addr"); ?></td> + <td class="listhdrr"><?=gettext("Remote Host"); ?></td> + <td class="listhdrr"><?=gettext("Bytes Sent"); ?></td> + <td class="listhdrr"><?=gettext("Bytes Rcvd"); ?></td> + <td class="listhdrr"><?=gettext("Service"); ?></td> + </tr> + +<?php + foreach ($clients as $client): +?> + <tr id="<?php echo "r:{$client['port']}:{$client['vpnid']}"; ?>"> + <td class="listlr"> + <?=$client['name'];?> + </td> + <td class="listr"> + <?=$client['status'];?> + </td> + <td class="listr"> + <?=$client['connect_time'];?> + </td> + <td class="listr"> + <?=$client['virtual_addr'];?> + </td> + <td class="listr"> + <?=$client['remote_host'];?> + </td> + <td class="listr"> + <?=format_bytes($client['bytes_sent']);?> + </td> + <td class="listr"> + <?=format_bytes($client['bytes_recv']);?> + </td> + <td class="listr" height="12"> + <table> + <tr> + <td> + <?php $ssvc = find_service_by_openvpn_vpnid($client['vpnid']); ?> + <?= get_service_status_icon($ssvc, false, true); ?> + <?= get_service_control_links($ssvc, true); ?> + </td> + </tr> + </table> + </td> + </tr> +<?php + endforeach; +?> + </table> + </td> + </tr> +</table> + +<?php +} + +if ($DisplayNote) { + echo "<br /><b>" . gettext("NOTE") . ":</b> " . gettext("If you have custom options that override the management features of OpenVPN on a client or server, they will cause that OpenVPN instance to not work correctly with this status page."); +} + +if ((empty($clients)) && (empty($servers)) && (empty($sk_servers))) { + echo gettext("No OpenVPN instances defined"); +} +?> +</form> + +<?php include("fend.inc"); ?> +<script type="text/javascript"> +//<![CDATA[ +function show_routes(id, buttonid) { + document.getElementById(buttonid).innerHTML=''; + aodiv = document.getElementById(id); + aodiv.style.display = "block"; +} +//]]> +</script> +</body> +</html> diff --git a/src/usr/local/www/status_queues.php b/src/usr/local/www/status_queues.php new file mode 100644 index 0000000..42789bf --- /dev/null +++ b/src/usr/local/www/status_queues.php @@ -0,0 +1,310 @@ +#!/usr/local/bin/php +<?php +/* $Id$ */ +/* + status_queues.php + Part of the pfSense project + Copyright (C) 2004, 2005 Scott Ullrich + Copyright (C) 2009 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/pfctl + pfSense_MODULE: shaper +*/ + +##|+PRIV +##|*IDENT=page-status-trafficshaper-queues +##|*NAME=Status: Traffic shaper: Queues page +##|*DESCR=Allow access to the 'Status: Traffic shaper: Queues' page. +##|*MATCH=status_queues.php* +##|-PRIV + +header("Last-Modified: " . gmdate("D, j M Y H:i:s") . " GMT"); +header("Expires: " . gmdate("D, j M Y H:i:s", time()) . " GMT"); +header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP/1.1 +header("Pragma: no-cache"); // HTTP/1.0 + +require("guiconfig.inc"); +class QueueStats { + public $queuename; + public $queuelength; + public $pps; + public $bandwidth; + public $borrows; + public $suspends; + public $drops; +} +if (!file_exists("{$g['varrun_path']}/qstats.pid") || !isvalidpid("{$g['varrun_path']}/qstats.pid")) { + /* Start in the background so we don't hang up the GUI */ + mwexec_bg("/usr/local/sbin/qstats -p {$g['varrun_path']}/qstats.pid"); + /* Give it a moment to start up */ + sleep(1); +} +$fd = @fsockopen("unix://{$g['varrun_path']}/qstats"); +if (!$fd) { + $error = "Something wrong happened during communication with stat gathering"; +} else { + $stats = ""; + while (!feof($fd)) { + $stats .= fread($fd, 4096); + } + fclose($fd); + @file_put_contents("{$g['tmp_path']}/qstats", $stats); + $altqstats = @parse_xml_config("{$g['tmp_path']}/qstats", array("altqstats")); + if ($altqstats == -1) { + $error = "No queue statistics could be read."; + } +} +if ($_REQUEST['getactivity']) { + $statistics = array(); + $bigger_stat = 0; + $stat_type = $_REQUEST['stats']; + /* build the queue stats. */ + foreach ($altqstats['queue'] as $q) { + statsQueues($q); + } + /* calculate the bigger amount of packets or bandwidth being moved through all queues. */ + if ($stat_type == "0") { + foreach ($statistics as $q) { + if ($bigger_stat < $q->pps) { + $bigger_stat = $q->pps; + } + } + } else { + foreach ($statistics as $q) { + if ($bigger_stat < $q->bandwidth) { + $bigger_stat = $q->bandwidth; + } + } + } + $finscript = ""; + foreach ($statistics as $q) { + if ($stat_type == "0") { + $packet_s = round(150 * (1 - $q->pps / $bigger_stat), 0); + } else { + $packet_s = round(150 * (1 - $q->bandwidth / $bigger_stat), 0); + } + if ($packet_s < 0) { + $packet_s = 0; + } + $finscript .= "jQuery('#queue{$q->queuename}widthb').width('{$packet_s}');"; + $finscript .= "jQuery('#queue{$q->queuename}widtha').width('" . (150 - $packet_s) . "');"; + $finscript .= "jQuery('#queue{$q->queuename}pps').val('" . number_format($q->pps, 1) . "');"; + $finscript .= "jQuery('#queue{$q->queuename}bps').val('" . format_bits($q->bandwidth) . "');"; + $finscript .= "jQuery('#queue{$q->queuename}borrows').val('{$q->borrows}');"; + $finscript .= "jQuery('#queue{$q->queuename}suspends').val('{$q->suspends}');"; + $finscript .= "jQuery('#queue{$q->queuename}drops').val('{$q->drops}');"; + $finscript .= "jQuery('#queue{$q->queuename}length').val('{$q->queuelength}');"; + } + unset($statistics, $altqstats); + header("Content-type: text/javascript"); + echo $finscript; + exit; +} +$pgtitle = array(gettext("Status"), gettext("Traffic shaper"), gettext("Queues")); +$shortcut_section = "trafficshaper"; +include("head.inc"); +?> +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<?php +if (!is_array($config['shaper']['queue']) || count($config['shaper']['queue']) < 1) { + echo gettext("Traffic shaping is not configured."); + include("fend.inc"); + echo "</body></html>"; + exit; +} +?> +<?php if (!$error): ?> +<form action="status_queues.php" method="post"> +<script type="text/javascript"> +//<![CDATA[ + function getqueueactivity() { + var url = "/status_queues.php"; + var pars = "getactivity=yes&stats=" + jQuery("#selStatistic").val(); + jQuery.ajax( + url, + { + type: 'post', + data: pars, + complete: activitycallback + }); + } + function activitycallback(transport) { + setTimeout('getqueueactivity()', 5100); + } + jQuery(document).ready(function() { + setTimeout('getqueueactivity()', 150); + }); +//]]> +</script> +<?php endif; ?> +<table width="100%" border="1" cellpadding="0" cellspacing="0" summary="status queues"> +<?php if ($error): ?> + <tr><td><?php echo $error; ?></td></tr> +<?php else: ?> + <tr> + <td class="listhdr"><?=gettext("Queue"); ?></td> + <td class="listhdr"> + <?=gettext("Statistics"); ?> + <select id="selStatistic"> + <option value="0">PPS</option> + <option value="1">Bandwidth</option> + </select> + </td> + <td class="listhdr" width="1%"><?=gettext("PPS"); ?></td> + <td class="listhdr" width="1%"><?=gettext("Bandwidth"); ?></td> + <td class="listhdr" width="1%"><?=gettext("Borrows"); ?></td> + <td class="listhdr" width="1%"><?=gettext("Suspends"); ?></td> + <td class="listhdr" width="1%"><?=gettext("Drops"); ?></td> + <td class="listhdr" width="1%"><?=gettext("Length"); ?></td> + </tr> +<?php + $if_queue_list = get_configured_interface_list_by_realif(false, true); + processQueues($altqstats, 0, ""); +?> +<?php endif; ?> +</table> +<p> + <strong><span class="red"><?=gettext("Note"); ?>:</span></strong><br /> + <?=gettext("Queue graphs take 5 seconds to sample data"); ?>.<br /> + <?=gettext("You can configure the Traffic Shaper"); ?> <a href="/firewall_shaper_wizards.php"><?=gettext("here"); ?></a>. +</p> +<script type="text/javascript"> +//<![CDATA[ + function StatsShowHide(classname) { + var firstrow = jQuery("." + classname).first(); + if (firstrow.is(':visible')) { + jQuery("." + classname).hide(); + } else { + jQuery("." + classname).show(); + } + } +//]]> +</script> +</form> +<?php include("fend.inc"); ?> +</body> +</html> +<?php +function processQueues($altqstats, $level, $parent_name) { + global $g; + global $if_queue_list; + $gray_value = 190 + $level * 10; + if ($gray_value > 250) { + $gray_value = 255; + } + $row_background = str_repeat(dechex($gray_value), 3); + $parent_name = $parent_name . " queuerow" . $altqstats['name'] . $altqstats['interface']; + $prev_if = $altqstats['interface']; + foreach ($altqstats['queue'] as $q) { + $if_name = ""; + foreach ($if_queue_list as $oif => $real_name) { + if ($oif == $q['interface']) { + $if_name = $real_name; + break; + } + } + if ($prev_if != $q['interface']) { + echo "<tr><td colspan=\"8\" style=\"padding: 2px;\"><b>Interface ". htmlspecialchars(convert_real_interface_to_friendly_descr($q['interface'])) . "</b></td></tr>"; + $prev_if = $q['interface']; + } +?> + <tr class="<?php echo $parent_name?>"> + <td bgcolor="#<?php echo $row_background?>" style="padding-left: <?php echo $level * 20?>px;"> + <font color="#000000"> + <? + if (strstr($q['name'], "root_")) { + echo "<a href=\"firewall_shaper.php?interface={$if_name}&queue={$if_name}&action=show\">Root queue</a>"; + } else { + echo "<a href=\"firewall_shaper.php?interface={$if_name}&queue={$q['name']}&action=show\">" . htmlspecialchars($q['name']) . "</a>"; + } + ?> + </font> + </td> +<?php + $cpuUsage = 0; + echo "<td class=\"nowrap\" width=\"1%\" bgcolor=\"#{$row_background}\">"; + echo "<img src='./themes/".$g['theme']."/images/misc/bar_left.gif' height='10' width='4' border='0' align='middle' alt='' />"; + echo "<img src='./themes/".$g['theme']."/images/misc/bar_blue.gif' height='10' name='queue{$q['name']}{$q['interface']}widtha' id='queue{$q['name']}{$q['interface']}widtha' width='" . $cpuUsage . "' border='0' align='middle' alt='" . htmlspecialchars($q['name']) . "' />"; + echo "<img src='./themes/".$g['theme']."/images/misc/bar_gray.gif' height='10' name='queue{$q['name']}{$q['interface']}widthb' id='queue{$q['name']}{$q['interface']}widthb' width='" . (150 - $cpuUsage) . "' border='0' align='middle' alt='" . htmlspecialchars($q['name']) . "' />"; + echo "<img src='./themes/".$g['theme']."/images/misc/bar_right.gif' height='10' width='5' border='0' align='middle' alt='' /> "; + if (is_array($q['queue'])) { + echo "<a href=\"#\" onclick=\"StatsShowHide('queuerow{$q['name']}{$q['interface']}');return false\">+/-</a> "; + } + echo " </td>"; + echo "<td width=\"1%\" bgcolor=\"#{$row_background}\"><input style='border: 0px solid white; background-color:#{$row_background}; color:#000000;width:70px;text-align:right;' size='10' name='queue{$q['name']}{$q['interface']}pps' id='queue{$q['name']}{$q['interface']}pps' value='(" . gettext("Loading") . ")' align='left' /></td>"; + echo "<td width=\"1%\" bgcolor=\"#{$row_background}\"><input style='border: 0px solid white; background-color:#{$row_background}; color:#000000;width:80px;text-align:right;' size='10' name='queue{$q['name']}{$q['interface']}bps' id='queue{$q['name']}{$q['interface']}bps' value='' align='right' /></td>"; + echo "<td width=\"1%\" bgcolor=\"#{$row_background}\"><input style='border: 0px solid white; background-color:#{$row_background}; color:#000000;width:70px;text-align:right;' size='10' name='queue{$q['name']}{$q['interface']}borrows' id='queue{$q['name']}{$q['interface']}borrows' value='' align='right' /></td>"; + echo "<td width=\"1%\" bgcolor=\"#{$row_background}\"><input style='border: 0px solid white; background-color:#{$row_background}; color:#000000;width:70px;text-align:right;' size='10' name='queue{$q['name']}{$q['interface']}suspends' id='queue{$q['name']}{$q['interface']}suspends' value='' align='right' /></td>"; + echo "<td width=\"1%\" bgcolor=\"#{$row_background}\"><input style='border: 0px solid white; background-color:#{$row_background}; color:#000000;width:70px;text-align:right;' size='10' name='queue{$q['name']}{$q['interface']}drops' id='queue{$q['name']}{$q['interface']}drops' value='' align='right' /></td>"; + echo "<td width=\"1%\" bgcolor=\"#{$row_background}\"><input style='border: 0px solid white; background-color:#{$row_background}; color:#000000;width:70px;text-align:right;' size='10' name='queue{$q['name']}{$q['interface']}length' id='queue{$q['name']}{$q['interface']}length' value='' align='right' /></td>"; +?> + </tr> +<?php + if (is_array($q['queue'])) { + processQueues($q, $level + 1, $parent_name); + } + }; +} +function statsQueues($xml) { + global $statistics; + + $current = new QueueStats(); + $child = new QueueStats(); + $current->queuename = $xml['name'] . $xml['interface']; + $current->queuelength = $xml['qlength']; + $current->pps = $xml['measured']; + $current->bandwidth = $xml['measuredspeedint']; + $current->borrows = intval($xml['borrows']); + $current->suspends = intval($xml['suspends']); + $current->drops = intval($xml['droppedpkts']); + if (is_array($xml['queue'])) { + foreach ($xml['queue'] as $q) { + $child = statsQueues($q); + $current->pps += $child->pps; + $current->bandwidth += $child->bandwidth; + $current->borrows += $child->borrows; + $current->suspends += $child->suspends; + $current->drops += $child->drops; + } + } + unset($child); + $statistics[] = $current; + return $current; +} +function format_bits($bits) { + if ($bits >= 1000000000) { + return sprintf("%.2f Gbps", $bits/1000000000); + } else if ($bits >= 1000000) { + return sprintf("%.2f Mbps", $bits/1000000); + } else if ($bits >= 1000) { + return sprintf("%.2f Kbps", $bits/1000); + } else { + return sprintf("%d bps", $bits); + } +} +?> diff --git a/src/usr/local/www/status_rrd_graph.php b/src/usr/local/www/status_rrd_graph.php new file mode 100644 index 0000000..037f521 --- /dev/null +++ b/src/usr/local/www/status_rrd_graph.php @@ -0,0 +1,747 @@ +<?php +/* $Id$ */ +/* + status_rrd_graph.php + Part of pfSense + Copyright (C) 2007 Seth Mos <seth.mos@dds.nl> + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-status-rrdgraphs +##|*NAME=Status: RRD Graphs page +##|*DESCR=Allow access to the 'Status: RRD Graphs' page. +##|*MATCH=status_rrd_graph.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require_once("rrd.inc"); + +unset($input_errors); + +/* if the rrd graphs are not enabled redirect to settings page */ +if (!isset($config['rrd']['enable'])) { + header("Location: status_rrd_graph_settings.php"); +} + +$home = getcwd(); +$rrddbpath = "/var/db/rrd/"; +chdir($rrddbpath); +$databases = glob("*.rrd"); +chdir($home); + +if ($_GET['cat']) { + $curcat = htmlspecialchars($_GET['cat']); +} else { + if (!empty($config['rrd']['category'])) { + $curcat = $config['rrd']['category']; + } else { + $curcat = "system"; + } +} + +if ($_POST['cat']) { + $curcat = htmlspecialchars($_POST['cat']); +} + +if ($_GET['zone']) + $curzone = $_GET['zone']; +else + $curzone = ''; + +if ($_POST['period']) { + $curperiod = $_POST['period']; +} else { + if (!empty($config['rrd']['period'])) { + $curperiod = $config['rrd']['period']; + } else { + $curperiod = "absolute"; + } +} + +if ($_POST['style']) { + $curstyle = $_POST['style']; +} else { + if(! empty($config['rrd']['style'])) { + $curstyle = $config['rrd']['style']; + } else { + $curstyle = "absolute"; + } +} + +if ($_POST['option']) { + $curoption = $_POST['option']; +} else { + switch($curcat) { + case "system": + $curoption = "processor"; + break; + case "queues": + $curoption = "queues"; + break; + case "queuedrops": + $curoption = "queuedrops"; + break; + case "quality": + foreach ($databases as $database) { + if (preg_match("/[-]quality\.rrd/i", $database)) { + /* pick off the 1st database we find that matches the quality graph */ + $name = explode("-", $database); + $curoption = "$name[0]"; + continue 2; + } + } + case "wireless": + foreach ($databases as $database) { + if (preg_match("/[-]wireless\.rrd/i", $database)) { + /* pick off the 1st database we find that matches the wireless graph */ + $name = explode("-", $database); + $curoption = "$name[0]"; + continue 2; + } + } + case "cellular": + foreach ($databases as $database) { + if (preg_match("/[-]cellular\.rrd/i", $database)) { + /* pick off the 1st database we find that matches the celullar graph */ + $name = explode("-", $database); + $curoption = "$name[0]"; + continue 2; + } + } + case "vpnusers": + foreach ($databases as $database) { + if (preg_match("/[-]vpnusers\.rrd/i", $database)) { + /* pick off the 1st database we find that matches the VPN graphs */ + $name = explode("-", $database); + $curoption = "$name[0]"; + continue 2; + } + } + case "captiveportal": + $curoption = "allgraphs"; + break; + case "ntpd": + if (isset($config['ntpd']['statsgraph'])) { + $curoption = "allgraphs"; + } else { + $curoption = "processor"; + $curcat = "system"; + } + break; + default: + $curoption = "wan"; + break; + } +} + +$now = time(); +if ($curcat == "custom") { + if (is_numeric($_GET['start'])) { + if ($start < ($now - (3600 * 24 * 365 * 5))) { + $start = $now - (8 * 3600); + } + + $start = $_POST['start']; + } else if ($_POST['start']) { + $start = strtotime($_POST['start']); + + if ($start === FALSE || $start === -1) { + $input_errors[] = gettext("Invalid start date/time:") . " '{$_POST['start']}'"; + + $start = $now - (8 * 3600); + } + } else { + $start = $now - (8 * 3600); + } +} + +if (is_numeric($_GET['end'])) { + $end = $_GET['end']; +} else if ($_GET['end']) { + $end = strtotime($_GET['end']); + if ($end === FALSE || $end === -1) { + $input_errors[] = gettext("Invalid end date/time:") . " '{$_POST['end']}'"; + + $end = $now; + } +} else { + $end = $now; +} + +/* this should never happen */ +if ($end < $start) { + log_error("start $start is smaller than end $end"); + $end = $now; +} + +$seconds = $end - $start; + +$styles = array('inverse' => gettext('Inverse'), + 'absolute' => gettext('Absolute')); + +/* sort names reverse so WAN comes first */ +rsort($databases); + +/* these boilerplate databases are required for the other menu choices */ +$dbheader = array("allgraphs-traffic.rrd", + "allgraphs-quality.rrd", + "allgraphs-wireless.rrd", + "allgraphs-cellular.rrd", + "allgraphs-vpnusers.rrd", + "allgraphs-packets.rrd", + "system-allgraphs.rrd", + "system-throughput.rrd", + "outbound-quality.rrd", + "outbound-packets.rrd", + "outbound-traffic.rrd"); + +/* additional menu choices for the custom tab */ +$dbheader_custom = array("system-throughput.rrd"); + +foreach ($databases as $database) { + if (stristr($database, "-wireless")) { + $wireless = true; + } + if (stristr($database, "-queues")) { + $queues = true; + } + if (stristr($database, "-cellular") && !empty($config['ppps'])) { + $cellular = true; + } + if (stristr($database, "-vpnusers")) { + $vpnusers = true; + } + if (stristr($database, "captiveportal-") && is_array($config['captiveportal'])) { + $captiveportal = true; + } + if (stristr($database, "ntpd") && isset($config['ntpd']['statsgraph'])) { + $ntpd = true; + } +} +/* append the existing array to the header */ +$ui_databases = array_merge($dbheader, $databases); +$custom_databases = array_merge($dbheader_custom, $databases); + +$graphs = array("eighthour", "day", "week", "month", "quarter", "year", "fouryear"); +$periods = array("absolute" => gettext("Absolute Timespans"), "current" => gettext("Current Period"), "previous" => gettext("Previous Period")); +$graph_length = array( + "eighthour" => 28800, + "day" => 86400, + "week" => 604800, + "month" => 2678400, + "quarter" => 7948800, + "year" => 31622400, + "fouryear" => 126230400); + +$pgtitle = array(gettext("Status"), gettext("RRD Graphs")); + +$closehead = false; + +/* Load all CP zones */ +if ($captiveportal && is_array($config['captiveportal'])) { + $cp_zones_tab_array = array(); + foreach ($config['captiveportal'] as $cpkey => $cp) { + if (!isset($cp['enable'])) { + continue; + } + + if ($curzone == '') { + $tabactive = true; + $curzone = $cpkey; + } elseif ($curzone == $cpkey) { + $tabactive = true; + } else { + $tabactive = false; + } + + $cp_zones_tab_array[] = array($cp['zone'], $tabactive, "status_rrd_graph.php?cat=captiveportal&zone=$cpkey"); + } +} + +function get_dates($curperiod, $graph) { + global $graph_length; + $now = time(); + $end = $now; + + if ($curperiod == "absolute") { + $start = $end - $graph_length[$graph]; + } else { + $curyear = date('Y', $now); + $curmonth = date('m', $now); + $curweek = date('W', $now); + $curweekday = date('N', $now) - 1; // We want to start on monday + $curday = date('d', $now); + $curhour = date('G', $now); + + switch ($curperiod) { + case "previous": + $offset = -1; + break; + default: + $offset = 0; + } + switch ($graph) { + case "eighthour": + if ($curhour < 24) { + $starthour = 16; + } + if ($curhour < 16) { + $starthour = 8; + } + if ($curhour < 8) { + $starthour = 0; + } + + switch ($offset) { + case 0: + $houroffset = $starthour; + break; + default: + $houroffset = $starthour + ($offset * 8); + break; + } + $start = mktime($houroffset, 0, 0, $curmonth, $curday, $curyear); + if ($offset != 0) { + $end = mktime(($houroffset + 8), 0, 0, $curmonth, $curday, $curyear); + } + break; + case "day": + $start = mktime(0, 0, 0, $curmonth, ($curday + $offset), $curyear); + if ($offset != 0) { + $end = mktime(0, 0, 0, $curmonth, (($curday + $offset) + 1), $curyear); + } + break; + case "week": + switch ($offset) { + case 0: + $weekoffset = 0; + break; + default: + $weekoffset = ($offset * 7) - 7; + break; + } + $start = mktime(0, 0, 0, $curmonth, (($curday - $curweekday) + $weekoffset), $curyear); + if ($offset != 0) { + $end = mktime(0, 0, 0, $curmonth, (($curday - $curweekday) + $weekoffset + 7), $curyear); + } + break; + case "month": + $start = mktime(0, 0, 0, ($curmonth + $offset), 0, $curyear); + if ($offset != 0) { + $end = mktime(0, 0, 0, (($curmonth + $offset) + 1), 0, $curyear); + } + break; + case "quarter": + $start = mktime(0, 0, 0, (($curmonth - 2) + $offset), 0, $curyear); + if ($offset != 0) { + $end = mktime(0, 0, 0, (($curmonth + $offset) + 1), 0, $curyear); + } + break; + case "year": + $start = mktime(0, 0, 0, 1, 0, ($curyear + $offset)); + if ($offset != 0) { + $end = mktime(0, 0, 0, 1, 0, (($curyear + $offset) +1)); + } + break; + case "fouryear": + $start = mktime(0, 0, 0, 1, 0, (($curyear - 3) + $offset)); + if ($offset != 0) { + $end = mktime(0, 0, 0, 1, 0, (($curyear + $offset) +1)); + } + break; + } + } + // echo "start $start ". date('l jS \of F Y h:i:s A', $start) .", end $end ". date('l jS \of F Y h:i:s A', $end) ."<br />"; + $dates = array(); + $dates['start'] = $start; + $dates['end'] = $end; + return $dates; +} + +function make_tabs() { + global $curcat; + + $tab_array = array(); + $tab_array[] = array(gettext("System"), ($curcat == "system"), "status_rrd_graph.php?cat=system"); + $tab_array[] = array(gettext("Traffic"), ($curcat == "traffic"), "status_rrd_graph.php?cat=traffic"); + $tab_array[] = array(gettext("Packets"), ($curcat == "packets"), "status_rrd_graph.php?cat=packets"); + $tab_array[] = array(gettext("Quality"), ($curcat == "quality"), "status_rrd_graph.php?cat=quality"); + + if($queues) { + $tab_array[] = array(gettext("Queues"), ($curcat == "queues"), "status_rrd_graph.php?cat=queues"); + $tab_array[] = array(gettext("QueueDrops"), ($curcat == "queuedrops"), "status_rrd_graph.php?cat=queuedrops"); + } + + if($wireless) { + $tab_array[] = array(gettext("Wireless"), ($curcat == "wireless"), "status_rrd_graph.php?cat=wireless"); + } + + if($cellular) { + $tab_array[] = array(gettext("Cellular"), ($curcat == "cellular"), "status_rrd_graph.php?cat=cellular"); + } + + if($vpnusers) { + $tab_array[] = array(gettext("VPN"), ($curcat == "vpnusers"), "status_rrd_graph.php?cat=vpnusers"); + } + + if($captiveportal) { + $tab_array[] = array(gettext("Captive Portal"), ($curcat == "captiveportal"), "status_rrd_graph.php?cat=captiveportal"); + } + + if(isset($config['ntpd']['statsgraph'])) { + $tab_array[] = array("NTP", ($curcat == "ntpd"), "status_rrd_graph.php?cat=ntpd"); + } + + $tab_array[] = array(gettext("Custom"), ($curcat == "custom"), "status_rrd_graph.php?cat=custom"); + $tab_array[] = array(gettext("Settings"), ($curcat == "settings"), "status_rrd_graph_settings.php"); + + return($tab_array); +} + +// Create the selectable list of graphs +function build_options() { + global $curcat, $custom_databases, $ui_databases; + + $optionslist = array(); + + if($curcat == "custom") { + foreach ($custom_databases as $db => $database) { + $optionc = explode("-", $database); + $friendly = convert_friendly_interface_to_friendly_descr(strtolower($optionc[0])); + if (empty($friendly)) { + $friendly = $optionc[0]; + } + + $search = array("-", ".rrd", $optionc[0]); + $replace = array(" :: ", "", $friendly); + $prettyprint = ucwords(str_replace($search, $replace, $database)); + $optionslist[$database] = htmlspecialchars($prettyprint); + } + } + + foreach ($ui_databases as $db => $database) { + if(! preg_match("/($curcat)/i", $database)) + continue; + + if (($curcat == "captiveportal") && !empty($curzone) && !preg_match("/captiveportal-{$curzone}/i", $database)) + continue; + + $optionc = explode("-", $database); + $search = array("-", ".rrd", $optionc); + $replace = array(" :: ", "", $friendly); + + switch($curcat) { + case "captiveportal": + $optionc = str_replace($search, $replace, $optionc[2]); + $prettyprint = ucwords(str_replace($search, $replace, $optionc)); + $optionslist[$optionc] = htmlspecialchars($prettyprint); + break; + case "system": + $optionc = str_replace($search, $replace, $optionc[1]); + $prettyprint = ucwords(str_replace($search, $replace, $optionc)); + $optionslist[$optionc] = htmlspecialchars($prettyprint); + break; + default: + /* Deduce an interface if possible and use the description */ + $optionc = "$optionc[0]"; + $friendly = convert_friendly_interface_to_friendly_descr(strtolower($optionc)); + if(empty($friendly)) { + $friendly = $optionc; + } + $search = array("-", ".rrd", $optionc); + $replace = array(" :: ", "", $friendly); + $prettyprint = ucwords(str_replace($search, $replace, $friendly)); + $optionslist[$optionc] = htmlspecialchars($prettyprint); + } + } + + return($optionslist); +} +include("head.inc"); + +display_top_tabs(make_tabs()); + +if ($input_errors && count($input_errors)) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(new Form_Button( + 'submit', + 'Go!' + )); + +$form->addClass('auto-submit'); + +$section = new Form_Section('Graph settings'); + +$group = new Form_Group('Options'); + +$group->add(new Form_Select( + 'option', + 'Graphs', + $curoption, + build_options() +))->setHelp('Graph'); + +$group->add(new Form_Select( + 'style', + 'Style', + $curstyle, + $styles +))->setHelp('Style'); + +$group->add(new Form_Select( + 'period', + 'Period', + $curperiod, + $periods +))->setHelp('Period'); + +if($curcat == 'custom') + $group->setHelp('Any changes to these option may not take affect until the next auto-refresh.'); + +$section->add($group); + +if($curcat == 'custom') { + + $section->addInput(new Form_Input( + 'cat', + null, + 'hidden', + 'custom' + )); + + $tz = date_default_timezone_get(); + $tz_msg = gettext("Enter date and/or time. Current timezone:") . " $tz"; + $start_fmt = strftime("%m/%d/%Y %H:%M:%S", $start); + $end_fmt = strftime("%m/%d/%Y %H:%M:%S", $end); + + $group = new Form_Group(''); + + $group->add(new Form_Input( + 'start', + 'Start', + 'datetime', + $start_fmt + ))->setHelp('Start'); + + $group->add(new Form_Input( + 'end', + 'End', + 'datetime', + $end_fmt + ))->setHelp('End'); + + if($curcat != 'custom') + $group->setHelp('Any changes to these option may not take affect until the next auto-refresh'); + + $section->add($group); + + $form->add($section); + print($form); + + $curdatabase = $curoption; + $graph = "custom-$curdatabase"; + if(in_array($curdatabase, $custom_databases)) { + $id = "{$graph}-{$curoption}-{$curdatabase}"; + $id = preg_replace('/\./', '_', $id); +?> + <div class="panel panel-default"> + <img align="center" name="<?=$id?>" id="<?=$id?>" alt="<?=$prettydb?> Graph" src="status_rrd_graph_img.php?start=<?=$start?>&end={<?=$end?>&database=<?=$curdatabase?>&style=<?=$curstyle?>&graph=<?=$graph?>" /> + </div> +<?php + + } +} else { + $form->add($section); + print($form); + + foreach($graphs as $graph) { + /* check which databases are valid for our category */ + foreach($ui_databases as $curdatabase) { + if(! preg_match("/($curcat)/i", $curdatabase)) + continue; + + if (($curcat == "captiveportal") && !empty($curzone) && !preg_match("/captiveportal-{$curzone}/i", $curdatabase)) + continue; + + $optionc = explode("-", $curdatabase); + $search = array("-", ".rrd", $optionc); + $replace = array(" :: ", "", $friendly); + + switch($curoption) { + case "outbound": + /* make sure we do not show the placeholder databases in the outbound view */ + if((stristr($curdatabase, "outbound")) || (stristr($curdatabase, "allgraphs"))) { + continue 2; + } + /* only show interfaces with a gateway */ + $optionc = "$optionc[0]"; + if(!interface_has_gateway($optionc)) { + if(!isset($gateways_arr)) { + if(preg_match("/quality/i", $curdatabase)) + $gateways_arr = return_gateways_array(); + else + $gateways_arr = array(); + } + $found_gateway = false; + foreach ($gateways_arr as $gw) { + if ($gw['name'] == $optionc) { + $found_gateway = true; + break; + } + } + if(!$found_gateway) { + continue 2; + } + } + + if(! preg_match("/(^$optionc-|-$optionc\\.)/i", $curdatabase)) { + continue 2; + } + break; + case "allgraphs": + /* make sure we do not show the placeholder databases in the all view */ + if((stristr($curdatabase, "outbound")) || (stristr($curdatabase, "allgraphs"))) { + continue 2; + } + break; + default: + /* just use the name here */ + if(! preg_match("/(^$curoption-|-$curoption\\.)/i", $curdatabase)) { + continue 2; + } + } + + if(in_array($curdatabase, $ui_databases)) { + $id = "{$graph}-{$curoption}-{$curdatabase}"; + $id = preg_replace('/\./', '_', $id); + + $dates = get_dates($curperiod, $graph); + $start = $dates['start']; + $end = $dates['end']; +?> + <div class="panel panel-default" align="center"> + <img name="<?=$id?>" id="<?=$id?>" alt="<?=$prettydb?> Graph" src="status_rrd_graph_img.php?start=<?=$start?>&end={<?=$end?>&database=<?=$curdatabase?>&style=<?=$curstyle?>&graph=<?=$graph?>" /> + </div> +<?php + } + } + } +} + +?> +<script type="text/javascript"> +//<![CDATA[ + function update_graph_images() { + //alert('updating'); + var randomid = Math.floor(Math.random()*11); + <?php + foreach($graphs as $graph) { + /* check which databases are valid for our category */ + foreach($ui_databases as $curdatabase) { + if(! stristr($curdatabase, $curcat)) { + continue; + } + $optionc = explode("-", $curdatabase); + $search = array("-", ".rrd", $optionc); + $replace = array(" :: ", "", $friendly); + switch($curoption) { + case "outbound": + /* make sure we do not show the placeholder databases in the outbound view */ + if((stristr($curdatabase, "outbound")) || (stristr($curdatabase, "allgraphs"))) { + continue 2; + } + /* only show interfaces with a gateway */ + $optionc = "$optionc[0]"; + if(!interface_has_gateway($optionc)) { + if(!isset($gateways_arr)) + if(preg_match("/quality/i", $curdatabase)) + $gateways_arr = return_gateways_array(); + else + $gateways_arr = array(); + $found_gateway = false; + foreach ($gateways_arr as $gw) { + if ($gw['name'] == $optionc) { + $found_gateway = true; + break; + } + } + if(!$found_gateway) { + continue 2; + } + } + if(! preg_match("/(^$optionc-|-$optionc\\.)/i", $curdatabase)) { + continue 2; + } + break; + case "allgraphs": + /* make sure we do not show the placeholder databases in the all view */ + if((stristr($curdatabase, "outbound")) || (stristr($curdatabase, "allgraphs"))) { + continue 2; + } + break; + default: + /* just use the name here */ + if(! preg_match("/(^$curoption-|-$curoption\\.)/i", $curdatabase)) { + continue 2; + } + } + $dates = get_dates($curperiod, $graph); + $start = $dates['start']; + if($curperiod == "current") { + $end = $dates['end']; + } + /* generate update events utilizing jQuery('') feature */ + $id = "{$graph}-{$curoption}-{$curdatabase}"; + $id = preg_replace('/\./', '_', $id); + + echo "\n"; + echo "\t\tjQuery('#{$id}').attr('src','status_rrd_graph_img.php?start={$start}&graph={$graph}&database={$curdatabase}&style={$curstyle}&tmp=' + randomid);\n"; + } + } + ?> + window.setTimeout('update_graph_images()', 355000); + } + window.setTimeout('update_graph_images()', 355000); +//]]> +</script> + +<script> +events.push(function(){ + $('.auto-submit').on('change', function(){ + $(this).submit(); + }); +}); +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_rrd_graph_img.php b/src/usr/local/www/status_rrd_graph_img.php new file mode 100644 index 0000000..d28472f --- /dev/null +++ b/src/usr/local/www/status_rrd_graph_img.php @@ -0,0 +1,1278 @@ +<?php +/* $Id$ */ +/* + status_rrd_graph_img.php + Part of pfSense + Copyright (C) 2009 Seth Mos <seth.mos@dds.nl> + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /bin/rm /usr/local/bin/rrdtool + pfSense_MODULE: system +*/ + +require_once("globals.inc"); +require_once("guiconfig.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("rrd.inc"); + +global $g; + +$pgtitle = array(gettext("System"), gettext("RRD Graphs"), gettext("Image viewer")); + +if ($_GET['database']) { + $curdatabase = basename($_GET['database']); + $curdatabase = str_replace(array("<", ">", ";", "&", "'", '"'), "", htmlspecialchars_decode($curdatabase, ENT_QUOTES | ENT_HTML401)); +} else { + $curdatabase = "wan-traffic.rrd"; +} + +if ($_GET['style']) { + $curstyle = $_GET['style']; +} else { + $curstyle = "inverse"; +} + +/* this is used for temp name */ +if ($_GET['graph']) { + $curgraph = str_replace(array("<", ">", ";", "&", "'", '"', '.', '/'), "", htmlspecialchars_decode($_GET['graph'], ENT_QUOTES | ENT_HTML401)); +} else { + $curgraph = "custom"; +} + +$now = time(); + +if (is_numeric($_GET['start'])) { + $start = $_GET['start']; +} else { + $start = $now - (8 * 3600); +} + +if (is_numeric($_GET['end'])) { + $end = $_GET['end']; +} else { + $end = $now; +} + +/* this should never happen */ +if ($end < $start) { + log_error("start $start is smaller than end $end"); + $end = $now; +} + +$seconds = $end - $start; + +$scales = array(); +$scales[14400] = "MINUTE:5:MINUTE:10:MINUTE:30:0:%H%:%M"; +$scales[57600] = "MINUTE:30:HOUR:1:HOUR:1:0:%H"; +$scales[172800] = "HOUR:1:HOUR:6:HOUR:2:0:%H"; +$scales[691200] = "HOUR:2:HOUR:12:DAY:1:0:%D %d"; +$scales[2764800] = "DAY:1:WEEK:1:WEEK:1:0:Week %W"; +$scales[16070400] = "WEEK:1:MONTH:1:MONTH:1:0:%b"; +$scales[42854400] = "MONTH:1:MONTH:1:MONTH:1:0:%b"; + +$archives = array(); +$archives[1] = 1200; +$archives[5] = 720; +$archives[60] = 1860; +$archives[1440] = 2284; + +$defOptions = array( + 'to' => 1, + 'parts' => 1, + 'precision' => 'minute', + 'distance' => FALSE, + 'separator' => ', ' +); + +/* always set the average to the highest value as a fallback */ +$average = 1440 * 60; +foreach ($archives as $rra => $value) { + $archivestart = $now - ($rra * 60 * $value); + if ($archivestart <= $start) { + $average = $rra * 60; + break; + } +} + +foreach ($scales as $scalelength => $value) { + if ($scalelength >= $seconds) { + $scale = $value; + break; + } +} + +// log_error("start $start, end $end, archivestart $archivestart, average $average, scale $scale, seconds $seconds"); + +/* Deduce a interface if possible and use the description */ +$curif = explode("-", $curdatabase); +$curif = "$curif[0]"; +$friendly = convert_friendly_interface_to_friendly_descr(strtolower($curif)); +if ($friendly == "") { + $friendly = $curif; +} +$search = array("-", ".rrd", $curif); +$replace = array(" :: ", "", $friendly); +$prettydb = ucwords(str_replace($search, $replace, $curdatabase)); + +$rrddbpath = "/var/db/rrd/"; +$rrdtmppath = "/tmp/"; +$rrdtool = "/usr/bin/nice -n20 /usr/local/bin/rrdtool"; +$uptime = "/usr/bin/uptime"; +$sed = "/usr/bin/sed"; + +$havg = timeDiff($average, $defOptions); +$hperiod = timeDiff($seconds, $defOptions); +$data = true; + +/* Don't leave it up to RRD Tool to select the RRA and resolution to use. */ +/* Specify the RRA and resolution to use per the graph havg value. */ +switch ($havg) { + case "1 minute": $step = 60; break; + case "5 minutes": $step = 300; break; + case "1 hour": $step = 3600; break; + case "1 day": $step = 86400; break; + default: $step = 0; break; +} + +$rrddbpath = "/var/db/rrd/"; +chdir($rrddbpath); +$databases = glob("*.rrd"); +rsort($databases); + +/* compare bytes/sec counters, divide bps by 8 */ +read_altq_config(); +if ($altq_list_queues[$curif]) { + $altq =& $altq_list_queues[$curif]; + switch ($altq->GetBwscale()) { + case "Gb": + $factor = 1024 * 1024 * 1024; + break; + case "Mb": + $factor = 1024 * 1024; + break; + case "Kb": + $factor = 1024; + break; + case "b": + default: + $factor = 1; + break; + } + $upstream = (($altq->GetBandwidth()*$factor)/8); + if ($upstream != 0) { + $downstream = $upstream; /* XXX: Ugly hack */ + } else { + $downstream = $upstream = 12500000; + } + $upif = $curif; + $downif = "lan"; /* XXX should this be set to something else?! */ +} else { + $altq = null; + $downstream = 12500000; + $upstream = 12500000; + $upif = "wan"; + $downif = "lan"; +} + +$speedlimit = ($upstream + $downstream); + +/* Set default colors explicitly, the theme can then override them below. + This prevents missing colors in themes from crashing the graphs. */ +/* Traffic Outbound Out-P-4, Out-B-4, Out-P-6, Out-B-6 */ +$colortrafficup = array('666666', 'CCCCCC', '2217AA', '625AE7'); + +/* Traffic Inbound In-P-4, In-B-4, In-P-6, In-B-6 */ +$colortrafficdown = array('990000', 'CC0000', 'FFC875', 'FF9900'); + +/* Packets Outbound Out-P-4, Out-B-4, Out-P-6, Out-B-6 */ +$colorpacketsup = array('666666', 'CCCCCC', '2217AA', '625AE7'); + +/* Packets Inbound In-P-4, In-B-4, In-P-6, In-B-6 */ +$colorpacketsdown = array('990000', 'CC0000', 'FFC875', 'FF9900'); + +/* 95th Percentile Lines Out, In */ +$colortraffic95 = array('660000', 'FF0000'); + +/* State Table pfrate, pfstates, pfnat, srcip, dstip */ +$colorstates = array('00AA00', '990000', '0000FF', '000000', 'DD9B00'); + +/* Processor Usage user, nice, system, int, processes */ +$colorprocessor = array('00AA00', '990000', '0000FF', 'DD9B00', '000000'); + +/* Memory Usage active, inact, free, cache, wire */ +$colormemory = array('00AA00', '990000', '0000FF', '666666', 'DD9B00'); + +/* MBUF Usage current, cache, total, max */ +$colormbuf = array('0080FF', '00E344', 'FF0000', '000000'); + +/* Traffic Shaper Queues q1, q2, q3, q4, q5, q6, q7, q8, q9 */ +$colorqueuesup = array('000000', '7B0000', '0080FF', '00E344', 'FF0000', '2217AA', 'FFC875', 'FF9900', 'CC0000'); +$colorqueuesdown = array('000000', '7B7B7B', '999999', 'BBBBBB', 'CCCCCC', 'D9D9D9', 'EEEEEE', 'FFFFFF', 'CCCCCC'); + +$colorqueuesdropup = array('000000', '7B0000', '0080FF', '00E344', 'FF0000', '2217AA', 'FFC875', 'FF9900', 'CC0000'); +$colorqueuesdropdown = array('000000', '7B7B7B', '999999', 'BBBBBB', 'CCCCCC', 'D9D9D9', 'EEEEEE', 'FFFFFF', 'CCCCCC'); + +/* Quality Graph Delay >420, 180-420, 60-180, 20-60, <20, Delay Avg */ +$colorqualityrtt = array('990000', 'a83c3c', 'b36666', 'bd9090', 'cccccc', '000000'); +/* Quality Graph Loss */ +$colorqualityloss = 'ee0000'; + +/* Wireless Graph SNR, Rate, Channel*/ +/* Cellular Graph RSSI, */ +$colorwireless = array('333333', 'a83c3c', '999999'); + +/* SPAMD Times min area, avg area, max area, Time line */ +$colorspamdtime = array('DDDDFF', 'AAAAFF', 'DDDDFF', '000066'); +/* SPAMD Connections max area, min area, min line, max line, avg line */ +$colorspamdconn = array('AA00BB', 'FFFFFF', '660088', 'FFFF88', '006600'); + +/* OpenVPN Users Online Users */ +$colorvpnusers = array('990000'); + +/* NTPD stats offset, clk jit, sys jit, wander */ +$colorntpd = array('0080FF', '00E344', 'FF0000', '000000'); + +/* Captive Portal Total Users Total Users */ +/* Captive Portal Concurrent Concurrent Users */ +$colorcaptiveportalusers = array('990000'); + +/* select theme colors if the inclusion file exists */ +$rrdcolors = "{$g['www_path']}/themes/{$g['theme']}/rrdcolors.inc.php"; +if (file_exists($rrdcolors)) { + include($rrdcolors); +} else { + log_error(sprintf(gettext("rrdcolors.inc.php for theme %s does not exist, using defaults!"), $g['theme'])); +} + +switch ($curstyle) { + case "absolute": + $multiplier = 1; + $AREA = "LINE1"; + break; + default: + $multiplier = -1; + $AREA = "AREA"; + break; +} + +function timeDiff($time, $opt = array()) { + // The default values + $defOptions = array( + 'to' => 0, + 'parts' => 1, + 'precision' => 'second', + 'distance' => TRUE, + 'separator' => ', ' + ); + $opt = array_merge($defOptions, $opt); + // Default to current time if no to point is given + (!$opt['to']) && ($opt['to'] = time()); + // Init an empty string + $str = ''; + // To or From computation + $diff = ($opt['to'] > $time) ? $opt['to'] - $time : $time - $opt['to']; + // An array of label => periods of seconds; + $periods = array( + 'decade' => 315569260, + 'year' => 31539600, + 'month' => 2629744, + 'week' => 604800, + 'day' => 86400, + 'hour' => 3600, + 'minute' => 60, + 'second' => 1 + ); + // 31539600, 31556926, 31622400 + // Round to precision + if ($opt['precision'] != 'second') { + $diff = round(($diff / $periods[$opt['precision']])) * $periods[$opt['precision']]; + } + // Report the value is 'less than 1 ' precision period away + (0 == $diff) && ($str = 'less than 1 ' . $opt['precision']); + // Loop over each period + foreach ($periods as $label => $value) { + // Stitch together the time difference string + (($x = round($diff / $value)) && $opt['parts']--) && $str .= ($str ? $opt['separator'] : '') . ($x .' '. $label. ($x > 1 ? 's' : '')); + // Stop processing if no more parts are going to be reported. + if ($opt['parts'] == 0 || $label == $opt['precision']) { + break; + } + // Get ready for the next pass + $diff -= $x * $value; + } + $opt['distance'] && $str .= ($str && $opt['to'] >= $time) ? ' ago' : ' away'; + return $str; +} + + +if ((strstr($curdatabase, "-traffic.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for traffic stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step --vertical-label \"bits/sec\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + $graphcmd .= "DEF:$curif-in_bytes_pass=$rrddbpath$curdatabase:inpass:AVERAGE:step=$step "; + $graphcmd .= "DEF:$curif-out_bytes_pass=$rrddbpath$curdatabase:outpass:AVERAGE:step=$step "; + $graphcmd .= "DEF:$curif-in_bytes_block=$rrddbpath$curdatabase:inblock:AVERAGE:step=$step "; + $graphcmd .= "DEF:$curif-out_bytes_block=$rrddbpath$curdatabase:outblock:AVERAGE:step=$step "; + + $graphcmd .= "DEF:$curif-in6_bytes_pass=$rrddbpath$curdatabase:inpass6:AVERAGE:step=$step "; + $graphcmd .= "DEF:$curif-out6_bytes_pass=$rrddbpath$curdatabase:outpass6:AVERAGE:step=$step "; + $graphcmd .= "DEF:$curif-in6_bytes_block=$rrddbpath$curdatabase:inblock6:AVERAGE:step=$step "; + $graphcmd .= "DEF:$curif-out6_bytes_block=$rrddbpath$curdatabase:outblock6:AVERAGE:step=$step "; + + $graphcmd .= "CDEF:\"$curif-in_bits_pass=$curif-in_bytes_pass,8,*\" "; + $graphcmd .= "CDEF:\"$curif-out_bits_pass=$curif-out_bytes_pass,8,*\" "; + $graphcmd .= "CDEF:\"$curif-in_bits_block=$curif-in_bytes_block,8,*\" "; + $graphcmd .= "CDEF:\"$curif-out_bits_block=$curif-out_bytes_block,8,*\" "; + + $graphcmd .= "CDEF:\"$curif-in6_bits_pass=$curif-in6_bytes_pass,8,*\" "; + $graphcmd .= "CDEF:\"$curif-out6_bits_pass=$curif-out6_bytes_pass,8,*\" "; + $graphcmd .= "CDEF:\"$curif-in6_bits_block=$curif-in6_bytes_block,8,*\" "; + $graphcmd .= "CDEF:\"$curif-out6_bits_block=$curif-out6_bytes_block,8,*\" "; + + $graphcmd .= "CDEF:\"$curif-in_bytes=$curif-in_bytes_pass,$curif-in_bytes_block,+\" "; + $graphcmd .= "CDEF:\"$curif-out_bytes=$curif-out_bytes_pass,$curif-out_bytes_block,+\" "; + $graphcmd .= "CDEF:\"$curif-in_bits=$curif-in_bits_pass,$curif-in_bits_block,+\" "; + $graphcmd .= "CDEF:\"$curif-out_bits=$curif-out_bits_pass,$curif-out_bits_block,+\" "; + + $graphcmd .= "CDEF:\"$curif-in6_bytes=$curif-in6_bytes_pass,$curif-in6_bytes_block,+\" "; + $graphcmd .= "CDEF:\"$curif-out6_bytes=$curif-out6_bytes_pass,$curif-out6_bytes_block,+\" "; + $graphcmd .= "CDEF:\"$curif-in6_bits=$curif-in6_bits_pass,$curif-in6_bits_block,+\" "; + $graphcmd .= "CDEF:\"$curif-out6_bits=$curif-out6_bits_pass,$curif-out6_bits_block,+\" "; + + $graphcmd .= "CDEF:\"$curif-bits_io=$curif-in_bits,$curif-out_bits,+\" "; + $graphcmd .= "CDEF:\"$curif-out_bits_block_neg=$curif-out_bits_block,$multiplier,*\" "; + $graphcmd .= "CDEF:\"$curif-out_bits_pass_neg=$curif-out_bits_pass,$multiplier,*\" "; + + $graphcmd .= "CDEF:\"$curif-bits6_io=$curif-in6_bits,$curif-out6_bits,+\" "; + $graphcmd .= "CDEF:\"$curif-out6_bits_block_neg=$curif-out6_bits_block,$multiplier,*\" "; + $graphcmd .= "CDEF:\"$curif-out6_bits_pass_neg=$curif-out6_bits_pass,$multiplier,*\" "; + + $graphcmd .= "CDEF:\"$curif-bytes_in_pass=$curif-in_bytes_pass,0,$speedlimit,LIMIT,UN,0,$curif-in_bytes_pass,IF,$average,*\" "; + $graphcmd .= "CDEF:\"$curif-bytes_out_pass=$curif-out_bytes_pass,0,$speedlimit,LIMIT,UN,0,$curif-out_bytes_pass,IF,$average,*\" "; + $graphcmd .= "CDEF:\"$curif-bytes_in_block=$curif-in_bytes_block,0,$speedlimit,LIMIT,UN,0,$curif-in_bytes_block,IF,$average,*\" "; + $graphcmd .= "CDEF:\"$curif-bytes_out_block=$curif-out_bytes_block,0,$speedlimit,LIMIT,UN,0,$curif-out_bytes_block,IF,$average,*\" "; + + $graphcmd .= "CDEF:\"$curif-bytes_in6_pass=$curif-in6_bytes_pass,0,$speedlimit,LIMIT,UN,0,$curif-in6_bytes_pass,IF,$average,*\" "; + $graphcmd .= "CDEF:\"$curif-bytes_out6_pass=$curif-out6_bytes_pass,0,$speedlimit,LIMIT,UN,0,$curif-out6_bytes_pass,IF,$average,*\" "; + $graphcmd .= "CDEF:\"$curif-bytes_in6_block=$curif-in6_bytes_block,0,$speedlimit,LIMIT,UN,0,$curif-in6_bytes_block,IF,$average,*\" "; + $graphcmd .= "CDEF:\"$curif-bytes_out6_block=$curif-out6_bytes_block,0,$speedlimit,LIMIT,UN,0,$curif-out6_bytes_block,IF,$average,*\" "; + + $graphcmd .= "CDEF:\"$curif-bytes_pass=$curif-bytes_in_pass,$curif-bytes_out_pass,+\" "; + $graphcmd .= "CDEF:\"$curif-bytes_block=$curif-bytes_in_block,$curif-bytes_out_block,+\" "; + + $graphcmd .= "CDEF:\"$curif-bytes_pass6=$curif-bytes_in6_pass,$curif-bytes_out6_pass,+\" "; + $graphcmd .= "CDEF:\"$curif-bytes_block6=$curif-bytes_in6_block,$curif-bytes_out6_block,+\" "; + + $graphcmd .= "CDEF:\"$curif-bytes_in_t_pass=$curif-in_bytes_pass,0,$speedlimit,LIMIT,UN,0,$curif-in_bytes_pass,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"$curif-bytes_out_t_pass=$curif-out_bytes_pass,0,$speedlimit,LIMIT,UN,0,$curif-out_bytes_pass,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"$curif-bytes_in_t_block=$curif-in_bytes_block,0,$speedlimit,LIMIT,UN,0,$curif-in_bytes_block,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"$curif-bytes_out_t_block=$curif-out_bytes_block,0,$speedlimit,LIMIT,UN,0,$curif-out_bytes_block,IF,$seconds,*\" "; + + $graphcmd .= "CDEF:\"$curif-bytes_in6_t_pass=$curif-in6_bytes_pass,0,$speedlimit,LIMIT,UN,0,$curif-in6_bytes_pass,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"$curif-bytes_out6_t_pass=$curif-out6_bytes_pass,0,$speedlimit,LIMIT,UN,0,$curif-out6_bytes_pass,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"$curif-bytes_in6_t_block=$curif-in6_bytes_block,0,$speedlimit,LIMIT,UN,0,$curif-in6_bytes_block,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"$curif-bytes_out6_t_block=$curif-out6_bytes_block,0,$speedlimit,LIMIT,UN,0,$curif-out6_bytes_block,IF,$seconds,*\" "; + + $graphcmd .= "CDEF:\"$curif-bytes_t_pass=$curif-bytes_in_t_pass,$curif-bytes_out_t_pass,+\" "; + $graphcmd .= "CDEF:\"$curif-bytes_t_block=$curif-bytes_in_t_block,$curif-bytes_out_t_block,+\" "; + $graphcmd .= "CDEF:\"$curif-bytes_t=$curif-bytes_t_pass,$curif-bytes_t_block,+\" "; + + $graphcmd .= "CDEF:\"$curif-bytes_t_pass6=$curif-bytes_in6_t_pass,$curif-bytes_out6_t_pass,+\" "; + $graphcmd .= "CDEF:\"$curif-bytes_t_block6=$curif-bytes_in6_t_block,$curif-bytes_out6_t_block,+\" "; + $graphcmd .= "CDEF:\"$curif-bytes_t6=$curif-bytes_t_pass6,$curif-bytes_t_block6,+\" "; + $graphcmd .= "VDEF:\"$curif-in_bits_95=$curif-in_bits,95,PERCENT\" "; + $graphcmd .= "CDEF:\"$curif-out_bits_mul=$curif-out_bits,$multiplier,*\" "; + $perc = $multiplier > 0 ? "95" : "5"; + $graphcmd .= "VDEF:\"$curif-out_bits_95=$curif-out_bits_mul,{$perc},PERCENT\" "; + + $graphcmd .= "AREA:\"$curif-in_bits_block#{$colortrafficdown[1]}:$curif-in-block\" "; + $graphcmd .= "AREA:\"$curif-in_bits_pass#{$colortrafficdown[0]}:$curif-in-pass:STACK\" "; + $graphcmd .= "AREA:\"$curif-in6_bits_block#{$colortrafficdown[3]}:$curif-in6-block:STACK\" "; + $graphcmd .= "AREA:\"$curif-in6_bits_pass#{$colortrafficdown[2]}:$curif-in6-pass:STACK\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + + $graphcmd .= "{$AREA}:\"$curif-out_bits_block_neg#{$colortrafficup[1]}:$curif-out-block\" "; + $graphcmd .= "{$AREA}:\"$curif-out_bits_pass_neg#{$colortrafficup[0]}:$curif-out-pass:STACK\" "; + $graphcmd .= "{$AREA}:\"$curif-out6_bits_block_neg#{$colortrafficup[3]}:$curif-out6-block:STACK\" "; + $graphcmd .= "{$AREA}:\"$curif-out6_bits_pass_neg#{$colortrafficup[2]}:$curif-out6-pass:STACK\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "HRULE:\"$curif-in_bits_95#{$colortraffic95[1]}:$curif-in (95%)\" "; + $graphcmd .= "HRULE:\"$curif-out_bits_95#{$colortraffic95[0]}:$curif-out (95%)\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t maximum\t average\t\t current\t period\t 95th percentile\\n\" "; + $graphcmd .= "COMMENT:\"IPv4 in-pass\t\" "; + $graphcmd .= "GPRINT:\"$curif-in_bits_pass:MAX:%7.2lf %sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-in_bits_pass:AVERAGE:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-in_bits_pass:LAST:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-bytes_in_t_pass:AVERAGE:%7.2lf %sB i\" "; + $graphcmd .= "GPRINT:\"$curif-in_bits_95:%7.2lf %sb/s\" "; + + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"IPv4 out-pass\t\" "; + $graphcmd .= "GPRINT:\"$curif-out_bits_pass:MAX:%7.2lf %sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-out_bits_pass:AVERAGE:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-out_bits_pass:LAST:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-bytes_out_t_pass:AVERAGE:%7.2lf %sB o\" "; + $graphcmd .= "GPRINT:\"$curif-out_bits_95:%7.2lf %sb/s\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"IPv4 in-block\t\" "; + $graphcmd .= "GPRINT:\"$curif-in_bits_block:MAX:%7.2lf %sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-in_bits_block:AVERAGE:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-in_bits_block:LAST:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-bytes_in_t_block:AVERAGE:%7.2lf %sB i\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"IPv4 out-block\t\" "; + $graphcmd .= "GPRINT:\"$curif-out_bits_block:MAX:%7.2lf %sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-out_bits_block:AVERAGE:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-out_bits_block:LAST:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-bytes_out_t_block:AVERAGE:%7.2lf %sB o\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"IPv6 in-pass\t\" "; + $graphcmd .= "GPRINT:\"$curif-in6_bits_pass:MAX:%7.2lf %sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-in6_bits_pass:AVERAGE:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-in6_bits_pass:LAST:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-bytes_in6_t_pass:AVERAGE:%7.2lf %sB i\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"IPv6 out-pass\t\" "; + $graphcmd .= "GPRINT:\"$curif-out6_bits_pass:MAX:%7.2lf %sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-out6_bits_pass:AVERAGE:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-out6_bits_pass:LAST:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-bytes_out6_t_pass:AVERAGE:%7.2lf %sB o\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"IPv6 in-block\t\" "; + $graphcmd .= "GPRINT:\"$curif-in6_bits_block:MAX:%7.2lf %sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-in6_bits_block:AVERAGE:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-in6_bits_block:LAST:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-bytes_in6_t_block:AVERAGE:%7.2lf %sB i\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"IPv6 out-block\t\" "; + $graphcmd .= "GPRINT:\"$curif-out6_bits_block:MAX:%7.2lf %sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-out6_bits_block:AVERAGE:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-out6_bits_block:LAST:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"$curif-bytes_out6_t_block:AVERAGE:%7.2lf %sB o\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif (strstr($curdatabase, "-throughput.rrd")) { + /* define graphcmd for throughput stats */ + /* this gathers all interface statistics, the database does not actually exist */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"bits/sec\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + + $iflist = get_configured_interface_list(); + $g = 0; + $operand = ""; + $comma = ""; + $graphtputbip = ""; + $graphtputbop = ""; + $graphtputbtp = ""; + $graphtputbib = ""; + $graphtputbob = ""; + $graphtputbtb = ""; + $graphtputbyip = ""; + $graphtputbyop = ""; + $graphtputbytp = ""; + $graphtputbyib = ""; + $graphtputbyob = ""; + $graphtputbytb = ""; + foreach ($iflist as $ifname) { + /* collect all interface stats */ + $graphcmd .= "DEF:\"{$ifname}-in_bytes_pass={$rrddbpath}{$ifname}-traffic.rrd:inpass:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"{$ifname}-out_bytes_pass={$rrddbpath}{$ifname}-traffic.rrd:outpass:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"{$ifname}-in_bytes_block={$rrddbpath}{$ifname}-traffic.rrd:inblock:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"{$ifname}-out_bytes_block={$rrddbpath}{$ifname}-traffic.rrd:outblock:AVERAGE:step=$step\" "; + + $graphcmd .= "CDEF:\"{$ifname}-in_bytes={$ifname}-in_bytes_pass,{$ifname}-in_bytes_block,+\" "; + $graphcmd .= "CDEF:\"{$ifname}-out_bytes={$ifname}-out_bytes_pass,{$ifname}-out_bytes_block,+\" "; + + $graphcmd .= "CDEF:\"{$ifname}-in_bits_pass={$ifname}-in_bytes_pass,8,*\" "; + $graphcmd .= "CDEF:\"{$ifname}-out_bits_pass={$ifname}-out_bytes_pass,8,*\" "; + $graphcmd .= "CDEF:\"{$ifname}-bits_io_pass={$ifname}-in_bits_pass,{$ifname}-out_bits_pass,+\" "; + + $graphcmd .= "CDEF:\"{$ifname}-in_bits_block={$ifname}-in_bytes_block,8,*\" "; + $graphcmd .= "CDEF:\"{$ifname}-out_bits_block={$ifname}-out_bytes_block,8,*\" "; + $graphcmd .= "CDEF:\"{$ifname}-bits_io_block={$ifname}-in_bits_block,{$ifname}-out_bits_block,+\" "; + + $graphcmd .= "CDEF:\"{$ifname}-bytes_in_pass={$ifname}-in_bytes_pass,0,$speedlimit,LIMIT,UN,0,{$ifname}-in_bytes_pass,IF,$average,*\" "; + $graphcmd .= "CDEF:\"{$ifname}-bytes_out_pass={$ifname}-out_bytes_pass,0,$speedlimit,LIMIT,UN,0,{$ifname}-out_bytes_pass,IF,$average,*\" "; + $graphcmd .= "CDEF:\"{$ifname}-bytes_in_block={$ifname}-in_bytes_block,0,$speedlimit,LIMIT,UN,0,{$ifname}-in_bytes_block,IF,$average,*\" "; + $graphcmd .= "CDEF:\"{$ifname}-bytes_out_block={$ifname}-out_bytes_block,0,$speedlimit,LIMIT,UN,0,{$ifname}-out_bytes_block,IF,$average,*\" "; + $graphcmd .= "CDEF:\"{$ifname}-bytes_pass={$ifname}-bytes_in_pass,{$ifname}-bytes_out_pass,+\" "; + $graphcmd .= "CDEF:\"{$ifname}-bytes_block={$ifname}-bytes_in_pass,{$ifname}-bytes_out_block,+\" "; + + $graphcmd .= "CDEF:\"{$ifname}-bytes_in_t_pass={$ifname}-in_bytes,0,$speedlimit,LIMIT,UN,0,{$ifname}-in_bytes_pass,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"{$ifname}-bytes_in_t_block={$ifname}-in_bytes,0,$speedlimit,LIMIT,UN,0,{$ifname}-in_bytes_block,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"{$ifname}-bytes_out_t_pass={$ifname}-out_bytes,0,$speedlimit,LIMIT,UN,0,{$ifname}-out_bytes_pass,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"{$ifname}-bytes_out_t_block={$ifname}-out_bytes,0,$speedlimit,LIMIT,UN,0,{$ifname}-out_bytes_block,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"{$ifname}-bytes_t_pass={$ifname}-bytes_in_t_pass,{$ifname}-bytes_out_t_pass,+\" "; + $graphcmd .= "CDEF:\"{$ifname}-bytes_t_block={$ifname}-bytes_in_t_block,{$ifname}-bytes_out_t_block,+\" "; + if ($g > 0) { + $operand .= ",+"; + $comma = ","; + } + $graphtputbip .= "{$comma}{$ifname}-in_bits_pass"; + $graphtputbop .= "{$comma}{$ifname}-out_bits_pass"; + $graphtputbtp .= "{$comma}{$ifname}-bits_io_pass"; + $graphtputbib .= "{$comma}{$ifname}-in_bits_block"; + $graphtputbob .= "{$comma}{$ifname}-out_bits_block"; + $graphtputbtb .= "{$comma}{$ifname}-bits_io_block"; + $graphtputbyip .= "{$comma}{$ifname}-bytes_in_t_pass"; + $graphtputbyop .= "{$comma}{$ifname}-bytes_out_t_pass"; + $graphtputbyib .= "{$comma}{$ifname}-bytes_in_t_block"; + $graphtputbyob .= "{$comma}{$ifname}-bytes_out_t_block"; + $graphtputbytp .= "{$comma}{$ifname}-bytes_t_pass"; + $graphtputbytb .= "{$comma}{$ifname}-bytes_t_block"; + $g++; + } + $graphcmd .= "CDEF:\"tput-in_bits_pass={$graphtputbip}{$operand}\" "; + $graphcmd .= "CDEF:\"tput-out_bits_pass={$graphtputbop}{$operand}\" "; + $graphcmd .= "CDEF:\"tput-bits_io_pass={$graphtputbtp}{$operand}\" "; + + $graphcmd .= "CDEF:\"tput-in_bits_block={$graphtputbib}{$operand}\" "; + $graphcmd .= "CDEF:\"tput-out_bits_block={$graphtputbob}{$operand}\" "; + $graphcmd .= "CDEF:\"tput-bits_io_block={$graphtputbtb}{$operand}\" "; + + $graphcmd .= "CDEF:\"tput-out_bits_pass_neg=tput-out_bits_pass,$multiplier,*\" "; + $graphcmd .= "CDEF:\"tput-out_bits_block_neg=tput-out_bits_block,$multiplier,*\" "; + + $graphcmd .= "CDEF:\"tput-bytes_in_t_pass={$graphtputbyip}{$operand}\" "; + $graphcmd .= "CDEF:\"tput-bytes_out_t_pass={$graphtputbyop}{$operand}\" "; + $graphcmd .= "CDEF:\"tput-bytes_t_pass={$graphtputbytp}{$operand}\" "; + + $graphcmd .= "CDEF:\"tput-bytes_in_t_block={$graphtputbyib}{$operand}\" "; + $graphcmd .= "CDEF:\"tput-bytes_out_t_block={$graphtputbyob}{$operand}\" "; + $graphcmd .= "CDEF:\"tput-bytes_t_block={$graphtputbytb}{$operand}\" "; + + $graphcmd .= "AREA:\"tput-in_bits_block#{$colortrafficdown[0]}:in-block \" "; + $graphcmd .= "AREA:\"tput-in_bits_pass#{$colortrafficdown[1]}:in-pass \" "; + + $graphcmd .= "{$AREA}:\"tput-out_bits_block_neg#{$colortrafficup[1]}:out-block \" "; + $graphcmd .= "{$AREA}:\"tput-out_bits_pass_neg#{$colortrafficup[0]}:out-pass \" "; + + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t maximum average current period\\n\" "; + $graphcmd .= "COMMENT:\"in-pass\t\" "; + $graphcmd .= "GPRINT:\"tput-in_bits_pass:MAX:%7.2lf %sb/s\" "; + $graphcmd .= "GPRINT:\"tput-in_bits_pass:AVERAGE:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"tput-in_bits_pass:LAST:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"tput-bytes_in_t_pass:AVERAGE:%7.2lf %sB i\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"out-pass\t\" "; + $graphcmd .= "GPRINT:\"tput-out_bits_pass:MAX:%7.2lf %sb/s\" "; + $graphcmd .= "GPRINT:\"tput-out_bits_pass:AVERAGE:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"tput-out_bits_pass:LAST:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"tput-bytes_out_t_pass:AVERAGE:%7.2lf %sB o\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"in-block\t\" "; + $graphcmd .= "GPRINT:\"tput-in_bits_block:MAX:%7.2lf %sb/s\" "; + $graphcmd .= "GPRINT:\"tput-in_bits_block:AVERAGE:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"tput-in_bits_block:LAST:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"tput-bytes_in_t_block:AVERAGE:%7.2lf %sB i\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"out-block\t\" "; + $graphcmd .= "GPRINT:\"tput-out_bits_block:MAX:%7.2lf %sb/s\" "; + $graphcmd .= "GPRINT:\"tput-out_bits_block:AVERAGE:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"tput-out_bits_block:LAST:%7.2lf %Sb/s\" "; + $graphcmd .= "GPRINT:\"tput-bytes_out_t_block:AVERAGE:%7.2lf %sB o\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-packets.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for packets stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"packets/sec\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + $graphcmd .= "DEF:\"$curif-in_pps_pass=$rrddbpath$curdatabase:inpass:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"$curif-out_pps_pass=$rrddbpath$curdatabase:outpass:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"$curif-in_pps_block=$rrddbpath$curdatabase:inblock:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"$curif-out_pps_block=$rrddbpath$curdatabase:outblock:AVERAGE:step=$step\" "; + + $graphcmd .= "DEF:\"$curif-in6_pps_pass=$rrddbpath$curdatabase:inpass6:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"$curif-out6_pps_pass=$rrddbpath$curdatabase:outpass6:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"$curif-in6_pps_block=$rrddbpath$curdatabase:inblock6:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"$curif-out6_pps_block=$rrddbpath$curdatabase:outblock6:AVERAGE:step=$step\" "; + + $graphcmd .= "CDEF:\"$curif-in_pps=$curif-in_pps_pass,$curif-in_pps_block,+\" "; + $graphcmd .= "CDEF:\"$curif-out_pps=$curif-out_pps_pass,$curif-out_pps_block,+\" "; + $graphcmd .= "CDEF:\"$curif-out_pps_pass_neg=$curif-out_pps_pass,$multiplier,*\" "; + $graphcmd .= "CDEF:\"$curif-out_pps_block_neg=$curif-out_pps_block,$multiplier,*\" "; + + $graphcmd .= "CDEF:\"$curif-in6_pps=$curif-in6_pps_pass,$curif-in6_pps_block,+\" "; + $graphcmd .= "CDEF:\"$curif-out6_pps=$curif-out6_pps_pass,$curif-out6_pps_block,+\" "; + $graphcmd .= "CDEF:\"$curif-out6_pps_pass_neg=$curif-out6_pps_pass,$multiplier,*\" "; + $graphcmd .= "CDEF:\"$curif-out6_pps_block_neg=$curif-out6_pps_block,$multiplier,*\" "; + + $graphcmd .= "CDEF:\"$curif-pps_in_pass=$curif-in_pps_pass,0,12500000,LIMIT,UN,0,$curif-in_pps_pass,IF,$average,*\" "; + $graphcmd .= "CDEF:\"$curif-pps_out_pass=$curif-out_pps_pass,0,12500000,LIMIT,UN,0,$curif-out_pps_pass,IF,$average,*\" "; + $graphcmd .= "CDEF:\"$curif-pps_in_block=$curif-in_pps_block,0,12500000,LIMIT,UN,0,$curif-in_pps_block,IF,$average,*\" "; + $graphcmd .= "CDEF:\"$curif-pps_out_block=$curif-out_pps_block,0,12500000,LIMIT,UN,0,$curif-out_pps_block,IF,$average,*\" "; + + $graphcmd .= "CDEF:\"$curif-pps_in6_pass=$curif-in6_pps_pass,0,12500000,LIMIT,UN,0,$curif-in6_pps_pass,IF,$average,*\" "; + $graphcmd .= "CDEF:\"$curif-pps_out6_pass=$curif-out6_pps_pass,0,12500000,LIMIT,UN,0,$curif-out6_pps_pass,IF,$average,*\" "; + $graphcmd .= "CDEF:\"$curif-pps_in6_block=$curif-in6_pps_block,0,12500000,LIMIT,UN,0,$curif-in6_pps_block,IF,$average,*\" "; + $graphcmd .= "CDEF:\"$curif-pps_out6_block=$curif-out6_pps_block,0,12500000,LIMIT,UN,0,$curif-out6_pps_block,IF,$average,*\" "; + + $graphcmd .= "CDEF:\"$curif-pps_io=$curif-in_pps,$curif-out_pps,+\" "; + $graphcmd .= "CDEF:\"$curif-pps_pass=$curif-pps_in_pass,$curif-pps_out_pass,+\" "; + $graphcmd .= "CDEF:\"$curif-pps_block=$curif-pps_in_block,$curif-pps_out_block,+\" "; + + $graphcmd .= "CDEF:\"$curif-pps_io6=$curif-in6_pps,$curif-out6_pps,+\" "; + $graphcmd .= "CDEF:\"$curif-pps_pass6=$curif-pps_in6_pass,$curif-pps_out6_pass,+\" "; + $graphcmd .= "CDEF:\"$curif-pps_block6=$curif-pps_in6_block,$curif-pps_out6_block,+\" "; + + $graphcmd .= "CDEF:\"$curif-pps_in_t_pass=$curif-in_pps_pass,0,12500000,LIMIT,UN,0,$curif-in_pps_pass,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"$curif-pps_out_t_pass=$curif-out_pps_pass,0,12500000,LIMIT,UN,0,$curif-out_pps_pass,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"$curif-pps_in_t_block=$curif-in_pps_block,0,12500000,LIMIT,UN,0,$curif-in_pps_block,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"$curif-pps_out_t_block=$curif-out_pps_block,0,12500000,LIMIT,UN,0,$curif-out_pps_block,IF,$seconds,*\" "; + + $graphcmd .= "CDEF:\"$curif-pps_in6_t_pass=$curif-in6_pps_pass,0,12500000,LIMIT,UN,0,$curif-in6_pps_pass,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"$curif-pps_out6_t_pass=$curif-out6_pps_pass,0,12500000,LIMIT,UN,0,$curif-out6_pps_pass,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"$curif-pps_in6_t_block=$curif-in6_pps_block,0,12500000,LIMIT,UN,0,$curif-in6_pps_block,IF,$seconds,*\" "; + $graphcmd .= "CDEF:\"$curif-pps_out6_t_block=$curif-out6_pps_block,0,12500000,LIMIT,UN,0,$curif-out6_pps_block,IF,$seconds,*\" "; + + $graphcmd .= "CDEF:\"$curif-pps_t_pass=$curif-pps_in_t_pass,$curif-pps_out_t_pass,+\" "; + $graphcmd .= "CDEF:\"$curif-pps_t_block=$curif-pps_in_t_block,$curif-pps_out_t_block,+\" "; + + $graphcmd .= "CDEF:\"$curif-pps_t_pass6=$curif-pps_in6_t_pass,$curif-pps_out6_t_pass,+\" "; + $graphcmd .= "CDEF:\"$curif-pps_t_block6=$curif-pps_in6_t_block,$curif-pps_out6_t_block,+\" "; + + $graphcmd .= "AREA:\"$curif-in_pps_block#{$colorpacketsdown[1]}:$curif-in-block\" "; + $graphcmd .= "AREA:\"$curif-in_pps_pass#{$colorpacketsdown[0]}:$curif-in-pass:STACK\" "; + $graphcmd .= "AREA:\"$curif-in6_pps_block#{$colorpacketsdown[3]}:$curif-in6-block:STACK\" "; + $graphcmd .= "AREA:\"$curif-in6_pps_pass#{$colorpacketsdown[2]}:$curif-in6-pass:STACK\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "$AREA:\"$curif-out_pps_block_neg#{$colorpacketsup[1]}:$curif-out-block\" "; + $graphcmd .= "$AREA:\"$curif-out_pps_pass_neg#{$colorpacketsup[0]}:$curif-out-pass:STACK\" "; + $graphcmd .= "$AREA:\"$curif-out6_pps_block_neg#{$colorpacketsup[3]}:$curif-out6-block:STACK\" "; + $graphcmd .= "$AREA:\"$curif-out6_pps_pass_neg#{$colorpacketsup[2]}:$curif-out6-pass:STACK\" "; + + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t maximum\t\t average\t current\t period\\n\" "; + $graphcmd .= "COMMENT:\"in-pass\t\" "; + $graphcmd .= "GPRINT:\"$curif-in_pps_pass:MAX:%7.2lf %s pps\" "; + $graphcmd .= "GPRINT:\"$curif-in_pps_pass:AVERAGE:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-in_pps_pass:LAST:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-pps_in_t_pass:AVERAGE:%7.2lf %s pkts\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"out-pass\t\" "; + $graphcmd .= "GPRINT:\"$curif-out_pps_pass:MAX:%7.2lf %s pps\" "; + $graphcmd .= "GPRINT:\"$curif-out_pps_pass:AVERAGE:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-out_pps_pass:LAST:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-pps_out_t_pass:AVERAGE:%7.2lf %s pkts\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"in-block\t\" "; + $graphcmd .= "GPRINT:\"$curif-in_pps_block:MAX:%7.2lf %s pps\" "; + $graphcmd .= "GPRINT:\"$curif-in_pps_block:AVERAGE:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-in_pps_block:LAST:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-pps_in_t_block:AVERAGE:%7.2lf %s pkts\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"out-block\t\" "; + $graphcmd .= "GPRINT:\"$curif-out_pps_block:MAX:%7.2lf %s pps\" "; + $graphcmd .= "GPRINT:\"$curif-out_pps_block:AVERAGE:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-out_pps_block:LAST:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-pps_out_t_block:AVERAGE:%7.2lf %s pkts\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"in-pass6\t\" "; + $graphcmd .= "GPRINT:\"$curif-in6_pps_pass:MAX:%7.2lf %s pps\" "; + $graphcmd .= "GPRINT:\"$curif-in6_pps_pass:AVERAGE:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-in6_pps_pass:LAST:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-pps_in6_t_pass:AVERAGE:%7.2lf %s pkts\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"out-pass6\t\" "; + $graphcmd .= "GPRINT:\"$curif-out6_pps_pass:MAX:%7.2lf %s pps\" "; + $graphcmd .= "GPRINT:\"$curif-out6_pps_pass:AVERAGE:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-out6_pps_pass:LAST:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-pps_out6_t_pass:AVERAGE:%7.2lf %s pkts\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"in-block6\t\" "; + $graphcmd .= "GPRINT:\"$curif-in6_pps_block:MAX:%7.2lf %s pps\" "; + $graphcmd .= "GPRINT:\"$curif-in6_pps_block:AVERAGE:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-in6_pps_block:LAST:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-pps_in6_t_block:AVERAGE:%7.2lf %s pkts\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"out-pass6\t\" "; + $graphcmd .= "GPRINT:\"$curif-out6_pps_block:MAX:%7.2lf %s pps\" "; + $graphcmd .= "GPRINT:\"$curif-out6_pps_block:AVERAGE:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-out6_pps_block:LAST:%7.2lf %S pps\" "; + $graphcmd .= "GPRINT:\"$curif-pps_out6_t_block:AVERAGE:%7.2lf %s pkts\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-wireless.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for packets stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"snr/channel/rate\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + $graphcmd .= "DEF:\"$curif-snr=$rrddbpath$curdatabase:snr:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"$curif-rate=$rrddbpath$curdatabase:rate:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"$curif-channel=$rrddbpath$curdatabase:channel:AVERAGE:step=$step\" "; + $graphcmd .= "LINE2:\"$curif-snr#{$colorwireless[0]}:$curif-snr\" "; + $graphcmd .= "LINE2:\"$curif-rate#{$colorwireless[1]}:$curif-rate\" "; + $graphcmd .= "LINE2:\"$curif-channel#{$colorwireless[2]}:$curif-channel\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t maximum\t\t average\t current\\n\" "; + $graphcmd .= "COMMENT:\"SNR\t\t\" "; + $graphcmd .= "GPRINT:\"$curif-snr:MAX:%7.2lf dBi \" "; + $graphcmd .= "GPRINT:\"$curif-snr:AVERAGE:%7.2lf dBi \" "; + $graphcmd .= "GPRINT:\"$curif-snr:LAST:%7.2lf dBi\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"RATE\t\t\" "; + $graphcmd .= "GPRINT:\"$curif-rate:MAX:%7.2lf Mb \" "; + $graphcmd .= "GPRINT:\"$curif-rate:AVERAGE:%7.2lf Mb \" "; + $graphcmd .= "GPRINT:\"$curif-rate:LAST:%7.2lf Mb\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Channel\t\" "; + $graphcmd .= "GPRINT:\"$curif-channel:MAX:%7.2lf \" "; + $graphcmd .= "GPRINT:\"$curif-channel:AVERAGE:%7.2lf \" "; + $graphcmd .= "GPRINT:\"$curif-channel:LAST:%7.2lf\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-vpnusers.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for vpn users stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"users\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + $graphcmd .= "DEF:\"$curif-users=$rrddbpath$curdatabase:users:AVERAGE:step=$step\" "; + $graphcmd .= "LINE2:\"$curif-users#{$colorvpnusers[0]}:$curif-users\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t maximum\t\t average\t current\\n\" "; + $graphcmd .= "COMMENT:\"Users Online\t\" "; + $graphcmd .= "GPRINT:\"$curif-users:MAX:%7.2lf \" "; + $graphcmd .= "GPRINT:\"$curif-users:AVERAGE:%7.2lf \" "; + $graphcmd .= "GPRINT:\"$curif-users:LAST:%7.2lf \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-states.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for states stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start -$seconds -e -$average --step $step "; + $graphcmd .= "--vertical-label \"states, ip\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + $graphcmd .= "DEF:\"$curif-pfrate=$rrddbpath$curdatabase:pfrate:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"$curif-pfstates=$rrddbpath$curdatabase:pfstates:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"$curif-pfnat=$rrddbpath$curdatabase:pfnat:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"$curif-srcip=$rrddbpath$curdatabase:srcip:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"$curif-dstip=$rrddbpath$curdatabase:dstip:AVERAGE:step=$step\" "; + $graphcmd .= "CDEF:\"$curif-pfrate_t=$curif-pfrate,0,1000000,LIMIT,UN,0,$curif-pfrate,IF,$seconds,*\" "; + $graphcmd .= "LINE1:\"$curif-pfrate#{$colorstates[0]}:$curif-pfrate\" "; + $graphcmd .= "LINE1:\"$curif-pfstates#{$colorstates[1]}:$curif-pfstates\" "; + $graphcmd .= "LINE1:\"$curif-pfnat#{$colorstates[2]}:$curif-pfnat\" "; + $graphcmd .= "LINE1:\"$curif-srcip#{$colorstates[3]}:$curif-srcip\" "; + $graphcmd .= "LINE1:\"$curif-dstip#{$colorstates[4]}:$curif-dstip\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t minimum average maximum current period\\n\" "; + $graphcmd .= "COMMENT:\"state changes\" "; + $graphcmd .= "GPRINT:\"$curif-pfrate:MIN:%7.2lf %s cps\" "; + $graphcmd .= "GPRINT:\"$curif-pfrate:AVERAGE:%7.2lf %s cps\" "; + $graphcmd .= "GPRINT:\"$curif-pfrate:MAX:%7.2lf %s cps\" "; + $graphcmd .= "GPRINT:\"$curif-pfrate:LAST:%7.2lf %S cps\" "; + $graphcmd .= "GPRINT:\"$curif-pfrate_t:AVERAGE:%7.2lf %s chg\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"filter states\" "; + $graphcmd .= "GPRINT:\"$curif-pfstates:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"$curif-pfstates:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"$curif-pfstates:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"$curif-pfstates:LAST:%7.2lf %s \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"nat states \" "; + $graphcmd .= "GPRINT:\"$curif-pfnat:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"$curif-pfnat:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"$curif-pfnat:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"$curif-pfnat:LAST:%7.2lf %s \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Source addr. \" "; + $graphcmd .= "GPRINT:\"$curif-srcip:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"$curif-srcip:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"$curif-srcip:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"$curif-srcip:LAST:%7.2lf %s \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Dest. addr. \" "; + $graphcmd .= "GPRINT:\"$curif-dstip:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"$curif-dstip:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"$curif-dstip:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"$curif-dstip:LAST:%7.2lf %s \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-processor.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for processor stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"utilization, number\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + $graphcmd .= "DEF:\"user=$rrddbpath$curdatabase:user:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"nice=$rrddbpath$curdatabase:nice:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"system=$rrddbpath$curdatabase:system:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"interrupt=$rrddbpath$curdatabase:interrupt:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"processes=$rrddbpath$curdatabase:processes:AVERAGE:step=$step\" "; + $graphcmd .= "AREA:\"user#{$colorprocessor[0]}:user\" "; + $graphcmd .= "AREA:\"nice#{$colorprocessor[1]}:nice:STACK\" "; + $graphcmd .= "AREA:\"system#{$colorprocessor[2]}:system:STACK\" "; + $graphcmd .= "AREA:\"interrupt#{$colorprocessor[3]}:interrupt:STACK\" "; + $graphcmd .= "LINE2:\"processes#{$colorprocessor[4]}:processes\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t minimum average maximum current\\n\" "; + $graphcmd .= "COMMENT:\"User util. \" "; + $graphcmd .= "GPRINT:\"user:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"user:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"user:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"user:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Nice util. \" "; + $graphcmd .= "GPRINT:\"nice:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"nice:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"nice:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"nice:LAST:%7.2lf %s \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"System util. \" "; + $graphcmd .= "GPRINT:\"system:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"system:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"system:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"system:LAST:%7.2lf %s \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Interrupt \" "; + $graphcmd .= "GPRINT:\"interrupt:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"interrupt:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"interrupt:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"interrupt:LAST:%7.2lf %s \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Processes \" "; + $graphcmd .= "GPRINT:\"processes:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"processes:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"processes:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"processes:LAST:%7.2lf %s \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-memory.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for memory usage stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"utilization, percent\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + $graphcmd .= "DEF:\"active=$rrddbpath$curdatabase:active:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"inactive=$rrddbpath$curdatabase:inactive:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"free=$rrddbpath$curdatabase:free:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"cache=$rrddbpath$curdatabase:cache:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"wire=$rrddbpath$curdatabase:wire:AVERAGE:step=$step\" "; + $graphcmd .= "LINE2:\"active#{$colormemory[0]}:active\" "; + $graphcmd .= "LINE2:\"inactive#{$colormemory[1]}:inactive\" "; + $graphcmd .= "LINE2:\"free#{$colormemory[2]}:free\" "; + $graphcmd .= "LINE2:\"cache#{$colormemory[3]}:cache\" "; + $graphcmd .= "LINE2:\"wire#{$colormemory[4]}:wire\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t minimum average maximum current\\n\" "; + $graphcmd .= "COMMENT:\"Active. \" "; + $graphcmd .= "GPRINT:\"active:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"active:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"active:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"active:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Inactive. \" "; + $graphcmd .= "GPRINT:\"inactive:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"inactive:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"inactive:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"inactive:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Free. \" "; + $graphcmd .= "GPRINT:\"free:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"free:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"free:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"free:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Cached. \" "; + $graphcmd .= "GPRINT:\"cache:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"cache:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"cache:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"cache:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Wired. \" "; + $graphcmd .= "GPRINT:\"wire:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"wire:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"wire:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"wire:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-mbuf.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for mbuf usage stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"utilization, percent\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} clusters - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + $graphcmd .= "DEF:\"current=$rrddbpath$curdatabase:current:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"cache=$rrddbpath$curdatabase:cache:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"total=$rrddbpath$curdatabase:total:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"max=$rrddbpath$curdatabase:max:AVERAGE:step=$step\" "; + $graphcmd .= "LINE2:\"current#{$colormbuf[0]}:current\" "; + $graphcmd .= "LINE2:\"cache#{$colormbuf[1]}:cache\" "; + $graphcmd .= "LINE2:\"total#{$colormbuf[2]}:total\" "; + $graphcmd .= "LINE2:\"max#{$colormbuf[3]}:max\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t minimum average maximum current\\n\" "; + $graphcmd .= "COMMENT:\"Current. \" "; + $graphcmd .= "GPRINT:\"current:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"current:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"current:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"current:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Cache. \" "; + $graphcmd .= "GPRINT:\"cache:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"cache:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"cache:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"cache:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Total. \" "; + $graphcmd .= "GPRINT:\"total:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"total:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"total:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"total:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Max. \" "; + $graphcmd .= "GPRINT:\"max:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"max:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"max:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"max:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-queues.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for queue stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"bits/sec\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + if ($altq) { + $a_queues =& $altq->get_queue_list(); + $t = 0; + } else { + $a_queues = array(); + $i = 0; + $t = 0; + } + foreach ($a_queues as $name => $q) { + $color = "$colorqueuesup[$t]"; + if ($t > 0) { + $stack = ":STACK"; + } + $graphcmd .= "DEF:\"$name=$rrddbpath$curdatabase:$name:AVERAGE:step=$step\" "; + $graphcmd .= "CDEF:\"$name-bytes_out=$name,0,$speedlimit,LIMIT,UN,0,$name,IF\" "; + $graphcmd .= "CDEF:\"$name-bits_out=$name-bytes_out,8,*\" "; + $graphcmd .= "$AREA:\"$name-bits_out#${color}:$name$stack\" "; + $t++; + if ($t > 7) { + $t = 0; + } + } + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-queuedrops.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for queuedrop stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"drops / sec\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + if ($altq) { + $a_queues =& $altq->get_queue_list(); + $t = 0; + } else { + $a_queues = array(); + $i = 0; + $t = 0; + } + foreach ($a_queues as $name => $q) { + $color = "$colorqueuesdropup[$t]"; + if ($t > 0) { + $stack = ":STACK"; + } + $graphcmd .= "DEF:\"$name=$rrddbpath$curdatabase:$name:AVERAGE:step=$step\" "; + $graphcmd .= "CDEF:\"$name-bytes_out=$name,0,$speedlimit,LIMIT,UN,0,$name,IF\" "; + $graphcmd .= "CDEF:\"$name-bits_out=$name-bytes_out,8,*\" "; + $graphcmd .= "CDEF:\"$name-bits_out_neg=$name-bits_out,$multiplier,*\" "; + $graphcmd .= "$AREA:\"$name-bits_out_neg#${color}:$name$stack\" "; + $t++; + if ($t > 7) { + $t = 0; + } + } + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-quality.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* make a link quality graphcmd */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png \\ + --start $start --end $end --step $step \\ + --title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" \\ + --color SHADEA#eeeeee --color SHADEB#eeeeee \\ + --vertical-label \"ms / %\" \\ + --height 200 --width 620 \\ + --lower-limit 0 \\ + DEF:delayraw=$rrddbpath$curdatabase:delay:AVERAGE:step=$step \\ + DEF:loss=$rrddbpath$curdatabase:loss:AVERAGE:step=$step \\ + \"CDEF:delay=delayraw,1000,*\" \\ + \"CDEF:roundavg=delay,PREV(delay),+,2,/\" \\ + \"CDEF:loss10=loss,$multiplier,*\" \\ + \"CDEF:r0=delay,20,MIN\" \\ + \"CDEF:r1=delay,60,MIN\" \\ + \"CDEF:r2=delay,180,MIN\" \\ + \"CDEF:r3=delay,420,MIN\" \\ + COMMENT:\"\t\t\t\t\tDelay\t\t\tPacket loss\\n\" \\ + AREA:delay#$colorqualityrtt[0]:\"> 420 ms\" \\ + GPRINT:delay:MIN:\"\t\tMin\\: %7.2lf ms\" \\ + GPRINT:loss:MIN:\"\tMin\\: %3.1lf %%\\n\" \\ + AREA:r3#$colorqualityrtt[1]:\"180-420 ms\" \\ + GPRINT:delay:AVERAGE:\"\t\tAvg\\: %7.2lf ms\" \\ + GPRINT:loss:AVERAGE:\"\tAvg\\: %3.1lf %%\\n\" \\ + AREA:r2#$colorqualityrtt[2]:\"60-180 ms\" \\ + GPRINT:delay:MAX:\"\t\tMax\\: %7.2lf ms\" \\ + GPRINT:loss:MAX:\"\tMax\\: %3.1lf %%\\n\" \\ + AREA:r1#$colorqualityrtt[3]:\"20-60 ms\\n\" \\ + AREA:r0#$colorqualityrtt[4]:\"< 20 ms\" \\ + GPRINT:delay:LAST:\"\t\tLast\\: %7.2lf ms\" \\ + GPRINT:loss:LAST:\"\tLast\: %3.1lf %%\\n\" \\ + AREA:loss10#$colorqualityloss:\"Packet loss\\n\" \\ + LINE1:delay#$colorqualityrtt[5]:\"Delay average\\n\" \\ + COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\""; +} elseif ((strstr($curdatabase, "spamd.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* graph a spamd statistics graph */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png \\ + --start $start --end $end --step $step \\ + --title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" \\ + --color SHADEA#eeeeee --color SHADEB#eeeeee \\ + --vertical-label=\"Conn / Time, sec.\" \\ + --height 200 --width 620 --no-gridfit \\ + --lower-limit 0 \\ + DEF:consmin=$rrddbpath$curdatabase:conn:MIN:step=$step \\ + DEF:consavg=$rrddbpath$curdatabase:conn:AVERAGE:step=$step \\ + DEF:consmax=$rrddbpath$curdatabase:conn:MAX:step=$step \\ + DEF:timemin=$rrddbpath$curdatabase:time:MIN:step=$step \\ + DEF:timeavg=$rrddbpath$curdatabase:time:AVERAGE:step=$step \\ + DEF:timemax=$rrddbpath$curdatabase:time:MAX:step=$step \\ + \"CDEF:timeminadj=timemin,0,86400,LIMIT,UN,0,timemin,IF\" \\ + \"CDEF:timeavgadj=timeavg,0,86400,LIMIT,UN,0,timeavg,IF\" \\ + \"CDEF:timemaxadj=timemax,0,86400,LIMIT,UN,0,timemax,IF\" \\ + \"CDEF:t1=timeminadj,timeavgadj,+,2,/,timeminadj,-\" \\ + \"CDEF:t2=timeavgadj,timemaxadj,+,2,/,timeminadj,-,t1,-\" \\ + \"CDEF:t3=timemaxadj,timeminadj,-,t1,-,t2,-\" \\ + AREA:timeminadj \\ + AREA:t1#$colorspamdtime[0]::STACK \\ + AREA:t2#$colorspamdtime[1]::STACK \\ + AREA:t3#$colorspamdtime[2]::STACK \\ + LINE2:timeavgadj#$colorspamdtime[3]:\"Time \" \\ + GPRINT:timeminadj:MIN:\"Min\\:%6.2lf\\t\" \\ + GPRINT:timeavgadj:AVERAGE:\"Avg\\:%6.2lf\\t\" \\ + GPRINT:timemaxadj:MAX:\"Max\\:%6.2lf\\n\" \\ + AREA:consmax#$colorspamdconn[0] \\ + AREA:consmin#$colorspamdconn[1] \\ + LINE1:consmin#$colorspamdconn[2] \\ + LINE1:consmax#$colorspamdconn[3] \\ + LINE1:consavg#$colorspamdconn[4]:\"Cons \" \\ + GPRINT:consmin:MIN:\"Min\\:%6.2lf\\t\" \\ + GPRINT:consavg:AVERAGE:\"Avg\\:%6.2lf\\t\" \\ + GPRINT:consmax:MAX:\"Max\\:%6.2lf\\n\" \\ + COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-cellular.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"signal\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + $graphcmd .= "DEF:\"$curif-rssi=$rrddbpath$curdatabase:rssi:AVERAGE:step=$step\" "; + $graphcmd .= "LINE2:\"$curif-rssi#{$colorwireless[0]}:$curif-rssi\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t maximum\t\t average\t current\\n\" "; + $graphcmd .= "COMMENT:\"RSSI\t\t\" "; + $graphcmd .= "GPRINT:\"$curif-rssi:MAX:%7.2lf \" "; + $graphcmd .= "GPRINT:\"$curif-rssi:AVERAGE:%7.2lf \" "; + $graphcmd .= "GPRINT:\"$curif-rssi:LAST:%7.2lf \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-loggedin.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for online Captive Portal users stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"Captive Portal Users\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--base=1000 "; + $graphcmd .= "--lower-limit=0 "; + $graphcmd .= "--slope-mode "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + $graphcmd .= "DEF:\"$curif-loggedinusers=$rrddbpath$curdatabase:loggedinusers:AVERAGE:step=$step\" "; + $graphcmd .= "CDEF:\"$curif-totalusers_t=PREV,UN,0,PREV,IF,$curif-loggedinusers,+\" "; + $graphcmd .= "CDEF:\"$curif-totalusers_d=$curif-totalusers_t,FLOOR\" "; + $graphcmd .= "AREA:\"$curif-totalusers_d#{$colorcaptiveportalusers[0]}:Total logged in users\" "; + $graphcmd .= "GPRINT:\"$curif-totalusers_d:MAX:%8.0lf \\n\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "-concurrent.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for online Captive Portal users stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"Captive Portal Users\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--base=1000 "; + $graphcmd .= "--lower-limit=0 "; + $graphcmd .= "--slope-mode "; + $graphcmd .= "--height 200 --width 620 "; + $graphcmd .= "DEF:\"$curif-concurrentusers=$rrddbpath$curdatabase:concurrentusers:AVERAGE:step=$step\" "; + $graphcmd .= "AREA:\"$curif-concurrentusers#{$colorcaptiveportalusers[0]}:Concurrent Users\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t current\t\t average\t maximum\\n\" "; + $graphcmd .= "COMMENT:\"Users Online\t\" "; + $graphcmd .= "GPRINT:\"$curif-concurrentusers:LAST:%8.0lf \" "; + $graphcmd .= "GPRINT:\"$curif-concurrentusers:AVERAGE:%8.0lf \" "; + $graphcmd .= "GPRINT:\"$curif-concurrentusers:MAX:%8.0lf \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} elseif ((strstr($curdatabase, "ntpd.rrd")) && (file_exists("$rrddbpath$curdatabase"))) { + /* define graphcmd for ntpd (was: mbuf) usage stats */ + $graphcmd = "$rrdtool graph $rrdtmppath$curdatabase-$curgraph.png "; + $graphcmd .= "--start $start --end $end --step $step "; + $graphcmd .= "--vertical-label \"time\" "; + $graphcmd .= "--color SHADEA#eeeeee --color SHADEB#eeeeee "; + $graphcmd .= "--title \"" . php_uname('n') . " - {$prettydb} - {$hperiod} - {$havg} average\" "; + $graphcmd .= "--height 200 --width 620 "; + $graphcmd .= "DEF:\"offset=$rrddbpath$curdatabase:offset:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"sjit=$rrddbpath$curdatabase:sjit:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"cjit=$rrddbpath$curdatabase:cjit:AVERAGE:step=$step\" "; + $graphcmd .= "DEF:\"wander=$rrddbpath$curdatabase:wander:AVERAGE:step=$step\" "; + $graphcmd .= "LINE2:\"offset#{$colorntpd[0]}:offset\" "; + $graphcmd .= "LINE2:\"sjit#{$colorntpd[1]}:sjit\" "; + $graphcmd .= "LINE2:\"cjit#{$colorntpd[2]}:cjit\" "; + $graphcmd .= "LINE2:\"wander#{$colorntpd[3]}:wander\" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t minimum average maximum current\\n\" "; + $graphcmd .= "COMMENT:\"Offset \" "; + $graphcmd .= "GPRINT:\"offset:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"offset:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"offset:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"offset:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"System jitter \" "; + $graphcmd .= "GPRINT:\"sjit:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"sjit:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"sjit:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"sjit:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Clock jitter \" "; + $graphcmd .= "GPRINT:\"cjit:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"cjit:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"cjit:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"cjit:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"Clk freq wander\" "; + $graphcmd .= "GPRINT:\"wander:MIN:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"wander:AVERAGE:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"wander:MAX:%7.2lf %s \" "; + $graphcmd .= "GPRINT:\"wander:LAST:%7.2lf %S \" "; + $graphcmd .= "COMMENT:\"\\n\" "; + $graphcmd .= "COMMENT:\"\t\t\t\t\t\t\t\t\t\t\t\t\t" . strftime('%b %d %H\:%M\:%S %Y') . "\" "; +} else { + $data = false; + log_error(sprintf(gettext("Sorry we do not have data to graph for %s"),$curdatabase)); +} + +/* check modification time to see if we need to generate image */ +if (file_exists("$rrdtmppath$curdatabase-$curgraph.png")) { + if ((time() - filemtime("$rrdtmppath$curdatabase-$curgraph.png")) >= 15) { + if ($data) { + $_gb = exec("$graphcmd 2>&1", $graphcmdoutput, $graphcmdreturn); + $graphcmdoutput = implode(" ", $graphcmdoutput) . $graphcmd; + flush(); + usleep(500); + } + } +} else { + if ($data) { + $_gb = exec("$graphcmd 2>&1", $graphcmdoutput, $graphcmdreturn); + $graphcmdoutput = implode(" ", $graphcmdoutput) . $graphcmd; + flush(); + usleep(500); + } +} +if (($graphcmdreturn <> 0) || (!$data)) { + log_error(sprintf(gettext('Failed to create graph with error code %1$s, the error is: %2$s'), $graphcmdreturn, $graphcmdoutput)); + if (strstr($curdatabase, "queues")) { + log_error(sprintf(gettext("failed to create graph from %s%s, removing database"), $rrddbpath, $curdatabase)); + unlink_if_exists($rrddbpath . $curif . $queues); + flush(); + usleep(500); + enable_rrd_graphing(); + } + if (strstr($curdatabase, "queuesdrop")) { + log_error(sprintf(gettext("failed to create graph from %s%s, removing database"), $rrddbpath, $curdatabase)); + unlink_if_exists($rrddbpath . $curdatabase); + flush(); + usleep(500); + enable_rrd_graphing(); + } + header("Content-type: image/png"); + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + header("Cache-Control: no-cache, no-store, must-revalidate"); + header("Pragma: no-cache"); + $file= "/usr/local/www/themes/{$g['theme']}/images/misc/rrd_error.png"; + readfile($file); +} else { + $file = "$rrdtmppath$curdatabase-$curgraph.png"; + if (file_exists("$file")) { + header("Content-type: image/png"); + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + header("Cache-Control: no-cache, no-store, must-revalidate"); + header("Pragma: no-cache"); + readfile($file); + } +} + +?> diff --git a/src/usr/local/www/status_rrd_graph_settings.php b/src/usr/local/www/status_rrd_graph_settings.php new file mode 100644 index 0000000..359c548 --- /dev/null +++ b/src/usr/local/www/status_rrd_graph_settings.php @@ -0,0 +1,199 @@ +<?php +/* $Id$ */ +/* + status_rrd_graph_settings.php + Part of pfSense + Copyright (C) 2007 Seth Mos <seth.mos@dds.nl> + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/bin/find + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-status-rrdgraph-settings +##|*NAME=Status: RRD Graphs settings page +##|*DESCR=Allow access to the 'Status: RRD Graphs: settings' page. +##|*MATCH=status_rrd_graph_settings.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require_once("rrd.inc"); + +$pconfig['enable'] = isset($config['rrd']['enable']); +$pconfig['category'] = $config['rrd']['category']; +$pconfig['style'] = $config['rrd']['style']; +$pconfig['period'] = $config['rrd']['period']; + +$curcat = "settings"; +$categories = array('system' => gettext("System"), + 'traffic' => gettext("Traffic"), + 'packets' => gettext("Packets"), + 'quality' => gettext("Quality"), + 'queues' => gettext("Queues"), + 'captiveportal' => gettext("Captive Portal")); + +if (isset($config['ntpd']['statsgraph'])) { + $categories['ntpd'] = gettext("NTP"); +} + +$styles = array('inverse' => gettext("Inverse"), + 'absolute' => gettext("Absolute")); +$periods = array("absolute" => gettext("Absolute Timespans"), + "current" => gettext("Current Period"), + "previous" => gettext("Previous Period")); + +if ($_POST['ResetRRD']) { + mwexec('/bin/rm /var/db/rrd/*'); + enable_rrd_graphing(); + setup_gateways_monitor(); + $savemsg = "RRD data has been cleared. New RRD files have been generated."; +} elseif ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + if (!$input_errors) { + $config['rrd']['enable'] = $_POST['enable'] ? true : false; + $config['rrd']['category'] = $_POST['category']; + $config['rrd']['style'] = $_POST['style']; + $config['rrd']['period'] = $_POST['period']; + write_config(); + + $retval = 0; + $retval = enable_rrd_graphing(); + $savemsg = get_std_save_message($retval); + } +} + +$here = getcwd(); +$rrddbpath = "/var/db/rrd/"; +chdir($rrddbpath); +$databases = glob('*.rrd'); +chdir($here); // Need to go back home otherwise the 'include/requires fail! + +foreach ($databases as $database) { + if (stristr($database, "wireless")) { + $wireless = true; + } + if (stristr($database, "queues")) { + $queues = true; + } + if (stristr($database, "-cellular") && !empty($config['ppps'])) { + $cellular = true; + } + if (stristr($database, "-vpnusers")) { + $vpnusers = true; + } + if (stristr($database, "captiveportal-") && is_array($config['captiveportal'])) { + $captiveportal = true; + } +} + +$pgtitle = array(gettext("Status"), gettext("RRD Graphs")); +include("head.inc"); + +$tab_array[] = array(gettext("System"), ($curcat == "system"), "status_rrd_graph.php?cat=system"); +$tab_array[] = array(gettext("Traffic"), ($curcat == "traffic"), "status_rrd_graph.php?cat=traffic"); +$tab_array[] = array(gettext("Packets"), ($curcat == "packets"), "status_rrd_graph.php?cat=packets"); +$tab_array[] = array(gettext("Quality"), ($curcat == "quality"), "status_rrd_graph.php?cat=quality"); + +if($queues) { + $tab_array[] = array(gettext("Queues"), ($curcat == "queues"), "status_rrd_graph.php?cat=queues"); + $tab_array[] = array(gettext("QueueDrops"), ($curcat == "queuedrops"), "status_rrd_graph.php?cat=queuedrops"); +} + +if($wireless) + $tab_array[] = array(gettext("Wireless"), ($curcat == "wireless"), "status_rrd_graph.php?cat=wireless"); + +if($cellular) + $tab_array[] = array(gettext("Cellular"), ($curcat == "cellular"), "status_rrd_graph.php?cat=cellular"); + +if($vpnusers) + $tab_array[] = array(gettext("VPN"), ($curcat == "vpnusers"), "status_rrd_graph.php?cat=vpnusers"); + +if($captiveportal) + $tab_array[] = array(gettext("Captive Portal"), ($curcat == "captiveportal"), "status_rrd_graph.php?cat=captiveportal"); + +if(isset($config['ntpd']['statsgraph'])) + $tab_array[] = array(gettext("NTP"), ($curcat == "ntpd"), "status_rrd_graph.php?cat=ntpd"); + +$tab_array[] = array(gettext("Custom"), ($curcat == "custom"), "status_rrd_graph.php?cat=custom"); +$tab_array[] = array(gettext("Settings"), ($curcat == "settings"), "status_rrd_graph_settings.php"); + +display_top_tabs($tab_array); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg); + +require('classes/Form.class.php'); + +$form = new Form; + +$section = new Form_Section('Graph settings'); + +$section->addInput(new Form_Checkbox( + 'enable', + 'RRD Graphs', + 'Enable the RRD Graphing backend', + $pconfig['enable'] +)); + +$section->addInput(new Form_Select( + 'category', + 'Default category', + $pconfig['category'], + $categories +)); + +$section->addInput(new Form_Select( + 'style', + 'Default style', + $pconfig['style'], + $styles +)); + +$section->addInput(new Form_Select( + 'period', + 'Default period', + $pconfig['period'], + $periods +))->setHelp('Graphs will not be allowed to be recreated within a 1 minute interval, please ' . + 'take this into account after changing the style.'); + +$form->addGlobal(new Form_Button( + 'resetRRD', + 'Reset RRD Data' +))->removeClass('btn-primary')->addClass('btn-danger'); + +$form->add($section); +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_services.php b/src/usr/local/www/status_services.php new file mode 100755 index 0000000..3dcf4cf --- /dev/null +++ b/src/usr/local/www/status_services.php @@ -0,0 +1,151 @@ +<?php +/* + status_services.php + Copyright (C) 2004, 2005 Scott Ullrich + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INClUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/local/sbin/openvpn /usr/bin/killall /bin/ps + pfSense_MODULE: services +*/ + +##|+PRIV +##|*IDENT=page-status-services +##|*NAME=Status: Services page +##|*DESCR=Allow access to the 'Status: Services' page. +##|*MATCH=status_services.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("service-utils.inc"); +require_once("shortcuts.inc"); + +$service_name = ''; +if (isset($_GET['service'])) { + $service_name = htmlspecialchars($_GET['service']); +} + +if (!empty($service_name)) { + switch ($_GET['mode']) { + case "restartservice": + $savemsg = service_control_restart($service_name, $_GET); + break; + case "startservice": + $savemsg = service_control_start($service_name, $_GET); + break; + case "stopservice": + $savemsg = service_control_stop($service_name, $_GET); + break; + } + + sleep(5); +} + +/* batch mode, allow other scripts to call this script */ +if ($_GET['batch']) { + exit; +} + +$pgtitle = array(gettext("Status"), gettext("Services")); +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +$services = get_services(); + +if (count($services) > 0) { +?> +<form action="status_services.php" method="post"> + <div class="panel-body panel-default"> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Service")?></th> + <th><?=gettext("Description")?></th> + <th><?=gettext("Status")?></th> + <th><?=gettext("Actions")?></th> + </tr> + </thead> + <tbody> +<?php + + uasort($services, "service_name_compare"); + + foreach($services as $service) { + if (empty($service['name'])) + continue; + + if (empty($service['description'])) + $service['description'] = get_pkg_descr($service['name']); +?> + <tr> + <td> + <?=$service['name']?> + </td> + + <td> + <?=$service['description']?> + </td> +<?php + // if service is running then listr else listbg + $bgclass = null; + $running = false; + + if (get_service_status($service)) + $running = true; +?> + <td> + <?=$running ? '<font color="green">Running</font>':'<font color="red">Stopped</font>'?> + </td> + <td> + <?=get_service_control_links($service)?> + +<?php + $scut = get_shortcut_by_service_name($service['name']); + + if (!empty($scut)) { + echo get_shortcut_main_link($scut, true, $service); + echo get_shortcut_status_link($scut, true, $service); + echo get_shortcut_log_link($scut, true); + } +?> + </td> + </tr> +<?php + } +?> + </tbody> + </table> + </div> + </div> +</form> +<?php +} else { + print_info_box(gettext("No services found"), 'danger'); +} + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_upnp.php b/src/usr/local/www/status_upnp.php new file mode 100644 index 0000000..abf53dc --- /dev/null +++ b/src/usr/local/www/status_upnp.php @@ -0,0 +1,132 @@ +<?php +/* $Id$ */ +/* + status_upnp.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /sbin/pfctl + pfSense_MODULE: upnp +*/ + +##|+PRIV +##|*IDENT=page-status-upnpstatus +##|*NAME=Status: UPnP Status page +##|*DESCR=Allow access to the 'Status: UPnP Status' page. +##|*MATCH=status_upnp.php* +##|-PRIV + +require("guiconfig.inc"); + +if ($_POST) { + if ($_POST['clear']) { + upnp_action('restart'); + $savemsg = gettext("Rules have been cleared and the daemon restarted"); + } +} + +$rdr_entries = array(); +exec("/sbin/pfctl -aminiupnpd -sn", $rdr_entries, $pf_ret); + +$pgtitle = array(gettext("Status"),gettext("UPnP & NAT-PMP Status")); +$shortcut_section = "upnp"; + +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if(!$config['installedpackages'] || !$config['installedpackages']['miniupnpd']['config'][0]['iface_array'] || + !$config['installedpackages']['miniupnpd']['config'][0]['enable']) { + + print_info_box('UPnP is currently disabled.', 'danger'); + include("foot.inc"); + exit; +} + +?> + +<div class="panel-body panel-default"> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Port")?></th> + <th><?=gettext("Protocol")?></th> + <th><?=gettext("Internal IP")?></th> + <th><?=gettext("Int. Port")?></th> + <th><?=gettext("Description")?></th> + </tr> + </thead> + <tbody> +<?php +$i = 0; + +foreach ($rdr_entries as $rdr_entry) { + if (preg_match("/on (.*) inet proto (.*) from any to any port = (.*) keep state label \"(.*)\" rtable [0-9] -> (.*) port (.*)/", $rdr_entry, $matches)) { + $rdr_proto = $matches[2]; + $rdr_port = $matches[3]; + $rdr_label =$matches[4]; + $rdr_ip = $matches[5]; + $rdr_iport = $matches[6]; + +?> + <tr> + <td> + <?=$rdr_port?> + </td> + <td> + <?=$rdr_proto?> + </td> + <td> + <?=$rdr_ip?> + </td> + <td> + <?=$rdr_iport?> + </td> + <td> + <?=$rdr_label?> + </td> + </tr> +<?php + } + $i++; +} +?> + </tbody> + </table> + </div> + <form action="status_upnp.php" method="post"> + <nav class="action-buttons"> + <input class="btn btn-danger" type="submit" name="clear" id="clear" value="<?=gettext("Clear all sessions")?>" /> + </nav> + </form> +</div> + +<?php +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/status_wireless.php b/src/usr/local/www/status_wireless.php new file mode 100644 index 0000000..798cc5d --- /dev/null +++ b/src/usr/local/www/status_wireless.php @@ -0,0 +1,223 @@ +<?php +/* + status_wireless.php + Copyright (C) 2004 Scott Ullrich + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: interfaces +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-wirelessstatus +##|*NAME=Status: Wireless page +##|*DESCR=Allow access to the 'Status: Wireless' page. +##|*MATCH=status_wireless.php* +##|-PRIV + +require_once("guiconfig.inc"); + +$pgtitle = array(gettext("Status"), gettext("Wireless")); +$shortcut_section = "wireless"; + +include("head.inc"); + +$if = $_POST['if']; + +if($_GET['if'] != "") + $if = $_GET['if']; + +$ciflist = get_configured_interface_with_descr(); +if (empty($if)) { + /* Find the first interface + that is wireless */ + foreach ($ciflist as $interface => $ifdescr) { + if (is_interface_wireless(get_real_interface($interface))) { + $if = $interface; + break; + } + } +} + +$tab_array = array(); + +foreach($ciflist as $interface => $ifdescr) { + if (is_interface_wireless(get_real_interface($interface))) { + $enabled = false; + if($if == $interface) + $enabled = true; + + $tab_array[] = array(gettext("Status") . " ({$ifdescr})", $enabled, "status_wireless.php?if={$interface}"); + } +} + +$rwlif = get_real_interface($if); + +if($_POST['rescanwifi'] != "") { + mwexec_bg("/sbin/ifconfig {$rwlif} scan 2>&1"); + $savemsg = gettext("Rescan has been initiated in the background. Refresh this page in 10 seconds to see the results."); +} + +if ($savemsg) + print_info_box($savemsg, 'success'); + +display_top_tabs($tab_array); +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Nearby access points or ad-hoc peers")?></h2></div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th>SSID</th> + <th>BSSID</th> + <th>CHAN</th> + <th>RATE</th> + <th>RSSI</th> + <th>INT</th> + <th>CAPS</th> + </tr> + </thead> + <tbody> +<?php + exec("/sbin/ifconfig {$rwlif} list scan 2>&1", $states, $ret); + /* Skip Header */ + array_shift($states); + + $counter = 0; + foreach ($states as $state) { + /* Split by Mac address for the SSID Field */ + $split = preg_split("/([0-9a-f][[0-9a-f]\:[0-9a-f][[0-9a-f]\:[0-9a-f][[0-9a-f]\:[0-9a-f][[0-9a-f]\:[0-9a-f][[0-9a-f]\:[0-9a-f][[0-9a-f])/i", $state); + preg_match("/([0-9a-f][[0-9a-f]\:[0-9a-f][[0-9a-f]\:[0-9a-f][[0-9a-f]\:[0-9a-f][[0-9a-f]\:[0-9a-f][[0-9a-f]\:[0-9a-f][[0-9a-f])/i", $state, $bssid); + $ssid = htmlspecialchars($split[0]); + $bssid = $bssid[0]; + /* Split the rest by using spaces for this line using the 2nd part */ + $split = preg_split("/[ ]+/i", $split[1]); + $channel = $split[1]; + $rate = $split[2]; + $rssi = $split[3]; + $int = $split[4]; + $caps = "$split[5] $split[6] $split[7] $split[8] $split[9] $split[10] $split[11] "; +?> + <tr> + <td> + <?=$ssid?> + </td> + <td> + <?=$bssid?> + </td> + <td> + <?=$channel?> + </td> + <td> + <?=$rate?> + </td> + <td> + <?=$rssi?> + </td> + <td> + <?=$int?> + </td> + <td> + <?=$caps?> + </td> + </tr> +<?php + } // e-o-foreach +?> + </tbody> + </table> + </div> + </div> +</div> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Associated or ad-hoc peers")?></h2></div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th>ADDR</font></th> + <th>AID</font></th> + <th>CHAN</font></th> + <th>RATE</font></th> + <th>RSSI</font></th> + <th>IDLE</font></th> + <th>TXSEQ</font></th> + <th>RXSEQ</font></th> + <th>CAPS</font></th> + <th>ERP</font></th> + </tr> + </thead> + <tbody> + +<?php + $states = array(); + exec("/sbin/ifconfig {$rwlif} list sta 2>&1", $states, $ret); + array_shift($states); + + $counter=0; + + foreach($states as $state) { + $split = preg_split("/[ ]+/i", $state); +?> + <tr> +<?php + /* Split the rest by using spaces for this line using the 2nd part */ + for($idx=0; $idx<10; $idx++) { +?> + <td> + <?=$split[$idx]?> + </td> +<?php + } +?> + </tr> +<?php + } +?> + </tbody> + </table> + </div> + </div> +</div> + + +<form action="status_wireless.php" method="post"> + <nav class="action-buttons"> + <input type="hidden" name="if" id="if" value="<?=htmlspecialchars($if)?>"> + <input type="submit" class="btn btn-success" name="rescanwifi" id="rescanwifi" value="Rescan"> + </nav> +</form> + +<?php +print_info_box('<b>Flags:</b> A = authorized, E = Extended Rate (802.11g), P = Power saving mode<br />' . + '<b>Capabilities:</b> E = ESS (infrastructure mode), I = IBSS (ad-hoc mode), P = privacy (WEP/TKIP/AES), ' . + 'S = Short preamble, s = Short slot time'); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system.php b/src/usr/local/www/system.php new file mode 100644 index 0000000..cd028a4 --- /dev/null +++ b/src/usr/local/www/system.php @@ -0,0 +1,424 @@ +<?php +/* $Id$ */ +/* + system.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /bin/kill /usr/bin/tar + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-system-generalsetup +##|*NAME=System: General Setup page +##|*DESCR=Allow access to the 'System: General Setup' page. +##|*MATCH=system.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +$pconfig['hostname'] = $config['system']['hostname']; +$pconfig['domain'] = $config['system']['domain']; +list($pconfig['dns1'], $pconfig['dns2'], $pconfig['dns3'], $pconfig['dns4']) = $config['system']['dnsserver']; + +$arr_gateways = return_gateways_array(); + +$pconfig['dns1gw'] = $config['system']['dns1gw']; +$pconfig['dns2gw'] = $config['system']['dns2gw']; +$pconfig['dns3gw'] = $config['system']['dns3gw']; +$pconfig['dns4gw'] = $config['system']['dns4gw']; + +$pconfig['dnsallowoverride'] = isset($config['system']['dnsallowoverride']); +$pconfig['timezone'] = $config['system']['timezone']; +$pconfig['timeupdateinterval'] = $config['system']['time-update-interval']; +$pconfig['timeservers'] = $config['system']['timeservers']; +$pconfig['theme'] = $config['system']['theme']; +$pconfig['language'] = $config['system']['language']; + +$pconfig['dnslocalhost'] = isset($config['system']['dnslocalhost']); + +if (!isset($pconfig['timeupdateinterval'])) { + $pconfig['timeupdateinterval'] = 300; +} +if (!$pconfig['timezone']) { + $pconfig['timezone'] = "Etc/UTC"; +} +if (!$pconfig['timeservers']) { + $pconfig['timeservers'] = "pool.ntp.org"; +} + +$changedesc = gettext("System") . ": "; +$changecount = 0; + +function is_timezone($elt) { + return !preg_match("/\/$/", $elt); +} + +if ($pconfig['timezone'] <> $_POST['timezone']) { + filter_pflog_start(true); +} + +exec('/usr/bin/tar -tzf /usr/share/zoneinfo.tgz', $timezonelist); +$timezonelist = array_filter($timezonelist, 'is_timezone'); +sort($timezonelist); + +$multiwan = false; +$interfaces = get_configured_interface_list(); +foreach ($interfaces as $interface) { + if (interface_has_gateway($interface)) { + $multiwan = true; + } +} + +if ($_POST) { + + $changecount++; + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "hostname domain"); + $reqdfieldsn = array(gettext("Hostname"), gettext("Domain")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ($_POST['hostname']) { + if (!is_hostname($_POST['hostname'])) { + $input_errors[] = gettext("The hostname can only contain the characters A-Z, 0-9 and '-'. It may not start or end with '-'."); + } else { + if (!is_unqualified_hostname($_POST['hostname'])) { + $input_errors[] = gettext("A valid hostname is specified, but the domain name part should be omitted"); + } + } + } + if ($_POST['domain'] && !is_domain($_POST['domain'])) { + $input_errors[] = gettext("The domain may only contain the characters a-z, 0-9, '-' and '.'."); + } + + $ignore_posted_dnsgw = array(); + + for ($dnscounter=1; $dnscounter<5; $dnscounter++) { + $dnsname="dns{$dnscounter}"; + $dnsgwname="dns{$dnscounter}gw"; + if (($_POST[$dnsname] && !is_ipaddr($_POST[$dnsname]))) { + $input_errors[] = gettext("A valid IP address must be specified for DNS server $dnscounter."); + } else { + if (($_POST[$dnsgwname] <> "") && ($_POST[$dnsgwname] <> "none")) { + // A real gateway has been selected. + if (is_ipaddr($_POST[$dnsname])) { + if ((is_ipaddrv4($_POST[$dnsname])) && (validate_address_family($_POST[$dnsname], $_POST[$dnsgwname]) === false)) { + $input_errors[] = gettext("You can not specify IPv6 gateway '{$_POST[$dnsgwname]}' for IPv4 DNS server '{$_POST[$dnsname]}'"); + } + if ((is_ipaddrv6($_POST[$dnsname])) && (validate_address_family($_POST[$dnsname], $_POST[$dnsgwname]) === false)) { + $input_errors[] = gettext("You can not specify IPv4 gateway '{$_POST[$dnsgwname]}' for IPv6 DNS server '{$_POST[$dnsname]}'"); + } + } else { + // The user selected a gateway but did not provide a DNS address. Be nice and set the gateway back to "none". + $ignore_posted_dnsgw[$dnsgwname] = true; + } + } + } + } + + $direct_networks_list = explode(" ", filter_get_direct_networks_list()); + for ($dnscounter=1; $dnscounter<5; $dnscounter++) { + $dnsitem = "dns{$dnscounter}"; + $dnsgwitem = "dns{$dnscounter}gw"; + if ($_POST[$dnsgwitem]) { + if (interface_has_gateway($_POST[$dnsgwitem])) { + foreach ($direct_networks_list as $direct_network) { + if (ip_in_subnet($_POST[$dnsitem], $direct_network)) { + $input_errors[] = sprintf(gettext("You can not assign a gateway to DNS '%s' server which is on a directly connected network."), $_POST[$dnsitem]); + } + } + } + } + } + + $t = (int)$_POST['timeupdateinterval']; + if (($t < 0) || (($t > 0) && ($t < 6)) || ($t > 1440)) { + $input_errors[] = gettext("The time update interval must be either 0 (disabled) or between 6 and 1440."); + } + # it's easy to have a little too much whitespace in the field, clean it up for the user before processing. + $_POST['timeservers'] = preg_replace('/[[:blank:]]+/', ' ', $_POST['timeservers']); + $_POST['timeservers'] = trim($_POST['timeservers']); + foreach (explode(' ', $_POST['timeservers']) as $ts) { + if (!is_domain($ts)) { + $input_errors[] = gettext("A NTP Time Server name may only contain the characters a-z, 0-9, '-' and '.'."); + } + } + + if (!$input_errors) { + update_if_changed("hostname", $config['system']['hostname'], $_POST['hostname']); + update_if_changed("domain", $config['system']['domain'], $_POST['domain']); + update_if_changed("timezone", $config['system']['timezone'], $_POST['timezone']); + update_if_changed("NTP servers", $config['system']['timeservers'], strtolower($_POST['timeservers'])); + update_if_changed("NTP update interval", $config['system']['time-update-interval'], $_POST['timeupdateinterval']); + + if ($_POST['language'] && $_POST['language'] != $config['system']['language']) { + $config['system']['language'] = $_POST['language']; + set_language($config['system']['language']); + } + + /* pfSense themes */ + if (!$g['disablethemeselection']) { + update_if_changed("System Theme", $config['theme'], $_POST['theme']); + } + + /* XXX - billm: these still need updating after figuring out how to check if they actually changed */ + $olddnsservers = $config['system']['dnsserver']; + unset($config['system']['dnsserver']); + if ($_POST['dns1']) { + $config['system']['dnsserver'][] = $_POST['dns1']; + } + if ($_POST['dns2']) { + $config['system']['dnsserver'][] = $_POST['dns2']; + } + if ($_POST['dns3']) { + $config['system']['dnsserver'][] = $_POST['dns3']; + } + if ($_POST['dns4']) { + $config['system']['dnsserver'][] = $_POST['dns4']; + } + + $olddnsallowoverride = $config['system']['dnsallowoverride']; + + unset($config['system']['dnsallowoverride']); + $config['system']['dnsallowoverride'] = $_POST['dnsallowoverride'] ? true : false; + + if ($_POST['dnslocalhost'] == "yes") { + $config['system']['dnslocalhost'] = true; + } else { + unset($config['system']['dnslocalhost']); + } + + /* which interface should the dns servers resolve through? */ + $outdnscounter = 0; + for ($dnscounter=1; $dnscounter<5; $dnscounter++) { + $dnsname="dns{$dnscounter}"; + $dnsgwname="dns{$dnscounter}gw"; + $olddnsgwname = $config['system'][$dnsgwname]; + + if ($ignore_posted_dnsgw[$dnsgwname]) { + $thisdnsgwname = "none"; + } else { + $thisdnsgwname = $pconfig[$dnsgwname]; + } + + // "Blank" out the settings for this index, then we set them below using the "outdnscounter" index. + $config['system'][$dnsgwname] = "none"; + $pconfig[$dnsgwname] = "none"; + $pconfig[$dnsname] = ""; + + if ($_POST[$dnsname]) { + // Only the non-blank DNS servers were put into the config above. + // So we similarly only add the corresponding gateways sequentially to the config (and to pconfig), as we find non-blank DNS servers. + // This keeps the DNS server IP and corresponding gateway "lined up" when the user blanks out a DNS server IP in the middle of the list. + $outdnscounter++; + $outdnsname="dns{$outdnscounter}"; + $outdnsgwname="dns{$outdnscounter}gw"; + $pconfig[$outdnsname] = $_POST[$dnsname]; + if ($_POST[$dnsgwname]) { + $config['system'][$outdnsgwname] = $thisdnsgwname; + $pconfig[$outdnsgwname] = $thisdnsgwname; + } else { + // Note: when no DNS GW name is chosen, the entry is set to "none", so actually this case never happens. + unset($config['system'][$outdnsgwname]); + $pconfig[$outdnsgwname] = ""; + } + } + if (($olddnsgwname != "") && ($olddnsgwname != "none") && (($olddnsgwname != $thisdnsgwname) || ($olddnsservers[$dnscounter-1] != $_POST[$dnsname]))) { + // A previous DNS GW name was specified. It has now gone or changed, or the DNS server address has changed. + // Remove the route. Later calls will add the correct new route if needed. + if (is_ipaddrv4($olddnsservers[$dnscounter-1])) { + mwexec("/sbin/route delete " . escapeshellarg($olddnsservers[$dnscounter-1])); + } else { + if (is_ipaddrv6($olddnsservers[$dnscounter-1])) { + mwexec("/sbin/route delete -inet6 " . escapeshellarg($olddnsservers[$dnscounter-1])); + } + } + } + } + + if ($changecount > 0) { + write_config($changedesc); + } + + $retval = 0; + $retval = system_hostname_configure(); + $retval |= system_hosts_generate(); + $retval |= system_resolvconf_generate(); + if (isset($config['dnsmasq']['enable'])) { + $retval |= services_dnsmasq_configure(); + } elseif (isset($config['unbound']['enable'])) { + $retval |= services_unbound_configure(); + } + $retval |= system_timezone_configure(); + $retval |= system_ntp_configure(); + + if ($olddnsallowoverride != $config['system']['dnsallowoverride']) { + $retval |= send_event("service reload dns"); + } + + // Reload the filter - plugins might need to be run. + $retval |= filter_configure(); + + $savemsg = get_std_save_message($retval); + } + + unset($ignore_posted_dnsgw); +} + +$pgtitle = array(gettext("System"), gettext("General Setup")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); +?> +<div id="container"> +<?php + +require('classes/Form.class.php'); +$form = new Form; +$section = new Form_Section('System'); +$section->addInput(new Form_Input( + 'hostname', + 'Hostname', + 'text', + $pconfig['hostname'], + ['placeholder' => 'pfSense'] +))->setHelp('Name of the firewall host, without domain part'); +$section->addInput(new Form_Input( + 'domain', + 'Domain', + 'text', + $pconfig['domain'], + ['placeholder' => 'mycorp.com, home, office, private, etc.'] +))->setHelp('Do not use \'local\' as a domain name. It will cause local '. + 'hosts running mDNS (avahi, bonjour, etc.) to be unable to resolve '. + 'local hosts not running mDNS.'); +$form->add($section); + +$section = new Form_Section('DNS server settings'); + +for ($i=1; $i<5; $i++) +{ + if (!isset($pconfig['dns'.$i])) + continue; + + $group = new Form_Group('DNS Server'); + $group->add(new Form_Input('dns['.$i.']', 'DNS Server', 'text', $pconfig['dns'.$i])); + $help = "Enter IP addresses to be used by the system for DNS resolution. " . + "These are also used for the DHCP service, DNS forwarder and for PPTP VPN clients."; + + if ($multiwan) + { + $options = array('none' => 'none'); + + foreach($arr_gateways as $gwname => $gwitem) + { + if((is_ipaddrv4(lookup_gateway_ip_by_name($pconfig[$dnsgw])) && (is_ipaddrv6($gwitem['gateway'])))) { + continue; + } + if((is_ipaddrv6(lookup_gateway_ip_by_name($pconfig[$dnsgw])) && (is_ipaddrv4($gwitem['gateway'])))) { + continue; + } + + $options[$gwname] = $gwname.' - '.$gwitem['friendlyiface'].' - '.$gwitem['gateway']; + } + + $group->add(new Form_Select( + 'gateway', + 'Gateway', + $pconfig['dns'.$i.'gw'], + $options + )); + + $help .= '<br/>'. "In addition, optionally select the gateway for each DNS server. " . + "When using multiple WAN connections there should be at least one unique DNS server per gateway."; + } + + $group->setHelp($help); + $section->add($group); +} + +$section->addInput(new Form_Checkbox( + 'dnsallowoverride', + 'DNS server override', + 'Allow DNS server list to be overridden by DHCP/PPP on WAN', + $pconfig['dnsallowoverride'] +))->setHelp(sprintf(gettext('If this option is set, %s will use DNS servers'. + 'assigned by a DHCP/PPP server on WAN for its own purposes (including '. + 'the DNS forwarder). However, they will not be assigned to DHCP and PPTP '. + 'VPN clients.'), $g['product_name'])); + +$section->addInput(new Form_Checkbox( + 'dnslocalhost', + 'Disable DNS forwarder', + 'Do not use the DNS Forwarder as a DNS server for the firewall', + $pconfig['dnslocalhost'] +))->setHelp('By default localhost (127.0.0.1) will be used as the first DNS'. + 'server where the DNS Forwarder or DNS Resolver is enabled and set to '. + 'listen on Localhost, so system can use the local DNS service to perform'. + 'lookups. Checking this box omits localhost from the list of DNS servers.'); + +$form->add($section); + +$section = new Form_Section('Localization'); +$section->addInput(new Form_Select( + 'timezone', + 'Timezone', + $pconfig['timezone'], + array_combine($timezonelist, $timezonelist) +))->setHelp('Select the location closest to you'); +$section->addInput(new Form_Input( + 'timeservers', + 'Timeservers', + 'text', + $pconfig['timeservers'] +))->setHelp('Use a space to separate multiple hosts (only one required). '. + 'Remember to set up at least one DNS server if you enter a host name here!'); +$section->addInput(new Form_Select( + 'language', + 'Language', + $pconfig['language'], + get_locale_list() +))->setHelp('Choose a language for the webConfigurator'); + +$form->add($section); + +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_advanced_admin.php b/src/usr/local/www/system_advanced_admin.php new file mode 100644 index 0000000..5691034 --- /dev/null +++ b/src/usr/local/www/system_advanced_admin.php @@ -0,0 +1,575 @@ +<?php +/* $Id$ */ +/* + system_advanced_admin.php + part of pfSense + Copyright (C) 2005-2010 Scott Ullrich + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/bin/killall + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-system-advanced-admin +##|*NAME=System: Advanced: Admin Access Page +##|*DESCR=Allow access to the 'System: Advanced: Admin Access' page. +##|*MATCH=system_advanced_admin.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +$pconfig['webguiproto'] = $config['system']['webgui']['protocol']; +$pconfig['webguiport'] = $config['system']['webgui']['port']; +$pconfig['max_procs'] = ($config['system']['webgui']['max_procs']) ? $config['system']['webgui']['max_procs'] : 2; +$pconfig['ssl-certref'] = $config['system']['webgui']['ssl-certref']; +$pconfig['disablehttpredirect'] = isset($config['system']['webgui']['disablehttpredirect']); +$pconfig['disableconsolemenu'] = isset($config['system']['disableconsolemenu']); +$pconfig['noantilockout'] = isset($config['system']['webgui']['noantilockout']); +$pconfig['nodnsrebindcheck'] = isset($config['system']['webgui']['nodnsrebindcheck']); +$pconfig['nohttpreferercheck'] = isset($config['system']['webgui']['nohttpreferercheck']); +$pconfig['pagenamefirst'] = isset($config['system']['webgui']['pagenamefirst']); +$pconfig['loginautocomplete'] = isset($config['system']['webgui']['loginautocomplete']); +$pconfig['althostnames'] = $config['system']['webgui']['althostnames']; +$pconfig['enableserial'] = $config['system']['enableserial']; +$pconfig['serialspeed'] = $config['system']['serialspeed']; +$pconfig['primaryconsole'] = $config['system']['primaryconsole']; +$pconfig['enablesshd'] = $config['system']['enablesshd']; +$pconfig['sshport'] = $config['system']['ssh']['port']; +$pconfig['sshdkeyonly'] = isset($config['system']['ssh']['sshdkeyonly']); +$pconfig['quietlogin'] = isset($config['system']['webgui']['quietlogin']); + +$a_cert =& $config['cert']; +$certs_available = false; + +if (is_array($a_cert) && count($a_cert)) { + $certs_available = true; +} + +if (!$pconfig['webguiproto'] || !$certs_available) { + $pconfig['webguiproto'] = "http"; +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if ($_POST['webguiport']) { + if (!is_port($_POST['webguiport'])) { + $input_errors[] = gettext("You must specify a valid webConfigurator port number"); + } + } + + if ($_POST['max_procs']) { + if (!is_numericint($_POST['max_procs']) || ($_POST['max_procs'] < 1) || ($_POST['max_procs'] > 500)) { + $input_errors[] = gettext("Max Processes must be a number 1 or greater"); + } + } + + if ($_POST['althostnames']) { + $althosts = explode(" ", $_POST['althostnames']); + foreach ($althosts as $ah) { + if (!is_hostname($ah)) { + $input_errors[] = sprintf(gettext("Alternate hostname %s is not a valid hostname."), htmlspecialchars($ah)); + } + } + } + + if ($_POST['sshport']) { + if (!is_port($_POST['sshport'])) { + $input_errors[] = gettext("You must specify a valid port number"); + } + } + + if ($_POST['sshdkeyonly'] == "yes") { + $config['system']['ssh']['sshdkeyonly'] = "enabled"; + } else if (isset($config['system']['ssh']['sshdkeyonly'])) { + unset($config['system']['ssh']['sshdkeyonly']); + } + + ob_flush(); + flush(); + + if (!$input_errors) { + if (update_if_changed("webgui protocol", $config['system']['webgui']['protocol'], $_POST['webguiproto'])) { + $restart_webgui = true; + } + if (update_if_changed("webgui port", $config['system']['webgui']['port'], $_POST['webguiport'])) { + $restart_webgui = true; + } + if (update_if_changed("webgui certificate", $config['system']['webgui']['ssl-certref'], $_POST['ssl-certref'])) { + $restart_webgui = true; + } + if (update_if_changed("webgui max processes", $config['system']['webgui']['max_procs'], $_POST['max_procs'])) { + $restart_webgui = true; + } + + if ($_POST['webgui-redirect'] == "yes") { + $config['system']['webgui']['disablehttpredirect'] = true; + $restart_webgui = true; + } else { + unset($config['system']['webgui']['disablehttpredirect']); + $restart_webgui = true; + } + if ($_POST['webgui-login-messages'] == "yes") { + $config['system']['webgui']['quietlogin'] = true; + } else { + unset($config['system']['webgui']['quietlogin']); + } + + if ($_POST['disableconsolemenu'] == "yes") { + $config['system']['disableconsolemenu'] = true; + } else { + unset($config['system']['disableconsolemenu']); + } + + if ($_POST['noantilockout'] == "yes") { + $config['system']['webgui']['noantilockout'] = true; + } else { + unset($config['system']['webgui']['noantilockout']); + } + + if ($_POST['enableserial'] == "yes" || $g['enableserial_force']) { + $config['system']['enableserial'] = true; + } else { + unset($config['system']['enableserial']); + } + + if (is_numericint($_POST['serialspeed'])) { + $config['system']['serialspeed'] = $_POST['serialspeed']; + } else { + unset($config['system']['serialspeed']); + } + + if ($_POST['primaryconsole']) { + $config['system']['primaryconsole'] = $_POST['primaryconsole']; + } else { + unset($config['system']['primaryconsole']); + } + + if ($_POST['nodnsrebindcheck'] == "yes") { + $config['system']['webgui']['nodnsrebindcheck'] = true; + } else { + unset($config['system']['webgui']['nodnsrebindcheck']); + } + + if ($_POST['nohttpreferercheck'] == "yes") { + $config['system']['webgui']['nohttpreferercheck'] = true; + } else { + unset($config['system']['webgui']['nohttpreferercheck']); + } + + if ($_POST['pagenamefirst'] == "yes") { + $config['system']['webgui']['pagenamefirst'] = true; + } else { + unset($config['system']['webgui']['pagenamefirst']); + } + + if ($_POST['loginautocomplete'] == "yes") { + $config['system']['webgui']['loginautocomplete'] = true; + } else { + unset($config['system']['webgui']['loginautocomplete']); + } + + if ($_POST['althostnames']) { + $config['system']['webgui']['althostnames'] = $_POST['althostnames']; + } else { + unset($config['system']['webgui']['althostnames']); + } + + $sshd_enabled = $config['system']['enablesshd']; + if ($_POST['enablesshd']) { + $config['system']['enablesshd'] = "enabled"; + } else { + unset($config['system']['enablesshd']); + } + + $sshd_keyonly = isset($config['system']['sshdkeyonly']); + if ($_POST['sshdkeyonly']) { + $config['system']['sshdkeyonly'] = true; + } else { + unset($config['system']['sshdkeyonly']); + } + + $sshd_port = $config['system']['ssh']['port']; + if ($_POST['sshport']) { + $config['system']['ssh']['port'] = $_POST['sshport']; + } else if (isset($config['system']['ssh']['port'])) { + unset($config['system']['ssh']['port']); + } + + if (($sshd_enabled != $config['system']['enablesshd']) || + ($sshd_keyonly != $config['system']['sshdkeyonly']) || + ($sshd_port != $config['system']['ssh']['port'])) { + $restart_sshd = true; + } + + if ($restart_webgui) { + global $_SERVER; + $http_host_port = explode("]", $_SERVER['HTTP_HOST']); + /* IPv6 address check */ + if (strstr($_SERVER['HTTP_HOST'], "]")) { + if (count($http_host_port) > 1) { + array_pop($http_host_port); + $host = str_replace(array("[", "]"), "", implode(":", $http_host_port)); + $host = "[{$host}]"; + } else { + $host = str_replace(array("[", "]"), "", implode(":", $http_host_port)); + $host = "[{$host}]"; + } + } else { + list($host) = explode(":", $_SERVER['HTTP_HOST']); + } + $prot = $config['system']['webgui']['protocol']; + $port = $config['system']['webgui']['port']; + if ($port) { + $url = "{$prot}://{$host}:{$port}/system_advanced_admin.php"; + } else { + $url = "{$prot}://{$host}/system_advanced_admin.php"; + } + } + + write_config(); + + $retval = filter_configure(); + $savemsg = get_std_save_message($retval); + + if ($restart_webgui) { + $savemsg .= sprintf("<br />" . gettext("One moment...redirecting to %s in 20 seconds."), $url); + } + + conf_mount_rw(); + setup_serial_port(); + // Restart DNS in case dns rebinding toggled + if (isset($config['dnsmasq']['enable'])) { + services_dnsmasq_configure(); + } elseif (isset($config['unbound']['enable'])) { + services_unbound_configure(); + } + conf_mount_ro(); + } +} + +$pgtitle = array(gettext("System"), gettext("Advanced: Admin Access")); +include("head.inc"); + + +print('WebGUIProto = ' . $pconfig['webguiproto']); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +$tab_array = array(); +$tab_array[] = array(gettext("Admin Access"), true, "system_advanced_admin.php"); +$tab_array[] = array(gettext("Firewall / NAT"), false, "system_advanced_firewall.php"); +$tab_array[] = array(gettext("Networking"), false, "system_advanced_network.php"); +$tab_array[] = array(gettext("Miscellaneous"), false, "system_advanced_misc.php"); +$tab_array[] = array(gettext("System Tunables"), false, "system_advanced_sysctl.php"); +$tab_array[] = array(gettext("Notifications"), false, "system_advanced_notifications.php"); +display_top_tabs($tab_array); + +?><div id="container"><?php + +require('classes/Form.class.php'); +$form = new Form; +$section = new Form_Section('WebConfigurator'); +$group = new Form_Group('Protocol'); + +$group->add(new Form_Checkbox( + 'webguiproto', + 'Protocol', + 'HTTP', + ($pconfig['webguiproto']=='http'), + 'http' +))->displayAsRadio(); + +$group->add(new Form_Checkbox( + 'webguiproto', + 'Protocol', + 'HTTPS', + ($pconfig['webguiproto']=='https'), + 'https' +))->displayAsRadio(); + +$group->setHelp($certs_available ? '':'No Certificates have been defined. You must '. + '<a href="system_certmanager.php">'. gettext("Create or Import").'</a> '. + 'a Certificate before SSL can be enabled.'); + +$section->add($group); + +$values = array(); +foreach($a_cert as $cert) + $values[ $cert['refid'] ] = $cert['descr']; + +$section->addInput($input = new Form_Select( + 'ssl-certref', + 'SSL Certificate', + $pconfig['ssl-certref'], + $values +)); + +$section->addInput(new Form_Input( + 'tcp-port', + 'TCP port', + 'number', + $config['system']['webgui']['port'], + ['min' => 1, 'max' => 65535] +))->setHelp('Enter a custom port number for the webConfigurator '. + 'above if you want to override the default (80 for HTTP, 443 '. + 'for HTTPS). Changes will take effect immediately after save.'); + +$section->addInput(new Form_Input( + 'max-processes', + 'Max Processes', + 'number', + $pconfig['max_procs'] +))->setHelp('Enter the number of webConfigurator processes you '. + 'want to run. This defaults to 2. Increasing this will allow more '. + 'users/browsers to access the GUI concurrently.'); + +$section->addInput(new Form_Checkbox( + 'webgui-redirect', + 'WebGUI redirect', + 'Disable webConfigurator redirect rule', + $pconfig['disablehttpredirect'] +))->setHelp('When this is unchecked, access to the webConfigurator '. + 'is always permitted even on port 80, regardless of the listening port configured. '. + 'Check this box to disable this automatically added redirect rule.'); + +$section->addInput(new Form_Checkbox( + 'webgui-login-autocomplete', + 'WebGUI Login Autocomplete', + 'Enable webConfigurator login autocomplete', + $pconfig['loginautocomplete'] +))->setHelp('When this is checked, login credentials for the webConfigurator may '. + 'be saved by the browser. While convenient, some security standards require this '. + 'to be disabled. Check this box to enable autocomplete on the login form so that '. + 'browsers will prompt to save credentials (NOTE: Some browsers do not respect '. + 'this option).'); + +$section->addInput(new Form_Checkbox( + 'webgui-login-messages', + 'WebGUI login messages', + 'Disable logging of webConfigurator successful logins', + $pconfig['quietlogin'] +))->setHelp('When this is checked, successful logins to the webConfigurator will '. + 'not be logged.'); + +if ($config['interfaces']['lan']) + $lockout_interface = "LAN"; +else + $lockout_interface = "WAN"; + +$section->addInput(new Form_Checkbox( + 'anti-lockout', + 'Anti-lockout', + 'Disable webConfigurator anti-lockout rule', + $pconfig['noantilockout'] +))->setHelp('When this is '. + 'unchecked, access to the webConfigurator on the %s interface is always '. + 'permitted, regardless of the user-defined firewall rule set. Check this box to '. + 'disable this automatically added rule, so access to the webConfigurator is '. + 'controlled by the user-defined firewall rules (ensure you have a firewall rule '. + 'in place that allows you in, or you will lock yourself out!)<em>Hint: the "Set interface(s) IP address" '. + 'option in the console menu resets this setting as well.</em>', [$lockout_interface]); + +$section->addInput(new Form_Checkbox( + 'dns-rebind-check', + 'DNS Rebind Check', + 'Disable DNS Rebinding Checks', + $pconfig['nodnsrebindcheck'] +))->setHelp('When this is unchecked, your system is protected against<a '. + 'href=\"http://en.wikipedia.org/wiki/DNS_rebinding\">DNS Rebinding attacks</a>. '. + 'This blocks private IP responses from your configured DNS servers. Check this '. + 'box to disable this protection if it interferes with webConfigurator access or '. + 'name resolution in your environment.'); + +$section->addInput(new Form_Input( + 'alternate-hostnames', + 'Alternate Hostnames', + 'text', + htmlspecialchars($pconfig['althostnames']) +))->setHelp('Alternate Hostnames for DNS Rebinding and HTTP_REFERER Checks. Here '. + 'you can specify alternate hostnames by which the router may be queried, to '. + 'bypass the DNS Rebinding Attack checks. Separate hostnames with spaces.'); + +$section->addInput(new Form_Checkbox( + 'browser-http_referer-enforcement', + 'Browser HTTP_REFERER enforcement', + 'Disable HTTP_REFERER enforcement check', + $pconfig['nohttpreferercheck'] +))->setHelp('When this is unchecked, access to the webConfigurator is protected '. + 'against HTTP_REFERER redirection attempts. Check this box to disable this '. + 'protection if you find that it interferes with webConfigurator access in certain '. + 'corner cases such as using external scripts to interact with this system. More '. + 'information on HTTP_REFERER is available from<a target="_blank" '. + 'href="http://en.wikipedia.org/wiki/HTTP_referrer">Wikipedia</a>.'); + +$section->addInput(new Form_Checkbox( + 'browser-tab-text', + 'Browser tab text', + 'Display page name first in browser tab', + $pconfig['pagenamefirst'] +))->setHelp('When this is unchecked, the browser tab shows the host name followed '. + 'by the current page. Check this box to display the current page followed by the '. + 'host name.'); + +$form->add($section); +$section = new Form_Section('Secure Shell'); + +$section->addInput(new Form_Checkbox( + 'secure-shell-server', + 'Secure Shell Server', + 'Enable Secure Shell', + isset($pconfig['enablesshd']) +)); + +$section->addInput(new Form_Checkbox( + 'authentication-method', + 'Authentication Method', + 'Disable password login for Secure Shell (RSA/DSA key only)', + $pconfig['sshdkeyonly'] +))->setHelp('When enabled, authorized keys need to be configured for each<a '. + 'href="system_usermanager.php">user</a>that has been granted secure shell '. + 'access.'); + +$section->addInput(new Form_Input( + 'ssh-port', + 'SSH port', + 'number', + $pconfig['sshport'], + ['min' => 1, 'max' => 65535, 'placeholder' => 22] +))->setHelp('Note: Leave this blank for the default of 22.'); + + +if (!$g['enableserial_force'] && ($g['platform'] == "pfSense" || $g['platform'] == "cdrom" || file_exists("/etc/nano_use_vga.txt"))) +{ + $form->add($section); + $section = new Form_Section('Serial Communications'); + + $section->addInput(new Form_Checkbox( + 'serial-terminal', + 'Serial Terminal', + 'Enables the first serial port with 115200/8/N/1 by default, or another speed selectable below.', + isset($pconfig['enableserial']) + ))->setHelp('Note: This will redirect the console output and messages to '. + 'the serial port. You can still access the console menu from the internal video '. + 'card/keyboard. A<b>null modem</b>serial cable or adapter is required to use the '. + 'serial console.'); + + $section->addInput(new Form_Select( + 'serial-speed', + 'Serial Speed', + $pconfig['serialspeed'], + array(115200, 57600, 38400, 19200, 14400, 9600) + ))->setHelp('Allows selection of different speeds for the serial console port.'); + + $section->addInput(new Form_Select( + 'primary-console', + 'Primary Console', + $pconfig['primaryconsole'], + array( + 'serial' => 'Serial Console', + 'video' => 'VGA Console', + ) + ))->setHelp('Select the preferred console if multiple consoles are present. '. + 'The preferred console will show pfSense boot script output. All consoles '. + 'display OS boot messages, console messages, and the console menu.'); +} + +$form->add($section); +$section = new Form_Section('Console Options'); + +$section->addInput(new Form_Checkbox( + 'console-menu', + 'Console menu', + 'Password protect the console menu', + $pconfig['disableconsolemenu'] +)); + +$form->add($section); +print $form; + +?> +<script> +//<![CDATA[ +events.push(function(){ + + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // On page load . . + hideInput('ssl-certificate', $('input[name=webguiproto]:checked').val() == 'http'); + + // On click . . + $('[id=webguiproto]').click(function () { + hideInput('ssl-certificate', $('input[name=webguiproto]:checked').val() == 'http'); + }); +}); +//]]> +</script> + +<?php +include("foot.inc"); + +if ($restart_webgui) + echo "<meta http-equiv=\"refresh\" content=\"20;url={$url}\" />"; + +if ($restart_sshd) +{ + killbyname("sshd"); + log_error(gettext("secure shell configuration has changed. Stopping sshd.")); + + if ($config['system']['enablesshd']) { + log_error(gettext("secure shell configuration has changed. Restarting sshd.")); + send_event("service restart sshd"); + } +} + +if ($restart_webgui) +{ + ob_flush(); + flush(); + log_error(gettext("webConfigurator configuration has changed. Restarting webConfigurator.")); + send_event("service restart webgui"); +}
\ No newline at end of file diff --git a/src/usr/local/www/system_advanced_firewall.php b/src/usr/local/www/system_advanced_firewall.php new file mode 100644 index 0000000..a31bfab --- /dev/null +++ b/src/usr/local/www/system_advanced_firewall.php @@ -0,0 +1,714 @@ +<?php +/* $Id$ */ +/* + system_advanced_firewall.php + part of pfSense + Copyright (C) 2005-2007 Scott Ullrich + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-system-advanced-firewall +##|*NAME=System: Advanced: Firewall and NAT page +##|*DESCR=Allow access to the 'System: Advanced: Firewall and NAT' page. +##|*MATCH=system_advanced_firewall.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +$pconfig['disablefilter'] = $config['system']['disablefilter']; +$pconfig['scrubnodf'] = $config['system']['scrubnodf']; +$pconfig['scrubrnid'] = $config['system']['scrubrnid']; +$pconfig['optimization'] = $config['filter']['optimization']; +$pconfig['adaptivestart'] = $config['system']['adaptivestart']; +$pconfig['adaptiveend'] = $config['system']['adaptiveend']; +$pconfig['maximumstates'] = $config['system']['maximumstates']; +$pconfig['aliasesresolveinterval'] = $config['system']['aliasesresolveinterval']; +$old_aliasesresolveinterval = $config['system']['aliasesresolveinterval']; +$pconfig['checkaliasesurlcert'] = isset($config['system']['checkaliasesurlcert']); +$pconfig['maximumtableentries'] = $config['system']['maximumtableentries']; +$pconfig['maximumfrags'] = $config['system']['maximumfrags']; +$pconfig['disablereplyto'] = isset($config['system']['disablereplyto']); +$pconfig['disablenegate'] = isset($config['system']['disablenegate']); +$pconfig['bogonsinterval'] = $config['system']['bogons']['interval']; +$pconfig['disablenatreflection'] = $config['system']['disablenatreflection']; +$pconfig['enablebinatreflection'] = $config['system']['enablebinatreflection']; +$pconfig['reflectiontimeout'] = $config['system']['reflectiontimeout']; +$pconfig['bypassstaticroutes'] = isset($config['filter']['bypassstaticroutes']); +$pconfig['disablescrub'] = isset($config['system']['disablescrub']); +$pconfig['tftpinterface'] = explode(",", $config['system']['tftpinterface']); +$pconfig['disablevpnrules'] = isset($config['system']['disablevpnrules']); +$pconfig['tcpfirsttimeout'] = $config['system']['tcpfirsttimeout']; +$pconfig['tcpopeningtimeout'] = $config['system']['tcpopeningtimeout']; +$pconfig['tcpestablishedtimeout'] = $config['system']['tcpestablishedtimeout']; +$pconfig['tcpclosingtimeout'] = $config['system']['tcpclosingtimeout']; +$pconfig['tcpfinwaittimeout'] = $config['system']['tcpfinwaittimeout']; +$pconfig['tcpclosedtimeout'] = $config['system']['tcpclosedtimeout']; +$pconfig['udpfirsttimeout'] = $config['system']['udpfirsttimeout']; +$pconfig['udpsingletimeout'] = $config['system']['udpsingletimeout']; +$pconfig['udpmultipletimeout'] = $config['system']['udpmultipletimeout']; +$pconfig['icmpfirsttimeout'] = $config['system']['icmpfirsttimeout']; +$pconfig['icmperrortimeout'] = $config['system']['icmperrortimeout']; +$pconfig['otherfirsttimeout'] = $config['system']['otherfirsttimeout']; +$pconfig['othersingletimeout'] = $config['system']['othersingletimeout']; +$pconfig['othermultipletimeout'] = $config['system']['othermultipletimeout']; + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if ((empty($_POST['adaptivestart']) && !empty($_POST['adaptiveend'])) || (!empty($_POST['adaptivestart']) && empty($_POST['adaptiveend']))) { + $input_errors[] = gettext("The Firewall Adaptive values must be set together."); + } + if (!empty($_POST['adaptivestart']) && !is_numericint($_POST['adaptivestart'])) { + $input_errors[] = gettext("The Firewall Adaptive Start value must be an integer."); + } + if (!empty($_POST['adaptive-end']) && !is_numericint($_POST['adaptive-end'])) { + $input_errors[] = gettext("The Firewall Adaptive End value must be an integer."); + } + if ($_POST['firewall-maximum-states'] && !is_numericint($_POST['firewall-maximum-states'])) { + $input_errors[] = gettext("The Firewall Maximum States value must be an integer."); + } + if ($_POST['aliases-hostnames-resolve-interval'] && !is_numericint($_POST['aliases-hostnames-resolve-interval'])) { + $input_errors[] = gettext("The Aliases Hostname Resolve Interval value must be an integer."); + } + if ($_POST['firewall-maximum-table-entries'] && !is_numericint($_POST['firewall-maximum-table-entries'])) { + $input_errors[] = gettext("The Firewall Maximum Table Entries value must be an integer."); + } + if ($_POST['maximumfrags'] && !is_numericint($_POST['maximumfrags'])) { + $input_errors[] = gettext("The Firewall Maximum Fragment Entries value must be an integer."); + } + if ($_POST['tcpidletimeout'] && !is_numericint($_POST['tcpidletimeout'])) { + $input_errors[] = gettext("The TCP idle timeout must be an integer."); + } + if ($_POST['reflectiontimeout'] && !is_numericint($_POST['reflectiontimeout'])) { + $input_errors[] = gettext("The Reflection timeout must be an integer."); + } + if ($_POST['tcpfirsttimeout'] && !is_numericint($_POST['tcpfirsttimeout'])) { + $input_errors[] = gettext("The TCP first timeout value must be an integer."); + } + if ($_POST['tcpopeningtimeout'] && !is_numericint($_POST['tcpopeningtimeout'])) { + $input_errors[] = gettext("The TCP opening timeout value must be an integer."); + } + if ($_POST['tcpestablishedtimeout'] && !is_numericint($_POST['tcpestablishedtimeout'])) { + $input_errors[] = gettext("The TCP established timeout value must be an integer."); + } + if ($_POST['tcpclosingtimeout'] && !is_numericint($_POST['tcpclosingtimeout'])) { + $input_errors[] = gettext("The TCP closing timeout value must be an integer."); + } + if ($_POST['tcpfinwaittimeout'] && !is_numericint($_POST['tcpfinwaittimeout'])) { + $input_errors[] = gettext("The TCP FIN wait timeout value must be an integer."); + } + if ($_POST['tcpclosedtimeout'] && !is_numericint($_POST['tcpclosedtimeout'])) { + $input_errors[] = gettext("The TCP closed timeout value must be an integer."); + } + if ($_POST['udpfirsttimeout'] && !is_numericint($_POST['udpfirsttimeout'])) { + $input_errors[] = gettext("The UDP first timeout value must be an integer."); + } + if ($_POST['udpsingletimeout'] && !is_numericint($_POST['udpsingletimeout'])) { + $input_errors[] = gettext("The UDP single timeout value must be an integer."); + } + if ($_POST['udpmultipletimeout'] && !is_numericint($_POST['udpmultipletimeout'])) { + $input_errors[] = gettext("The UDP multiple timeout value must be an integer."); + } + if ($_POST['icmpfirsttimeout'] && !is_numericint($_POST['icmpfirsttimeout'])) { + $input_errors[] = gettext("The ICMP first timeout value must be an integer."); + } + if ($_POST['icmperrortimeout'] && !is_numericint($_POST['icmperrortimeout'])) { + $input_errors[] = gettext("The ICMP error timeout value must be an integer."); + } + if ($_POST['otherfirsttimeout'] && !is_numericint($_POST['otherfirsttimeout'])) { + $input_errors[] = gettext("The Other first timeout value must be an integer."); + } + if ($_POST['othersingletimeout'] && !is_numericint($_POST['othersingletimeout'])) { + $input_errors[] = gettext("The Other single timeout value must be an integer."); + } + if ($_POST['othermultipletimeout'] && !is_numericint($_POST['othermultipletimeout'])) { + $input_errors[] = gettext("The Other multiple timeout value must be an integer."); + } + + ob_flush(); + flush(); + + if (!$input_errors) { + + if ($_POST['disablefilter'] == "yes") { + $config['system']['disablefilter'] = "enabled"; + } else { + unset($config['system']['disablefilter']); + } + + if ($_POST['disablevpnrules'] == "yes") { + $config['system']['disablevpnrules'] = true; + } else { + unset($config['system']['disablevpnrules']); + } + if ($_POST['rfc959workaround'] == "yes") { + $config['system']['rfc959workaround'] = "enabled"; + } else { + unset($config['system']['rfc959workaround']); + } + + if ($_POST['scrubnodf'] == "yes") { + $config['system']['scrubnodf'] = "enabled"; + } else { + unset($config['system']['scrubnodf']); + } + + if ($_POST['scrubrnid'] == "yes") { + $config['system']['scrubrnid'] = "enabled"; + } else { + unset($config['system']['scrubrnid']); + } + + if (!empty($_POST['adaptiveend'])) { + $config['system']['adaptiveend'] = $_POST['adaptiveend']; + } else { + unset($config['system']['adaptiveend']); + } + if (!empty($_POST['adaptivestart'])) { + $config['system']['adaptivestart'] = $_POST['adaptivestart']; + } else { + unset($config['system']['adaptivestart']); + } + + if ($_POST['checkaliasesurlcert'] == "yes") { + $config['system']['checkaliasesurlcert'] = true; + } else { + unset($config['system']['checkaliasesurlcert']); + } + + $config['system']['optimization'] = $_POST['optimization']; + $config['system']['maximumstates'] = $_POST['maximumstates']; + $config['system']['aliasesresolveinterval'] = $_POST['aliasesresolveinterval']; + $config['system']['maximumtableentries'] = $_POST['maximumtableentries']; + $config['system']['maximumfrags'] = $_POST['maximumfrags']; + + if (!empty($_POST['tcpfirsttimeout'])) { + $config['system']['tcpfirsttimeout'] = $_POST['tcpfirsttimeout']; + } else { + unset($config['system']['tcpfirsttimeout']); + } + if (!empty($_POST['tcpopeningtimeout'])) { + $config['system']['tcpopeningtimeout'] = $_POST['tcpopeningtimeout']; + } else { + unset($config['system']['tcpopeningtimeout']); + } + if (!empty($_POST['tcpestablishedtimeout'])) { + $config['system']['tcpestablishedtimeout'] = $_POST['tcpestablishedtimeout']; + } else { + unset($config['system']['tcpestablishedtimeout']); + } + if (!empty($_POST['tcpclosingtimeout'])) { + $config['system']['tcpclosingtimeout'] = $_POST['tcpclosingtimeout']; + } else { + unset($config['system']['tcpclosingtimeout']); + } + if (!empty($_POST['tcpfinwaittimeout'])) { + $config['system']['tcpfinwaittimeout'] = $_POST['tcpfinwaittimeout']; + } else { + unset($config['system']['tcpfinwaittimeout']); + } + if (!empty($_POST['tcpclosedtimeout'])) { + $config['system']['tcpclosedtimeout'] = $_POST['tcpclosedtimeout']; + } else { + unset($config['system']['tcpclosedtimeout']); + } + if (!empty($_POST['udpfirsttimeout'])) { + $config['system']['udpfirsttimeout'] = $_POST['udpfirsttimeout']; + } else { + unset($config['system']['udpfirsttimeout']); + } + if (!empty($_POST['udpsingletimeout'])) { + $config['system']['udpsingletimeout'] = $_POST['udpsingletimeout']; + } else { + unset($config['system']['udpsingletimeout']); + } + if (!empty($_POST['udpmultipletimeout'])) { + $config['system']['udpmultipletimeout'] = $_POST['udpmultipletimeout']; + } else { + unset($config['system']['udpmultipletimeout']); + } + if (!empty($_POST['icmpfirsttimeout'])) { + $config['system']['icmpfirsttimeout'] = $_POST['icmpfirsttimeout']; + } else { + unset($config['system']['icmpfirsttimeout']); + } + if (!empty($_POST['icmperrortimeout'])) { + $config['system']['icmperrortimeout'] = $_POST['icmperrortimeout']; + } else { + unset($config['system']['icmperrortimeout']); + } + if (!empty($_POST['otherfirsttimeout'])) { + $config['system']['otherfirsttimeout'] = $_POST['otherfirsttimeout']; + } else { + unset($config['system']['otherfirsttimeout']); + } + if (!empty($_POST['othersingletimeout'])) { + $config['system']['othersingletimeout'] = $_POST['othersingletimeout']; + } else { + unset($config['system']['othersingletimeout']); + } + if (!empty($_POST['othermultipletimeout'])) { + $config['system']['othermultipletimeout'] = $_POST['othermultipletimeout']; + } else { + unset($config['system']['othermultipletimeout']); + } + + if ($_POST['natreflection'] == "proxy") { + unset($config['system']['disablenatreflection']); + unset($config['system']['enablenatreflectionpurenat']); + } else if ($_POST['natreflection'] == "purenat") { + unset($config['system']['disablenatreflection']); + $config['system']['enablenatreflectionpurenat'] = "yes"; + } else { + $config['system']['disablenatreflection'] = "yes"; + unset($config['system']['enablenatreflectionpurenat']); + } + + if ($_POST['enablebinatreflection'] == "yes") { + $config['system']['enablebinatreflection'] = "yes"; + } else { + unset($config['system']['enablebinatreflection']); + } + + if ($_POST['disablereplyto'] == "yes") { + $config['system']['disablereplyto'] = $_POST['disablereplyto']; + } else { + unset($config['system']['disablereplyto']); + } + + if ($_POST['disablenegate'] == "yes") { + $config['system']['disablenegate'] = $_POST['disablenegate']; + } else { + unset($config['system']['disablenegate']); + } + + if ($_POST['enablenatreflectionhelper'] == "yes") { + $config['system']['enablenatreflectionhelper'] = "yes"; + } else { + unset($config['system']['enablenatreflectionhelper']); + } + + $config['system']['reflectiontimeout'] = $_POST['reflection-timeout']; + + if ($_POST['bypassstaticroutes'] == "yes") { + $config['filter']['bypassstaticroutes'] = $_POST['bypassstaticroutes']; + } elseif (isset($config['filter']['bypassstaticroutes'])) { + unset($config['filter']['bypassstaticroutes']); + } + + if ($_POST['disablescrub'] == "yes") { + $config['system']['disablescrub'] = $_POST['disablescrub']; + } else { + unset($config['system']['disablescrub']); + } + + if ($_POST['tftpinterface']) { + $config['system']['tftpinterface'] = implode(",", $_POST['tftpinterface']); + } else { + unset($config['system']['tftpinterface']); + } + + if ($_POST['update-frequency'] != $config['system']['bogons']['interval']) { + switch ($_POST['update-frequency']) { + case 'daily': + install_cron_job("/usr/bin/nice -n20 /etc/rc.update_bogons.sh", true, "1", "3", "*", "*", "*"); + break; + case 'weekly': + install_cron_job("/usr/bin/nice -n20 /etc/rc.update_bogons.sh", true, "1", "3", "*", "*", "0"); + break; + case 'monthly': + // fall through + default: + install_cron_job("/usr/bin/nice -n20 /etc/rc.update_bogons.sh", true, "1", "3", "1", "*", "*"); + } + $config['system']['bogons']['interval'] = $_POST['update-frequency']; + } + + write_config(); + + // Kill filterdns when value changes, filter_configure() will restart it + if (($old_aliasesresolveinterval != $config['system']['aliasesresolveinterval']) && + isvalidpid("{$g['varrun_path']}/filterdns.pid")) { + killbypid("{$g['varrun_path']}/filterdns.pid"); + } + + $retval = 0; + $retval = filter_configure(); + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message($retval); + } else { + $savemsg = $retval; + } + } +} + +$pgtitle = array(gettext("System"), gettext("Advanced: Firewall and NAT")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); + +$tab_array = array(); +$tab_array[] = array(gettext("Admin Access"), false, "system_advanced_admin.php"); +$tab_array[] = array(gettext("Firewall / NAT"), true, "system_advanced_firewall.php"); +$tab_array[] = array(gettext("Networking"), false, "system_advanced_network.php"); +$tab_array[] = array(gettext("Miscellaneous"), false, "system_advanced_misc.php"); +$tab_array[] = array(gettext("System Tunables"), false, "system_advanced_sysctl.php"); +$tab_array[] = array(gettext("Notifications"), false, "system_advanced_notifications.php"); +display_top_tabs($tab_array); + +?><div id="container"><?php + +require('classes/Form.class.php'); +$form = new Form; +$section = new Form_Section('Firewall Advanced'); + +$section->addInput(new Form_Checkbox( + 'ip-do-not-fragment-compatibility', + 'IP Do-Not-Fragment compatibility', + 'Clear invalid DF bits instead of dropping the packets', + isset($config['system']['scrubnodf']) +))->setHelp('This allows for communications with hosts that generate fragmented '. + 'packets with the don"t fragment (DF) bit set. Linux NFS is known to do this. '. + 'This will cause the filter to not drop such packets but instead clear the don"t '. + 'fragment bit.'); + +$section->addInput(new Form_Checkbox( + 'ip-random-id-generation', + 'IP Random id generation', + 'Insert a stronger id into IP header of packets passing through the filter.', + isset($config['system']['scrubrnid']) +))->setHelp('Replaces the IP identification field of packets with random values to '. + 'compensate for operating systems that use predictable values. This option only '. + 'applies to packets that are not fragmented after the optional packet '. + 'reassembly.'); + +$section->addInput($input = new Form_Select( + 'firewall-optimization-options', + 'Firewall Optimization Options', + $config['system']['optimization'], + array( + 'normal' => 'normal: the default optimization algorithm', + 'high-latency' => 'high-latency: used for eg. satellite links. Expires idle connections later than default', + 'aggressive' => 'aggressive: expires idle connections quicker. More efficient use of CPU and memory but can drop legitimate idle connections', + 'conservative' => 'conservative: tries to avoid dropping any legitimate idle connections at the expense of increased memory usage and CPU utilization.', + ) +))->setHelp('Select the type of state table optimization to use'); + +$section->addInput(new Form_Checkbox( + 'disable-firewall', + 'Disable Firewall', + 'Disable all packet filtering.', + isset($config['system']['disablefilter']) +))->setHelp('Note: This converts %s into a routing only platform!<br/>'. + 'Note: This will also turn off NAT! If you only want to disable NAT, '. + 'and not firewall rules, visit the <a href="firewall_nat_out.php">Outbound '. + 'NAT</a>page.', [$g["product_name"]]); + +$section->addInput(new Form_Checkbox( + 'disable-firewall-scrub', + 'Disable Firewall Scrub', + 'Disables the PF scrubbing option which can sometimes interfere with NFS and PPTP traffic.', + isset($config['system']['disablescrub']) +)); + +$group = new Form_Group('Firewall Adaptive Timeouts'); + +$group->add(new Form_Input( + 'adaptive-start', + 'Adaptive start', + 'number', + $pconfig['adaptivestart'], + ['min' => 1] +))->setHelp('When the number of state entries exceeds this value, adaptive '. + 'scaling begins. All timeout values are scaled linearly with factor '. + '(adaptive.end - number of states) / (adaptive.end - adaptive.start).'); + +$group->add(new Form_Input( + 'adaptive-end', + 'Adaptive end', + 'number', + $pconfig['adaptiveend'], + ['min' => 1] +))->setHelp('When reaching this number of state entries, all timeout values '. + 'become zero, effectively purging all state entries immediately. This '. + 'value is used to define the scale factor, it should not actually be '. + 'reached (set a lower state limit, see below).'); + +$group->setHelp('Timeouts for states can be scaled adaptively as the number of '. + 'state table entries grows. Leave blank for the default (0)'); + +$section->add($group); + +$section->addInput(new Form_Input( + 'firewall-maximum-states', + 'Firewall Maximum States', + 'number', + $pconfig['maximumstates'], + ['min' => 1, 'placeholder' => pfsense_default_state_size()] +))->setHelp('Maximum number of connections to hold in the firewall state table.. '. + '<br/>Note: Leave this blank for the default. On your system the default '. + 'size is: %d', [pfsense_default_state_size()]); + +$section->addInput(new Form_Input( + 'firewall-maximum-table-entries', + 'Firewall Maximum Table Entries', + 'text', + $pconfig['maximumtableentries'], + ['placeholder' => pfsense_default_table_entries_size()] +))->setHelp('Maximum number of table entries for systems such as aliases, '. + 'sshlockout, snort, etc, combined..<br/>Note: Leave this blank for the '. + 'default. On your system the default size is: %d', + [pfsense_default_table_entries_size()]); + +$section->addInput(new Form_Checkbox( + 'static-route-filtering', + 'Static route filtering', + 'Bypass firewall rules for traffic on the same interface', + $pconfig['bypassstaticroutes'] +))->setHelp('This option only applies if you have defined one or more static '. + 'routes. If it is enabled, traffic that enters and leaves through the same '. + 'interface will not be checked by the firewall. This may be desirable in some '. + 'situations where multiple subnets are connected to the same interface.'); + +$section->addInput(new Form_Checkbox( + 'disable-auto-added-vpn-rules', + 'Disable Auto-added VPN rules', + 'Disable all auto-added VPN rules.', + isset($config['system']['disablevpnrules']) +))->setHelp('<span>Note: This disables automatically added rules for IPsec, '. + 'PPTP.</span>'); + +$section->addInput(new Form_Checkbox( + 'disable-reply-to', + 'Disable reply-to', + 'Disable reply-to on WAN rules', + $pconfig['disablereplyto'] +))->setHelp('With Multi-WAN you generally want to ensure traffic leaves the same '. + 'interface it arrives on, hence reply-to is added automatically by default. When '. + 'using bridging, you must disable this behavior if the WAN gateway IP is '. + 'different from the gateway IP of the hosts behind the bridged interface.'); + +$section->addInput(new Form_Checkbox( + 'disable-negate-rules', + 'Disable Negate rules', + 'Disable Negate rule on policy routing rules', + $pconfig['disablenegate'] +))->setHelp('With Multi-WAN you generally want to ensure traffic reaches directly '. + 'connected networks and VPN networks when using policy routing. You can disable '. + 'this for special purposes but it requires manually creating rules for these '. + 'networks'); + +$section->addInput(new Form_Input( + 'aliases-hostnames-resolve-interval', + 'Aliases Hostnames Resolve Interval', + 'text', + $pconfig['aliasesresolveinterval'], + ['placeholder' => '300'] +))->setHelp('Interval, in seconds, that will be used to resolve hostnames '. + 'configured on aliases.. <br/>Note: Leave this blank for the default '. + '(300s).'); + +$section->addInput(new Form_Checkbox( + 'check-certificate-of-aliases-urls', + 'Check certificate of aliases URLs', + 'Verify HTTPS certificates when downloading alias URLs', + $pconfig['checkaliasesurlcert'] +))->setHelp('Make sure the certificate is valid for all HTTPS addresses on '. + 'aliases. If it\'s not valid or is revoked, do not download it.'); + +$form->add($section); +$section = new Form_Section('Bogon Networks'); + +$section->addInput(new Form_Select( + 'update-frequency', + 'Update Frequency', + empty($pconfig['bogonsinterval']) ? 'monthly' : $pconfig['bogonsinterval'], + array( + 'monthly' => 'Monthly', + 'weekly' => 'Weekly', + 'daily' => 'Daily', + ) +))->setHelp('The frequency of updating the lists of IP addresses that are '. + 'reserved (but not RFC 1918) or not yet assigned by IANA.'); + +$form->add($section); + +if (count($config['interfaces']) > 1) +{ + $section = new Form_Section('Network Address Translation'); + + if (isset($config['system']['disablenatreflection'])) + $value = 'disable'; + elseif (!isset($config['system']['enablenatreflectionpurenat'])) + $value = 'proxy'; + else + $value = 'purenat'; + + $section->addInput(new Form_Select( + 'nat-reflection-mode-for-port-forwards', + 'NAT Reflection mode for port forwards', + $value, + array( + 'disable' => 'disabled', + 'proxy' => 'NAT + proxy', + 'purenat' => 'Pure NAT', + ) + ))->setHelp('<ul><li>The pure NAT mode uses a set of NAT rules to direct '. + 'packets to the target of the port forward. It has better scalability, '. + 'but it must be possible to accurately determine the interface and '. + 'gateway IP used for communication with the target at the time the '. + 'rules are loaded. There are no inherent limits to the number of ports '. + 'other than the limits of the protocols. All protocols available for '. + 'port forwards are supported.</li><li>The NAT + proxy mode uses a '. + 'helper program to send packets to the target of the port forward. '. + 'It is useful in setups where the interface and/or gateway IP used '. + 'for communication with the target cannot be accurately determined at '. + 'the time the rules are loaded. Reflection rules are not created for '. + 'ranges larger than 500 ports and will not be used for more than 1000 '. + 'ports total between all port forwards. Only TCP and UDP protocols are '. + 'supported.</li></ul>Individual rules may be configured to override '. + 'this system setting on a per-rule basis.'); + + $section->addInput(new Form_Input( + 'reflection-timeout', + 'Reflection Timeout', + 'number', + $config['system']['reflectiontimeout'], + ['min' => 1] + ))->setHelp('Enter value for Reflection timeout in seconds.<br/>Note: Only '. + 'applies to Reflection on port forwards in NAT + proxy mode.'); + + $section->addInput(new Form_Checkbox( + 'enable-nat-reflection-for-1-1-nat', + 'Enable NAT Reflection for 1:1 NAT', + 'Automatic creation of additional NAT redirect rules from within your internal networks.', + isset($config['system']['enablebinatreflection']) + ))->setHelp('Note: Reflection on 1:1 mappings is only for the inbound component of '. + 'the 1:1 mappings. This functions the same as the pure NAT mode for port '. + 'forwards. For more details, refer to the pure NAT mode description '. + 'above. Individual rules may be configured to override this system setting on a '. + 'per-rule basis.'); + + $section->addInput(new Form_Checkbox( + 'enable-automatic-outbound-nat-for-reflection', + 'Enable automatic outbound NAT for Reflection', + 'Automatic create outbound NAT rules that direct traffic back out to the same subnet it originated from.', + isset($config['system']['enablenatreflectionhelper']) + ))->setHelp('Required for full functionality of the pure NAT mode of NAT '. + 'Reflection for port forwards or NAT Reflection for 1:1 NAT.Note: This only works '. + 'for assigned interfaces. Other interfaces require manually creating the '. + 'outbound NAT rules that direct the reply packets back through the router.'); + + $section->addInput(new Form_Select( + 'tftp-proxy', + 'TFTP Proxy', + $pconfig['tftpinterface'], + get_configured_interface_with_descr(), + true + ))->setHelp('Choose the interfaces where you want TFTP proxy helper to be enabled.'); + + $form->add($section); +} + +$section = new Form_Section('State Timeouts'); + +$group = new Form_Group('TCP Timeouts'); +$tcpTimeouts = array('First', 'Opening', 'Established', 'Closing', 'FIN', 'closed'); +foreach ($tcpTimeouts as $name) +{ + $group->add(new Form_Input( + 'tcp'. strtolower($name) .'timeout', + 'TCP '. $name, + 'number', + $config['system']['tcp'. strtolower($name) .'timeout'] + ))->setHelp('Enter value for TCP '. $name .' timeout in seconds. Leave blank for '. + 'default (recommended).'); +} + +$section->add($group); + +$group = new Form_Group('UDP Timeouts'); +$udpTimeouts = array('First', 'Single', 'Multiple'); +foreach ($udpTimeouts as $name) +{ + $group->add(new Form_Input( + 'udp'. strtolower($name) .'timeout', + 'UDP '. $name, + 'number', + $config['system']['udo'. strtolower($name) .'timeout'] + ))->setHelp('Enter value for UDP '. $name .' timeout in seconds. Leave blank for '. + 'default (recommended).'); +} + +$section->add($group); + +$group = new Form_Group('ICMP Timeouts'); +$udpTimeouts = array('First', 'Error'); +foreach ($udpTimeouts as $name) +{ + $group->add(new Form_Input( + 'icmp'. strtolower($name) .'timeout', + 'UDP '. $name, + 'number', + $config['system']['icmp'. strtolower($name) .'timeout'] + ))->setHelp('Enter value for ICMP '. $name .' timeout in seconds. Leave blank for '. + 'default (recommended).'); +} + +$section->add($group); + +$group = new Form_Group('Other Timeouts'); +foreach ($udpTimeouts as $name) +{ + $group->add(new Form_Input( + 'other'. strtolower($name) .'timeout', + 'Other '. $name, + 'number', + $config['system']['other'. strtolower($name) .'timeout'] + ))->setHelp('Enter value for ICMP '. $name .' timeout in seconds. Leave blank for '. + 'default (recommended).'); +} + +$section->add($group); + +print $form; +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_advanced_misc.php b/src/usr/local/www/system_advanced_misc.php new file mode 100644 index 0000000..17676a8 --- /dev/null +++ b/src/usr/local/www/system_advanced_misc.php @@ -0,0 +1,574 @@ +<?php +/* $Id$ */ +/* + system_advanced_misc.php + part of pfSense + Copyright (C) 2005-2007 Scott Ullrich + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-system-advanced-misc +##|*NAME=System: Advanced: Miscellaneous page +##|*DESCR=Allow access to the 'System: Advanced: Miscellaneous' page. +##|*MATCH=system_advanced_misc.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("vpn.inc"); +require_once("vslb.inc"); + +$pconfig['proxyurl'] = $config['system']['proxyurl']; +$pconfig['proxyport'] = $config['system']['proxyport']; +$pconfig['proxyuser'] = $config['system']['proxyuser']; +$pconfig['proxypass'] = $config['system']['proxypass']; +$pconfig['harddiskstandby'] = $config['system']['harddiskstandby']; +$pconfig['lb_use_sticky'] = isset($config['system']['lb_use_sticky']); +$pconfig['srctrack'] = $config['system']['srctrack']; +$pconfig['gw_switch_default'] = isset($config['system']['gw_switch_default']); +$pconfig['powerd_enable'] = isset($config['system']['powerd_enable']); +$pconfig['crypto_hardware'] = $config['system']['crypto_hardware']; +$pconfig['thermal_hardware'] = $config['system']['thermal_hardware']; +$pconfig['schedule_states'] = isset($config['system']['schedule_states']); +$pconfig['kill_states'] = isset($config['system']['kill_states']); +$pconfig['skip_rules_gw_down'] = isset($config['system']['skip_rules_gw_down']); +$pconfig['apinger_debug'] = isset($config['system']['apinger_debug']); +$pconfig['use_mfs_tmpvar'] = isset($config['system']['use_mfs_tmpvar']); +$pconfig['use_mfs_tmp_size'] = $config['system']['use_mfs_tmp_size']; +$pconfig['use_mfs_var_size'] = $config['system']['use_mfs_var_size']; +$pconfig['pkg_nochecksig'] = isset($config['system']['pkg_nochecksig']); +$pconfig['host_uuid'] = !isset($config['system']['host_uuid']); + +$pconfig['powerd_ac_mode'] = "hadp"; +if (!empty($config['system']['powerd_ac_mode'])) { + $pconfig['powerd_ac_mode'] = $config['system']['powerd_ac_mode']; +} + +$pconfig['powerd_battery_mode'] = "hadp"; +if (!empty($config['system']['powerd_battery_mode'])) { + $pconfig['powerd_battery_mode'] = $config['system']['powerd_battery_mode']; +} + +$pconfig['powerd_normal_mode'] = "hadp"; +if (!empty($config['system']['powerd_normal_mode'])) { + $pconfig['powerd_normal_mode'] = $config['system']['powerd_normal_mode']; +} + +$crypto_modules = array( + 'glxsb' => gettext("AMD Geode LX Security Block"), + 'aesni' => gettext("AES-NI CPU-based Acceleration")); + +$thermal_hardware_modules = array( + 'coretemp' => gettext("Intel Core* CPU on-die thermal sensor"), + 'amdtemp' => gettext("AMD K8, K10 and K11 CPU on-die thermal sensor")); + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + ob_flush(); + flush(); + + if (!empty($_POST['crypto_hardware']) && !array_key_exists($_POST['crypto_hardware'], $crypto_modules)) { + $input_errors[] = gettext("Please select a valid Cryptographic Accelerator."); + } + + if (!empty($_POST['thermal_hardware']) && !array_key_exists($_POST['thermal_hardware'], $thermal_hardware_modules)) { + $input_errors[] = gettext("Please select a valid Thermal Hardware Sensor."); + } + + if (!empty($_POST['use_mfs_tmp_size']) && (!is_numeric($_POST['use_mfs_tmp_size']) || ($_POST['use_mfs_tmp_size'] < 40))) { + $input_errors[] = gettext("/tmp Size must be numeric and should not be less than 40MB."); + } + + if (!empty($_POST['use_mfs_var_size']) && (!is_numeric($_POST['use_mfs_var_size']) || ($_POST['use_mfs_var_size'] < 60))) { + $input_errors[] = gettext("/var Size must be numeric and should not be less than 60MB."); + } + + if (!empty($_POST['proxyport']) && !is_port($_POST['proxyport'])) { + $input_errors[] = gettext("Proxy port must be a valid port number, 1-65535."); + } + + if (!empty($_POST['proxyurl']) && !is_fqdn($_POST['proxyurl']) && !is_ipaddr($_POST['proxyurl'])) { + $input_errors[] = gettext("Proxy URL must be a valid IP address or FQDN."); + } + + if (!empty($_POST['proxyuser']) && preg_match("/[^a-zA-Z0-9\.\-_@]/", $_POST['proxyuser'])) { + $input_errors[] = gettext("The proxy username contains invalid characters."); + } + + if (!$input_errors) { + + if ($_POST['harddiskstandby'] <> "") { + $config['system']['harddiskstandby'] = $_POST['harddiskstandby']; + system_set_harddisk_standby(); + } else { + unset($config['system']['harddiskstandby']); + } + + if ($_POST['proxyurl'] <> "") { + $config['system']['proxyurl'] = $_POST['proxyurl']; + } else { + unset($config['system']['proxyurl']); + } + + if ($_POST['proxyport'] <> "") { + $config['system']['proxyport'] = $_POST['proxyport']; + } else { + unset($config['system']['proxyport']); + } + + if ($_POST['proxyuser'] <> "") { + $config['system']['proxyuser'] = $_POST['proxyuser']; + } else { + unset($config['system']['proxyuser']); + } + + if ($_POST['proxypass'] <> "") { + $config['system']['proxypass'] = $_POST['proxypass']; + } else { + unset($config['system']['proxypass']); + } + + $need_relayd_restart = false; + if ($_POST['lb_use_sticky'] == "yes") { + if (!isset($config['system']['lb_use_sticky'])) { + $config['system']['lb_use_sticky'] = true; + $need_relayd_restart = true; + } + if ($config['system']['srctrack'] != $_POST['source-tracking-timeout']) { + $config['system']['srctrack'] = $_POST['source-tracking-timeout']; + $need_relayd_restart = true; + } + } else { + if (isset($config['system']['lb_use_sticky'])) { + unset($config['system']['lb_use_sticky']); + $need_relayd_restart = true; + } + } + + if ($_POST['gw_switch_default'] == "yes") { + $config['system']['gw_switch_default'] = true; + } else { + unset($config['system']['gw_switch_default']); + } + + if ($_POST['pkg_nochecksig'] == "yes") { + $config['system']['pkg_nochecksig'] = true; + } elseif (isset($config['system']['pkg_nochecksig'])) { + unset($config['system']['pkg_nochecksig']); + } + + if ($_POST['host_uuid'] == "yes") { + unset($config['system']['host_uuid']); + } else { + $config['system']['host_uuid'] = true; + } + + if ($_POST['powerd_enable'] == "yes") { + $config['system']['powerd_enable'] = true; + } else { + unset($config['system']['powerd_enable']); + } + + $config['system']['powerd_ac_mode'] = $_POST['ac-power']; + $config['system']['powerd_battery_mode'] = $_POST['battery-power']; + $config['system']['powerd_normal_mode'] = $_POST['unknown-power']; + + if ($_POST['crypto_hardware']) { + $config['system']['crypto_hardware'] = $_POST['crypto_hardware']; + } else { + unset($config['system']['crypto_hardware']); + } + + if ($_POST['thermal_hardware']) { + $config['system']['thermal_hardware'] = $_POST['thermal_hardware']; + } else { + unset($config['system']['thermal_hardware']); + } + + if ($_POST['schedule_states'] == "yes") { + $config['system']['schedule_states'] = true; + } else { + unset($config['system']['schedule_states']); + } + + if ($_POST['kill_states'] == "yes") { + $config['system']['kill_states'] = true; + } else { + unset($config['system']['kill_states']); + } + + if ($_POST['skip_rules_gw_down'] == "yes") { + $config['system']['skip_rules_gw_down'] = true; + } else { + unset($config['system']['skip_rules_gw_down']); + } + + $need_apinger_restart = false; + if ($_POST['apinger_debug'] == "yes") { + if (!isset($config['system']['apinger_debug'])) { + $need_apinger_restart = true; + } + $config['system']['apinger_debug'] = true; + } else { + if (isset($config['system']['apinger_debug'])) { + $need_apinger_restart = true; + } + unset($config['system']['apinger_debug']); + } + + if ($_POST['use_mfs_tmpvar'] == "yes") { + $config['system']['use_mfs_tmpvar'] = true; + } else { + unset($config['system']['use_mfs_tmpvar']); + } + + $config['system']['use_mfs_tmp_size'] = $_POST['-tmp-ram-disk-size']; + $config['system']['use_mfs_var_size'] = $_POST['-var-ram-disk-size']; + + if (isset($_POST['periodic-rrd-backup'])) { + $config['system']['rrdbackup'] = $_POST['periodic-rrd-backup']; + install_cron_job("/etc/rc.backup_rrd.sh", ($config['system']['rrdbackup'] > 0), $minute="0", "*/{$config['system']['rrdbackup']}"); + } + if (isset($_POST['periodic-dhcp-leases-backup'])) { + $config['system']['dhcpbackup'] = $_POST['periodic-dhcp-leases-backup']; + install_cron_job("/etc/rc.backup_dhcpleases.sh", ($config['system']['dhcpbackup'] > 0), $minute="0", "*/{$config['system']['dhcpbackup']}"); + } + + write_config(); + + $retval = 0; + system_resolvconf_generate(true); + $retval = filter_configure(); + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message(gettext($retval)); + } else { + $savemsg = gettext($retval); + } + + activate_powerd(); + load_crypto(); + load_thermal_hardware(); + if ($need_relayd_restart) { + relayd_configure(); + } + if ($need_apinger_restart) { + setup_gateways_monitor(); + } + } +} + +$pgtitle = array(gettext("System"), gettext("Advanced: Miscellaneous")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); + +$tab_array = array(); +$tab_array[] = array(gettext("Admin Access"), false, "system_advanced_admin.php"); +$tab_array[] = array(gettext("Firewall / NAT"), false, "system_advanced_firewall.php"); +$tab_array[] = array(gettext("Networking"), false, "system_advanced_network.php"); +$tab_array[] = array(gettext("Miscellaneous"), true, "system_advanced_misc.php"); +$tab_array[] = array(gettext("System Tunables"), false, "system_advanced_sysctl.php"); +$tab_array[] = array(gettext("Notifications"), false, "system_advanced_notifications.php"); +display_top_tabs($tab_array); + +?><div id="container"><?php + +require('classes/Form.class.php'); +$form = new Form; +$section = new Form_Section('Proxy support'); + +$section->addInput(new Form_Input( + 'proxy-url', + 'Proxy URL', + 'text', + $pconfig['proxyurl'] +))->setHelp('Hostname or IP address of proxy server this system will '. + 'use for its outbound Internet access.'); + +$section->addInput(new Form_Input( + 'proxy-port', + 'Proxy Port', + 'text', + $pconfig['proxyport'] +))->setHelp('Port where proxy server is listening.'); + +$section->addInput(new Form_Input( + 'proxy-username', + 'Proxy Username', + 'text', + $pconfig['proxyuser'] +))->setHelp('Username for authentication to proxy server. Optional, '. + 'leave blank to not use authentication.'); + +$section->addInput(new Form_Input( + 'proxy-password', + 'Proxy Password', + 'text', + $pconfig['proxypass'] +))->setHelp('Password for authentication to proxy server.'); + +$form->add($section); +$section = new Form_Section('Load Balancing'); + +$group = new Form_Group('Load Balancing'); + +$group->add(new Form_Checkbox( + 'use-sticky-connections', + 'Use sticky connections', + 'Use sticky connections', + $pconfig['lb_use_sticky'] +))->setHelp('Successive connections will be redirected to the servers in a '. + 'round-robin manner with connections from the same source being sent to the '. + 'same web server. This "sticky connection" will exist as long as there are '. + 'states that refer to this connection. Once the states expire, so will the '. + 'sticky connection. Further connections from that host will be redirected '. + 'to the next web server in the round robin. Changing this option will '. + 'restart the Load Balancing service.'); + +$group->add(new Form_Input( + 'source-tracking-timeout', + 'Source tracking timeout', + 'number', + $pconfig['srctrack'], + ['placeholder' => 1400] +))->setHelp('Set the source tracking timeout for sticky connections. By default '. + 'this is 0, so source tracking is removed as soon as the state expires. '. + 'Setting this timeout higher will cause the source/destination relationship '. + 'to persist for longer periods of time.'); + +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'default-gateway-switching', + 'Default gateway switching', + 'Enable default gateway switching', + $pconfig['gw_switch_default'] +))->setHelp('If the default gateway goes down, switch the default gateway to '. + 'another available one. This is not enabled by default, as it"s unnecessary in '. + 'most all scenarios, which instead use gateway groups.'); + +$form->add($section); +$section = new Form_Section('Power savings'); + +$section->addInput(new Form_Checkbox( + 'powerd', + 'PowerD', + 'Enable PowerD', + $pconfig['powerd_enable'] +))->setHelp('The powerd utility monitors '. + 'the system state and sets various power control options accordingly. It offers '. + 'four modes (maximum, minimum, adaptive and hiadaptive) that can be individually '. + 'selected while on AC power or batteries. The modes maximum, minimum, adaptive '. + 'and hiadaptive may be abbreviated max, min, adp, hadp. Maximum mode chooses the '. + 'highest performance values. Minimum mode selects the lowest performance values '. + 'to get the most power savings. Adaptive mode attempts to strike a balance by '. + 'degrading performance when the system appears idle and increasing it when the '. + 'system is busy. It offers a good balance between a small performance loss for '. + 'greatly increased power savings. Hiadaptive mode is alike adaptive mode, but '. + 'tuned for systems where performance and interactivity are more important than '. + 'power consumption. It raises frequency faster, drops slower and keeps twice '. + 'lower CPU load.'); + +$modes = array( + 'hadp' => 'Hiadaptive', + 'adp' => 'Adaptive', + 'min' => 'Minimum', + 'max' => 'Maximum', +); + +$section->addInput(new Form_Select( + 'ac-power', + 'AC Power', + $pconfig['powerd_ac_mode'], + $modes +)); + +$section->addInput(new Form_Select( + 'battery-power', + 'Battery Power', + $pconfig['powerd_battery_mode'], + $modes +)); + +$section->addInput(new Form_Select( + 'unknown-power', + 'Unknown Power', + $pconfig['powerd_normal_mode'], + $modes +)); + +$form->add($section); +$section = new Form_Section('Cryptographic & Thermal Hardware'); + +$section->addInput(new Form_Select( + 'cryptographic-hardware', + 'Cryptographic Hardware', + $pconfig['crypto_hardware'], + $crypto_modules +))->setHelp('A cryptographic '. + 'accelerator module will use hardware support to speed up some cryptographic '. + 'functions on systems which have the chip. Do not enable this option if you have '. + 'a Hifn cryptographic acceleration card, as this will take precedence and the '. + 'Hifn card will not be used. Acceleration should be automatic for IPsec when '. + 'using a cipher supported by your chip, such as AES-128. OpenVPN should be set '. + 'for AES-128-CBC and have cryptodev enabled for hardware acceleration.If you do '. + 'not have a crypto chip in your system, this option will have no effect. To '. + 'unload the selected module, set this option to "none" and then reboot.'); + +$section->addInput(new Form_Select( + 'thermal-sensors', + 'Thermal Sensors', + $pconfig['thermal_hardware'], + array('' => 'None/ACPI') + $thermal_hardware_modules +))->setHelp('If you have a '. + 'supported CPU, selecting a themal sensor will load the appropriate driver to '. + 'read its temperature. Setting this to "None" will attempt to read the '. + 'temperature from an ACPI-compliant motherboard sensor instead, if one is '. + 'present.If you do not have a supported thermal sensor chip in your system, this '. + 'option will have no effect. To unload the selected module, set this option to '. + '"none" and then reboot.'); + +$form->add($section); +$section = new Form_Section('Schedules'); + +$section->addInput(new Form_Checkbox( + 'schedule-states', + 'Schedule States', + 'Do not kill connections when schedule expires', + $pconfig['schedule_states'] +))->setHelp('By default, when a schedule expires, connections permitted by that '. + 'schedule are killed. This option overrides that behavior by not clearing states '. + 'for existing connections.'); + +$form->add($section); +$section = new Form_Section('Gateway Monitoring'); + +$section->addInput(new Form_Checkbox( + 'state-killing-on-gateway-failure', + 'State Killing on Gateway Failure', + 'Flush states for a gateway that goes down', + $pconfig['kill_states'] +))->setHelp('The monitoring process will flush states for a gateway that goes down '. + 'if this box is not checked. Check this box to disable this behavior.'); + +$section->addInput(new Form_Checkbox( + 'skip-rules-when-gateway-is-down', + 'Skip rules when gateway is down', + 'Do not create rules when gateway is down', + $pconfig['skip_rules_gw_down'] +))->setHelp('By default, when a rule has a gateway specified and this gateway is '. + 'down, the rule is created omitting the gateway. This option overrides that '. + 'behavior by omitting the entire rule instead.'); + +$section->addInput(new Form_Checkbox( + 'gateway-monitoring-logging', + 'Gateway monitoring logging', + 'Enable debug logging', + $pconfig['apinger_debug'] +))->setHelp('Enable this setting to log debug information from the gateway '. + 'monitoring process to the system logs.'); + +$form->add($section); +$section = new Form_Section('RAM Disk Settings (Reboot to Apply Changes)'); + +$section->addInput(new Form_Checkbox( + 'use-ram-disks', + 'Use RAM Disks', + 'Use memory file system for /tmp and /var', + ($pconfig['use_mfs_tmpvar'] || $g['platform'] != "pfSense") +))->setHelp('Set this if you wish to use /tmp and /var as RAM disks (memory file '. + 'system disks) on a full install rather than use the hard disk. Setting this will '. + 'cause the data in /tmp and /var to be lost at reboot, including log data. RRD '. + 'and DHCP Leases will be retained.'); + +$section->addInput(new Form_Input( + '-tmp-ram-disk-size', + '/tmp RAM Disk Size', + 'number', + $pconfig['use_mfs_tmp_size'], + ['placeholder' => 40] +))->setHelp('Set the size, in MB, for the /tmp '. + 'RAM disk. Leave blank for 40MB. Do not set lower than 40.'); + +$section->addInput(new Form_Input( + '-var-ram-disk-size', + '/var RAM Disk Size', + 'number', + $pconfig['use_mfs_var_size'], + ['placeholder' => 60] +))->setHelp('Set the size, in MB, for the /var '. + 'RAM disk. Leave blank for 60MB. Do not set lower than 60.'); + +$section->addInput(new Form_Input( + 'periodic-rrd-backup', + 'Periodic RRD Backup', + 'number', + $config['system']['rrdbackup'], + ['min' => 1, 'max' => 24, 'placeholder' => 'frequency between 1 and 24 hours'] +))->setHelp('This will periodically backup the RRD data so '. + 'it can be restored automatically on the next boot. Keep in mind that the more '. + 'frequent the backup, the more writes will happen to your media.'); + +$section->addInput(new Form_Input( + 'periodic-dhcp-leases-backup', + 'Periodic DHCP Leases Backup', + 'number', + $config['system']['rrdbackup'], + ['min' => 1, 'max' => 24, 'placeholder' => 'frequency between 1 and 24 hours'] +))->setHelp('This will periodically backup the DHCP leases so '. + 'it can be restored automatically on the next boot. Keep in mind that the more '. + 'frequent the backup, the more writes will happen to your media.'); + +$form->add($section); +$section = new Form_Section('Package settings'); + +$section->addInput(new Form_Checkbox( + 'package-signature', + 'Package signature', + 'Disable check package signature', + $pconfig['pkg_nochecksig'] +))->setHelp('Enable this option to allow pfSense to install any package without '. + 'checking its signature.'); + +$form->add($section); +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_advanced_network.php b/src/usr/local/www/system_advanced_network.php new file mode 100644 index 0000000..c770603 --- /dev/null +++ b/src/usr/local/www/system_advanced_network.php @@ -0,0 +1,277 @@ +<?php +/* + system_advanced_network.php + part of pfSense + Copyright (C) 2005-2007 Scott Ullrich + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-system-advanced-network +##|*NAME=System: Advanced: Network page +##|*DESCR=Allow access to the 'System: Advanced: Networking' page. +##|*MATCH=system_advanced_network.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + + +$pconfig['ipv6nat_enable'] = isset($config['diag']['ipv6nat']['enable']); +$pconfig['ipv6nat_ipaddr'] = $config['diag']['ipv6nat']['ipaddr']; +$pconfig['ipv6allow'] = isset($config['system']['ipv6allow']); +$pconfig['prefer_ipv4'] = isset($config['system']['prefer_ipv4']); +$pconfig['polling_enable'] = isset($config['system']['polling']); +$pconfig['sharednet'] = $config['system']['sharednet']; +$pconfig['disablechecksumoffloading'] = isset($config['system']['disablechecksumoffloading']); +$pconfig['disablesegmentationoffloading'] = isset($config['system']['disablesegmentationoffloading']); +$pconfig['disablelargereceiveoffloading'] = isset($config['system']['disablelargereceiveoffloading']); + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + if ($_POST['ipv6nat_enable'] && !is_ipaddr($_POST['ipv6nat_ipaddr'])) { + $input_errors[] = gettext("You must specify an IP address to NAT IPv6 packets."); + } + + ob_flush(); + flush(); + if (!$input_errors) { + + if ($_POST['ipv6nat_enable'] == "yes") { + $config['diag']['ipv6nat']['enable'] = true; + $config['diag']['ipv6nat']['ipaddr'] = $_POST['ip-address']; + } else { + if ($config['diag']) { + if ($config['diag']['ipv6nat']) { + unset($config['diag']['ipv6nat']['enable']); + unset($config['diag']['ipv6nat']['ipaddr']); + } + } + } + + if ($_POST['ipv6allow'] == "yes") { + $config['system']['ipv6allow'] = true; + } else { + unset($config['system']['ipv6allow']); + } + + if ($_POST['prefer_ipv4'] == "yes") { + $config['system']['prefer_ipv4'] = true; + } else { + unset($config['system']['prefer_ipv4']); + } + + if ($_POST['sharednet'] == "yes") { + $config['system']['sharednet'] = true; + system_disable_arp_wrong_if(); + } else { + unset($config['system']['sharednet']); + system_enable_arp_wrong_if(); + } + + if ($_POST['polling_enable'] == "yes") { + $config['system']['polling'] = true; + setup_polling(); + } else { + unset($config['system']['polling']); + setup_polling(); + } + + if ($_POST['disablechecksumoffloading'] == "yes") { + $config['system']['disablechecksumoffloading'] = true; + } else { + unset($config['system']['disablechecksumoffloading']); + } + + if ($_POST['disablesegmentationoffloading'] == "yes") { + $config['system']['disablesegmentationoffloading'] = true; + } else { + unset($config['system']['disablesegmentationoffloading']); + } + + if ($_POST['disablelargereceiveoffloading'] == "yes") { + $config['system']['disablelargereceiveoffloading'] = true; + } else { + unset($config['system']['disablelargereceiveoffloading']); + } + + setup_microcode(); + + // Write out configuration (config.xml) + write_config(); + + // Set preferred protocol + prefer_ipv4_or_ipv6(); + + $retval = filter_configure(); + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message(gettext($retval)); + } else { + $savemsg = gettext($retval); + } + } +} + +$pgtitle = array(gettext("System"), gettext("Advanced: Networking")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); + +$tab_array = array(); +$tab_array[] = array(gettext("Admin Access"), false, "system_advanced_admin.php"); +$tab_array[] = array(gettext("Firewall / NAT"), false, "system_advanced_firewall.php"); +$tab_array[] = array(gettext("Networking"), true, "system_advanced_network.php"); +$tab_array[] = array(gettext("Miscellaneous"), false, "system_advanced_misc.php"); +$tab_array[] = array(gettext("System Tunables"), false, "system_advanced_sysctl.php"); +$tab_array[] = array(gettext("Notifications"), false, "system_advanced_notifications.php"); +display_top_tabs($tab_array); + +?><div id="container"><?php + +require('classes/Form.class.php'); +$form = new Form; +$section = new Form_Section('IPv6 Options'); + +$section->addInput(new Form_Checkbox( + 'allow-ipv6', + 'Allow IPv6', + 'All IPv6 traffic will be blocked by the firewall unless this box is checked', + $pconfig['ipv6allow'] +))->setHelp('NOTE: This does not disable any IPv6 features on the firewall, it only '. + 'blocks traffic.'); + +$group = new Form_Group('IPv6 over IPv4 Tunneling'); +$group->add(new Form_Checkbox( + 'ipv6-over-ipv4-tunneling', + 'IPv6 over IPv4 Tunneling', + 'Enable IPv4 NAT encapsulation of IPv6 packets', + $pconfig['ipv6allow'] +))->setHelp('NOTE: This does not disable any IPv6 features on the firewall, it only '. + 'blocks traffic.'); + +$group->add(new Form_Input( + 'ip-address', + 'IP address', + 'text', + $pconfig['ipv6nat_ipaddr'] +))->setHelp('Enable IPv4 NAT encapsulation of IPv6 packets. <br/>This provides an '. + 'RFC 2893 compatibility mechanism that can be used to tunneling IPv6 packets over '. + 'IPv4 routing infrastructures. If enabled, don"t forget to add a firewall rule to '. + 'permit IPv6 packets.'); + +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'prefer-ipv4-over-ipv6', + 'Prefer IPv4 over IPv6', + 'Prefer to use IPv4 even if IPv6 is available', + $pconfig['prefer_ipv4'] +))->setHelp('By default, if a hostname resolves IPv6 and IPv4 addresses IPv6 will '. + 'be used, if you check this option, IPv4 will be used instead of IPv6.'); + +$form->add($section); +$section = new Form_Section('Network Interfaces'); + +$section->addInput(new Form_Checkbox( + 'device-polling', + 'Device polling', + 'Enable device polling', + $pconfig['polling_enable'] +))->setHelp('Device polling is a technique that lets the system periodically poll '. + 'network devices for new data instead of relying on interrupts. This prevents '. + 'your webConfigurator, SSH, etc. from being inaccessible due to interrupt floods '. + 'when under extreme load. Generally this is not recommended. Not all NICs support '. + 'polling; see the %s homepage for a list of supported cards', [$g["product_name"]]); + + +$section->addInput(new Form_Checkbox( + 'hardware-checksum-offloading', + 'Hardware Checksum Offloading', + 'Disable hardware checksum offload', + isset($config['system']['disablechecksumoffloading']) +))->setHelp('Checking this option will disable hardware checksum offloading.<br/>'. + 'Checksum offloading is broken in some hardware, particularly some Realtek cards. '. + 'Rarely, drivers may have problems with checksum offloading and some specific '. + 'NICs.This will take effect after you reboot the machine or re-configure each '. + 'interface.'); + +$section->addInput(new Form_Checkbox( + 'hardware-tcp-segmentation-offloading', + 'Hardware TCP Segmentation Offloading', + 'Disable hardware TCP segmentation offload', + isset($config['system']['disablesegmentationoffloading']) +))->setHelp('Checking this option will disable hardware TCP segmentation '. + 'offloading (TSO, TSO4, TSO6). This offloading is broken in some hardware '. + 'drivers, and may impact performance with some specific NICs.This will take '. + 'effect after you reboot the machine or re-configure each interface.'); + +$section->addInput(new Form_Checkbox( + 'hardware-large-receive-offloading', + 'Hardware Large Receive Offloading', + 'Disable hardware large receive offload', + isset($config['system']['disablelargereceiveoffloading']) +))->setHelp('Checking this option will disable hardware large receive offloading '. + '(LRO). This offloading is broken in some hardware drivers, and may impact '. + 'performance with some specific NICs.This will take effect after you reboot the '. + 'machine or re-configure each interface.'); + +$section->addInput(new Form_Checkbox( + 'arp-handling', + 'ARP Handling', + 'Suppress ARP messages', + isset($pconfig['sharednet']) +))->setHelp('This option will suppress ARP log messages when multiple interfaces '. + 'reside on the same broadcast domain'); + +if (get_freebsd_version() == 8) +{ + $section->addInput(new Form_Checkbox( + 'Enable Flowtable', + 'Enable flowtable support', + $pconfig['flowtable'] + ))->setHelp('Enables infrastructure for caching flows as a means of accelerating '. + 'L3 and L2 lookupsas well as providing stateful load balancing when used with '. + 'RADIX_MPATH.'); +} + +$form->add($section); +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_advanced_notifications.php b/src/usr/local/www/system_advanced_notifications.php new file mode 100644 index 0000000..91a3f70 --- /dev/null +++ b/src/usr/local/www/system_advanced_notifications.php @@ -0,0 +1,341 @@ +<?php +/* $Id$ */ +/* + system_advanced_notifications.php + part of pfSense + Copyright (C) 2009 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-system-advanced-notifications +##|*NAME=System: Advanced: Notifications page +##|*DESCR=Allow access to the 'System: Advanced: Notifications' page. +##|*MATCH=system_advanced_notifications.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("notices.inc"); + +// Growl +$pconfig['disable_growl'] = isset($config['notifications']['growl']['disable']); +if ($config['notifications']['growl']['password']) { + $pconfig['password'] = $config['notifications']['growl']['password']; +} +if ($config['notifications']['growl']['ipaddress']) { + $pconfig['ipaddress'] = $config['notifications']['growl']['ipaddress']; +} + +if ($config['notifications']['growl']['notification_name']) { + $pconfig['notification_name'] = $config['notifications']['growl']['notification_name']; +} else { + $pconfig['notification_name'] = "{$g['product_name']} growl alert"; +} + +if ($config['notifications']['growl']['name']) { + $pconfig['name'] = $config['notifications']['growl']['name']; +} else { + $pconfig['name'] = 'PHP-Growl'; +} + + +// SMTP +$pconfig['disable_smtp'] = isset($config['notifications']['smtp']['disable']); +if ($config['notifications']['smtp']['ipaddress']) { + $pconfig['smtpipaddress'] = $config['notifications']['smtp']['ipaddress']; +} +if ($config['notifications']['smtp']['port']) { + $pconfig['smtpport'] = $config['notifications']['smtp']['port']; +} +if (isset($config['notifications']['smtp']['ssl'])) { + $pconfig['smtpssl'] = true; +} +if (isset($config['notifications']['smtp']['tls'])) { + $pconfig['smtptls'] = true; +} +if ($config['notifications']['smtp']['notifyemailaddress']) { + $pconfig['smtpnotifyemailaddress'] = $config['notifications']['smtp']['notifyemailaddress']; +} +if ($config['notifications']['smtp']['username']) { + $pconfig['smtpusername'] = $config['notifications']['smtp']['username']; +} +if ($config['notifications']['smtp']['password']) { + $pconfig['smtppassword'] = $config['notifications']['smtp']['password']; +} +if ($config['notifications']['smtp']['authentication_mechanism']) { + $pconfig['smtpauthmech'] = $config['notifications']['smtp']['authentication_mechanism']; +} +if ($config['notifications']['smtp']['fromaddress']) { + $pconfig['smtpfromaddress'] = $config['notifications']['smtp']['fromaddress']; +} + +// System Sounds +$pconfig['disablebeep'] = isset($config['system']['disablebeep']); + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + if (isset($_POST['save'])) { + + // Growl + $config['notifications']['growl']['ipaddress'] = $_POST['ip-address']; + $config['notifications']['growl']['password'] = $_POST['password']; + $config['notifications']['growl']['name'] = $_POST['registration-name']; + $config['notifications']['growl']['notification_name'] = $_POST['notification-name']; + + if ($_POST['disable_growl'] == "yes") { + $config['notifications']['growl']['disable'] = true; + } else { + unset($config['notifications']['growl']['disable']); + } + + // SMTP + $config['notifications']['smtp']['ipaddress'] = $_POST['smtpipaddress']; + $config['notifications']['smtp']['port'] = $_POST['smtpport']; + if (isset($_POST['smtpssl'])) { + $config['notifications']['smtp']['ssl'] = true; + } else { + unset($config['notifications']['smtp']['ssl']); + } + + if (isset($_POST['smtptls'])) { + $config['notifications']['smtp']['tls'] = true; + } else { + unset($config['notifications']['smtp']['tls']); + } + + $config['notifications']['smtp']['notifyemailaddress'] = $_POST['smtpnotifyemailaddress']; + $config['notifications']['smtp']['username'] = $_POST['smtpusername']; + $config['notifications']['smtp']['password'] = $_POST['smtppassword']; + $config['notifications']['smtp']['authentication_mechanism'] = $_POST['smtpauthmech']; + $config['notifications']['smtp']['fromaddress'] = $_POST['smtpfromaddress']; + + if ($_POST['disable_smtp'] == "yes") { + $config['notifications']['smtp']['disable'] = true; + } else { + unset($config['notifications']['smtp']['disable']); + } + + // System Sounds + if ($_POST['disablebeep'] == "yes") { + $config['system']['disablebeep'] = true; + } else { + unset($config['system']['disablebeep']); + } + + write_config(); + pfSenseHeader("system_advanced_notifications.php"); + return; + + } + + if (isset($_POST['test-growl'])) { + // Send test message via growl + if ($config['notifications']['growl']['ipaddress'] && + $config['notifications']['growl']['password'] = $_POST['password']) { + unlink_if_exists($g['vardb_path'] . "/growlnotices_lastmsg.txt"); + register_via_growl(); + notify_via_growl(sprintf(gettext("This is a test message from %s. It is safe to ignore this message."), $g['product_name']), true); + } + } + + if (isset($_POST['test-smtp'])) { + // Send test message via smtp + if (file_exists("/var/db/notices_lastmsg.txt")) { + unlink("/var/db/notices_lastmsg.txt"); + } + $savemsg = notify_via_smtp(sprintf(gettext("This is a test message from %s. It is safe to ignore this message."), $g['product_name']), true); + } +} + +$pgtitle = array(gettext("System"), gettext("Advanced: Notifications")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); + +$tab_array = array(); +$tab_array[] = array(gettext("Admin Access"), false, "system_advanced_admin.php"); +$tab_array[] = array(gettext("Firewall / NAT"), false, "system_advanced_firewall.php"); +$tab_array[] = array(gettext("Networking"), false, "system_advanced_network.php"); +$tab_array[] = array(gettext("Miscellaneous"), false, "system_advanced_misc.php"); +$tab_array[] = array(gettext("System Tunables"), false, "system_advanced_sysctl.php"); +$tab_array[] = array(gettext("Notifications"), true, "system_advanced_notifications.php"); +display_top_tabs($tab_array); + +?><div id="container"><?php + +require('classes/Form.class.php'); +$form = new Form; +$section = new Form_Section('Growl'); + +$section->addInput(new Form_Checkbox( + 'disable-growl', + 'Disable Growl', + 'Disable Growl Notifications', + $pconfig['disable_growl'] +))->setHelp('Check this option to disable growl notifications but preserve the '. + 'settings below.'); + +$section->addInput(new Form_Input( + 'registration-name', + 'Registration Name', + 'text', + $pconfig['name'], + ['placeholder' => 'PHP-Growl'] +))->setHelp('Enter the name to register with the Growl server.'); + +$section->addInput(new Form_Input( + 'notification-name', + 'Notification Name', + 'text', + $pconfig['notification_name'], + ['placeholder' => $g["product_name"].' growl alert'] + +))->setHelp('Enter a name for the Growl notifications'); + +$section->addInput(new Form_Input( + 'ip-address', + 'IP Address', + 'text', + $pconfig['ipaddress'] +))->setHelp('This is the IP address that you would like to send growl '. + 'notifications to.'); + +$section->addInput(new Form_Input( + 'password', + 'Password', + 'text', + $pconfig['password'] +))->setHelp('Enter the password of the remote growl notification device.'); + +$section->addInput(new Form_Input( + 'test-growl', + 'Test Growl', + 'submit', + 'Test Growl settings' +))->setHelp('A test notification will be sent even if the service is '. + 'marked as disabled.'); + +$form->add($section); +$section = new Form_Section('E-Mail'); + +$section->addInput(new Form_Checkbox( + 'disable-smtp', + 'Disable SMTP', + 'Disable SMTP Notifications', + $pconfig['disable_smtp'] +))->setHelp('Check this option to disable SMTP notifications but preserve the '. + 'settings below. Some other mechanisms, such as packages, may need these settings '. + 'in place to function.'); + +$section->addInput(new Form_Input( + 'e-mail-server', + 'E-Mail server', + 'text', + $pconfig['smtpipaddress'] +))->setHelp('This is the FQDN or IP address of the SMTP E-Mail server to '. + 'which notifications will be sent.'); + +$section->addInput(new Form_Input( + 'smtp-port-of-e-mail-server', + 'SMTP Port of E-Mail server', + 'number', + $pconfig['smtpport'] +))->setHelp('This is the port of the SMTP E-Mail server, typically 25, 587 '. + '(submission) or 465 (smtps)'); + +$group = new Form_Group('Secure SMTP Connection'); +$group->add(new Form_Checkbox( + 'enable-ssl-tls', + 'Enable SSL/TLS', + 'Enable SMTP over SSL/TLS', + isset($pconfig['smtpssl']) +)); + +$group->add(new Form_Checkbox( + 'secure-starttls', + 'Secure STARTTLS', + 'Enable STARTTLS', + isset($pconfig['smtptls']) +)); + +$section->add($group); + +$section->addInput(new Form_Input( + 'from-e-mail-address', + 'From e-mail address', + 'text', + $pconfig['smtpfromaddress'] +))->setHelp('This is the e-mail address that will appear in the from field.'); + +$section->addInput(new Form_Input( + 'notification-e-mail-address', + 'Notification E-Mail address', + 'text', + $pconfig['smtpnotifyemailaddress'] +))->setHelp('Enter the e-mail address that you would like email '. + 'notifications sent to.'); + +$section->addInput(new Form_Input( + 'notification-e-mail-auth-username-optional-', + 'Notification E-Mail auth username (optional)', + 'text', + $pconfig['smtpusername'] +))->setHelp('Enter the e-mail address username for SMTP authentication.'); + +$section->addInput(new Form_Input( + 'notification-e-mail-auth-password', + 'Notification E-Mail auth password', + 'password', + $pconfig['smtppassword'] +))->setHelp('Enter the e-mail address password for SMTP authentication.'); + +$section->addInput(new Form_Input( + 'test-smtp', + 'Test SMTP', + 'submit', + 'Test SMTP settings' +))->setHelp('A test notification will be sent even if the service is '. + 'marked as disabled.'); + +$section->addInput(new Form_Checkbox( + 'startup-shutdown-sound', + 'Startup/Shutdown Sound', + 'Disable the startup/shutdown beep', + $pconfig['disablebeep'] +))->setHelp('When this is checked, startup and shutdown sounds will no longer '. + 'play.'); + +$form->add($section); +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_advanced_sysctl.php b/src/usr/local/www/system_advanced_sysctl.php new file mode 100644 index 0000000..9e7da14 --- /dev/null +++ b/src/usr/local/www/system_advanced_sysctl.php @@ -0,0 +1,238 @@ +<?php +/* $Id$ */ +/* + system_advanced_sysctl.php + part of pfSense + Copyright (C) 2005-2007 Scott Ullrich + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-system-advanced-sysctl +##|*NAME=System: Advanced: Tunables page +##|*DESCR=Allow access to the 'System: Advanced: Tunables' page. +##|*MATCH=system_advanced_sysctl.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['sysctl'])) { + $config['sysctl'] = array(); +} +if (!is_array($config['sysctl']['item'])) { + $config['sysctl']['item'] = array(); +} + +$a_tunable = &$config['sysctl']['item']; +$tunables = system_get_sysctls(); + +if (isset($_GET['id'])) { + $id = htmlspecialchars_decode($_GET['id']); +} +if (isset($_POST['id'])) { + $id = htmlspecialchars_decode($_POST['id']); +} + +$act = $_GET['act']; +if (isset($_POST['act'])) { + $act = $_POST['act']; +} + +if ($act == "edit") { + if (isset($a_tunable[$id])) { + $pconfig['tunable'] = $a_tunable[$id]['tunable']; + $pconfig['value'] = $a_tunable[$id]['value']; + $pconfig['descr'] = $a_tunable[$id]['descr']; + } else if (isset($tunables[$id])) { + $pconfig['tunable'] = $tunables[$id]['tunable']; + $pconfig['value'] = $tunables[$id]['value']; + $pconfig['descr'] = $tunables[$id]['descr']; + } +} +if ($act == "del") { + if ($a_tunable[$id]) { + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + if (!$input_errors) { + unset($a_tunable[$id]); + write_config(); + mark_subsystem_dirty('sysctl'); + pfSenseHeader("system_advanced_sysctl.php"); + exit; + } + } +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + + if ($_POST['apply']) { + $retval = 0; + system_setup_sysctl(); + $savemsg = get_std_save_message($retval); + clear_subsystem_dirty('sysctl'); + } + + if ($_POST['Submit'] == gettext("Save")) { + $tunableent = array(); + + $tunableent['tunable'] = $_POST['tunable']; + $tunableent['value'] = $_POST['value']; + $tunableent['descr'] = $_POST['descr']; + + if (isset($id) && isset($a_tunable[$id])) { + $a_tunable[$id] = $tunableent; + } else { + $a_tunable[] = $tunableent; + } + + mark_subsystem_dirty('sysctl'); + write_config(); + pfSenseHeader("system_advanced_sysctl.php"); + exit; + } +} + +$pgtitle = array(gettext("System"), gettext("Advanced: System Tunables")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); +if (is_subsystem_dirty('sysctl') && ($act != "edit" )) + print_info_box_np(gettext("The firewall tunables have changed. You must apply the configuration to take affect.")); + +$tab_array = array(); +$tab_array[] = array(gettext("Admin Access"), false, "system_advanced_admin.php"); +$tab_array[] = array(gettext("Firewall / NAT"), false, "system_advanced_firewall.php"); +$tab_array[] = array(gettext("Networking"), false, "system_advanced_network.php"); +$tab_array[] = array(gettext("Miscellaneous"), false, "system_advanced_misc.php"); +$tab_array[] = array(gettext("System Tunables"), true, "system_advanced_sysctl.php"); +$tab_array[] = array(gettext("Notifications"), false, "system_advanced_notifications.php"); +display_top_tabs($tab_array); + +if ($act != "edit" ): ?> +<div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title"><?=gettext('System Tunables'); ?></h2> + </div> + <div class="panel-body"> + <div class="form-group"> + <table class="table table-responsive table-hover table-condensed"> + <caption><strong><?=gettext('NOTE: '); ?></strong><?=gettext('The options on this page are intended for use by advanced users only.'); ?></caption> + <thead> + <tr> + <th class="col-sm-3"><?=gettext("Tunable Name"); ?></th> + <th><?=gettext("Description"); ?></th> + <th class="col-sm-1"><?=gettext("Value"); ?></th> + <th><a class="btn btn-xs btn-primary" href="system_advanced_sysctl.php?act=edit"><?=gettext('New'); ?></a></th> + </tr> + </thead> + <?php foreach ($tunables as $i => $tunable): + if (!isset($tunable['modified'])) + $i = $tunable['tunable']; ?> + <tr> + <td><?=$tunable['tunable']; ?></td> + <td><?=$tunable['descr']; ?></td> + <td><?=$tunable['value']; ?> + <?php if($tunable['value'] == "default") + echo "(" . get_default_sysctl_value($tunable['tunable']) . ")"; ?> + </td> + <td> + <a class="btn btn-xs btn-primary" href="system_advanced_sysctl.php?act=edit&id=<?=$i;?>"><?=gettext('Edit'); ?></a> + <?php if (isset($tunable['modified'])): ?> + <a class="btn btn-xs btn-danger" href="system_advanced_sysctl.php?act=del&id=<?=$i;?>"><?=gettext('Delete/Reset'); ?></a> + <?php endif; ?> + </td> + </tr> + <?php + endforeach; + unset($tunables); + ?> + </table> + </div> + </div> +</div> + +<?php else: + require('classes/Form.class.php'); + $form = new Form; + $section = new Form_Section('Edit Tunable'); + + $section->addInput(new Form_Input( + 'tunable', + 'Tunable', + 'text', + $pconfig['tunable'] + ))->setWidth(4); + + $section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] + ))->setWidth(4); + + $section->addInput(new Form_Input( + 'value', + 'Value', + 'text', + $pconfig['value'] + ))->setWidth(4); + + if (isset($id) && $a_tunable[$id]) { + $form->addGlobal(new Form_Input( + 'id', + 'id', + 'hidden', + $id + )); + } + + $form->add($section); + + print $form; + +endif; + +include("fend.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_authservers.php b/src/usr/local/www/system_authservers.php new file mode 100644 index 0000000..52e9715 --- /dev/null +++ b/src/usr/local/www/system_authservers.php @@ -0,0 +1,657 @@ +<?php +/* + system_authservers.php + + Copyright (C) 2008 Shrew Soft Inc. + Copyright (C) 2010 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: auth +*/ + +##|+PRIV +##|*IDENT=page-system-authservers +##|*NAME=System: Authentication Servers +##|*DESCR=Allow access to the 'System: Authentication Servers' page. +##|*MATCH=system_authservers.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("auth.inc"); + +$pgtitle = array(gettext("System"), gettext("Authentication Servers")); +$shortcut_section = "authentication"; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (!is_array($config['system']['authserver'])) { + $config['system']['authserver'] = array(); +} + +$a_servers = auth_get_authserver_list(); +foreach ($a_servers as $servers) { + $a_server[] = $servers; +} + +if (!is_array($config['ca'])) { + $config['ca'] = array(); +} +$a_ca =& $config['ca']; + +$act = $_GET['act']; +if ($_POST['act']) { + $act = $_POST['act']; +} + +if ($act == "del") { + + if (!$a_server[$_GET['id']]) { + pfSenseHeader("system_authservers.php"); + exit; + } + + /* Remove server from main list. */ + $serverdeleted = $a_server[$_GET['id']]['name']; + foreach ($config['system']['authserver'] as $k => $as) { + if ($config['system']['authserver'][$k]['name'] == $serverdeleted) { + unset($config['system']['authserver'][$k]); + } + } + + /* Remove server from temp list used later on this page. */ + unset($a_server[$_GET['id']]); + + $savemsg = gettext("Authentication Server") . " " . htmlspecialchars($serverdeleted) . " " . gettext("deleted") . "<br />"; + write_config($savemsg); +} + +if ($act == "edit") { + if (isset($id) && $a_server[$id]) { + + $pconfig['type'] = $a_server[$id]['type']; + $pconfig['name'] = $a_server[$id]['name']; + + if ($pconfig['type'] == "ldap") { + $pconfig['ldap_caref'] = $a_server[$id]['ldap_caref']; + $pconfig['ldap_host'] = $a_server[$id]['host']; + $pconfig['ldap_port'] = $a_server[$id]['ldap_port']; + $pconfig['ldap_urltype'] = $a_server[$id]['ldap_urltype']; + $pconfig['ldap_protver'] = $a_server[$id]['ldap_protver']; + $pconfig['ldap_scope'] = $a_server[$id]['ldap_scope']; + $pconfig['ldap_basedn'] = $a_server[$id]['ldap_basedn']; + $pconfig['ldap_authcn'] = $a_server[$id]['ldap_authcn']; + $pconfig['ldap_extended_enabled'] = $a_server[$id]['ldap_extended_enabled']; + $pconfig['ldap_extended_query'] = $a_server[$id]['ldap_extended_query']; + $pconfig['ldap_binddn'] = $a_server[$id]['ldap_binddn']; + $pconfig['ldap_bindpw'] = $a_server[$id]['ldap_bindpw']; + $pconfig['ldap_attr_user'] = $a_server[$id]['ldap_attr_user']; + $pconfig['ldap_attr_group'] = $a_server[$id]['ldap_attr_group']; + $pconfig['ldap_attr_member'] = $a_server[$id]['ldap_attr_member']; + $pconfig['ldap_utf8'] = isset($a_server[$id]['ldap_utf8']); + $pconfig['ldap_nostrip_at'] = isset($a_server[$id]['ldap_nostrip_at']); + + if (!$pconfig['ldap_binddn'] || !$pconfig['ldap_bindpw']) { + $pconfig['ldap_anon'] = true; + } + } + + if ($pconfig['type'] == "radius") { + $pconfig['radius_host'] = $a_server[$id]['host']; + $pconfig['radius_auth_port'] = $a_server[$id]['radius_auth_port']; + $pconfig['radius_acct_port'] = $a_server[$id]['radius_acct_port']; + $pconfig['radius_secret'] = $a_server[$id]['radius_secret']; + $pconfig['radius_timeout'] = $a_server[$id]['radius_timeout']; + + if ($pconfig['radius_auth_port'] && + $pconfig['radius_acct_port']) { + $pconfig['radius_srvcs'] = "both"; + } + + if ($pconfig['radius_auth_port'] && + !$pconfig['radius_acct_port']) { + $pconfig['radius_srvcs'] = "auth"; + $pconfig['radius_acct_port'] = 1813; + } + + if (!$pconfig['radius_auth_port'] && + $pconfig['radius_acct_port']) { + $pconfig['radius_srvcs'] = "acct"; + $pconfig['radius_auth_port'] = 1812; + } + + } + } +} + +if ($act == "new") { + $pconfig['ldap_protver'] = 3; + $pconfig['ldap_anon'] = true; + $pconfig['radius_srvcs'] = "both"; + $pconfig['radius_auth_port'] = "1812"; + $pconfig['radius_acct_port'] = "1813"; +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + + if ($pconfig['type'] == "ldap") { + $reqdfields = explode(" ", + "name type ldap_host ldap_port " . + "ldap_urltype ldap_protver ldap_scope " . + "ldap_attr_user ldap_attr_group ldap_attr_member ldapauthcontainers"); + $reqdfieldsn = array( + gettext("Descriptive name"), + gettext("Type"), + gettext("Hostname or IP"), + gettext("Port value"), + gettext("Transport"), + gettext("Protocol version"), + gettext("Search level"), + gettext("User naming Attribute"), + gettext("Group naming Attribute"), + gettext("Group member attribute"), + gettext("Authentication container")); + + if (!$pconfig['ldap_anon']) { + $reqdfields[] = "ldap_binddn"; + $reqdfields[] = "ldap_bindpw"; + $reqdfieldsn[] = gettext("Bind user DN"); + $reqdfieldsn[] = gettext("Bind Password"); + } + } + + if ($pconfig['type'] == "radius") { + $reqdfields = explode(" ", "name type radius_host radius_srvcs"); + $reqdfieldsn = array( + gettext("Descriptive name"), + gettext("Type"), + gettext("Hostname or IP"), + gettext("Services")); + + if ($pconfig['radisu_srvcs'] == "both" || + $pconfig['radisu_srvcs'] == "auth") { + $reqdfields[] = "radius_auth_port"; + $reqdfieldsn[] = gettext("Authentication port value"); + } + + if ($pconfig['radisu_srvcs'] == "both" || + $pconfig['radisu_srvcs'] == "acct") { + $reqdfields[] = "radius_acct_port"; + $reqdfieldsn[] = gettext("Accounting port value"); + } + + if (!isset($id)) { + $reqdfields[] = "radius_secret"; + $reqdfieldsn[] = gettext("Shared Secret"); + } + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (preg_match("/[^a-zA-Z0-9\.\-_]/", $_POST['host'])) { + $input_errors[] = gettext("The host name contains invalid characters."); + } + + if (auth_get_authserver($pconfig['name']) && !isset($id)) { + $input_errors[] = gettext("An authentication server with the same name already exists."); + } + + if (($pconfig['type'] == "radius") && isset($_POST['radius_timeout']) && !empty($_POST['radius_timeout']) && (!is_numeric($_POST['radius_timeout']) || (is_numeric($_POST['radius_timeout']) && ($_POST['radius_timeout'] <= 0)))) { + $input_errors[] = gettext("RADIUS Timeout value must be numeric and positive."); + } + + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + + if (!$input_errors) { + $server = array(); + $server['refid'] = uniqid(); + if (isset($id) && $a_server[$id]) { + $server = $a_server[$id]; + } + + $server['type'] = $pconfig['type']; + $server['name'] = $pconfig['name']; + + if ($server['type'] == "ldap") { + + if (!empty($pconfig['ldap_caref'])) { + $server['ldap_caref'] = $pconfig['ldap_caref']; + } + $server['host'] = $pconfig['ldap_host']; + $server['ldap_port'] = $pconfig['ldap_port']; + $server['ldap_urltype'] = $pconfig['ldap_urltype']; + $server['ldap_protver'] = $pconfig['ldap_protver']; + $server['ldap_scope'] = $pconfig['ldap_scope']; + $server['ldap_basedn'] = $pconfig['ldap_basedn']; + $server['ldap_authcn'] = $pconfig['ldapauthcontainers']; + $server['ldap_extended_enabled'] = $pconfig['ldap_extended_enabled']; + $server['ldap_extended_query'] = $pconfig['ldap_extended_query']; + $server['ldap_attr_user'] = $pconfig['ldap_attr_user']; + $server['ldap_attr_group'] = $pconfig['ldap_attr_group']; + $server['ldap_attr_member'] = $pconfig['ldap_attr_member']; + if ($pconfig['ldap_utf8'] == "yes") { + $server['ldap_utf8'] = true; + } else { + unset($server['ldap_utf8']); + } + if ($pconfig['ldap_nostrip_at'] == "yes") { + $server['ldap_nostrip_at'] = true; + } else { + unset($server['ldap_nostrip_at']); + } + + + if (!$pconfig['ldap_anon']) { + $server['ldap_binddn'] = $pconfig['ldap_binddn']; + $server['ldap_bindpw'] = $pconfig['ldap_bindpw']; + } else { + unset($server['ldap_binddn']); + unset($server['ldap_bindpw']); + } + } + + if ($server['type'] == "radius") { + + $server['host'] = $pconfig['radius_host']; + + if ($pconfig['radius_secret']) { + $server['radius_secret'] = $pconfig['radius_secret']; + } + + if ($pconfig['radius_timeout']) { + $server['radius_timeout'] = $pconfig['radius_timeout']; + } else { + $server['radius_timeout'] = 5; + } + + if ($pconfig['radius_srvcs'] == "both") { + $server['radius_auth_port'] = $pconfig['radius_auth_port']; + $server['radius_acct_port'] = $pconfig['radius_acct_port']; + } + + if ($pconfig['radius_srvcs'] == "auth") { + $server['radius_auth_port'] = $pconfig['radius_auth_port']; + unset($server['radius_acct_port']); + } + + if ($pconfig['radius_srvcs'] == "acct") { + $server['radius_acct_port'] = $pconfig['radius_acct_port']; + unset($server['radius_auth_port']); + } + } + + if (isset($id) && $config['system']['authserver'][$id]) { + $config['system']['authserver'][$id] = $server; + } else { + $config['system']['authserver'][] = $server; + } + + write_config(); + + pfSenseHeader("system_authservers.php"); + } +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); + +$tab_array = array(); +$tab_array[] = array(gettext("Users"), false, "system_usermanager.php"); +$tab_array[] = array(gettext("Groups"), false, "system_groupmanager.php"); +$tab_array[] = array(gettext("Settings"), false, "system_usermanager_settings.php"); +$tab_array[] = array(gettext("Servers"), true, "system_authservers.php"); +display_top_tabs($tab_array); + +if (!($act == "new" || $act == "edit" || $input_errors)) +{ + ?> + <div class="table-responsive"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th><?=gettext("Server Name")?></th> + <th><?=gettext("Type")?></th> + <th><?=gettext("Host Name")?></th> + <th></th> + </tr> + </thead> + <tbody> + <?php foreach($a_server as $i => $server): ?> + <tr> + <td><?=htmlspecialchars($server['name'])?></td> + <td><?=htmlspecialchars($auth_server_types[$server['type']])?></td> + <td><?=htmlspecialchars($server['host'])?></td> + <td> + <?php if ($i < (count($a_server) - 1)): ?> + <a href="system_authservers.php?act=edit&id=<?=$i?>" class="btn btn-xs btn-primary">edit</a> + <a href="system_authservers.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger">delete</a> + <?php endif?> + </td> + </tr> + <?php endforeach; ?> + </tbody> + </table> + </div> + + <nav class="action-buttons"> + <a href="?act=new" class="btn btn-success">add new</a> + </nav> +<?php + include("foot.inc"); + exit; +} + +require('classes/Form.class.php'); +$form = new Form; +$form->setAction('system_authservers.php?act=edit'); +$form->addGlobal(new Form_Input( + 'userid', + null, + 'hidden', + $id +)); + +$section = new Form_Section('Server settings'); + +$section->addInput($input = new Form_Input( + 'name', + 'Descriptive name', + 'text', + $pconfig['name'] +)); + +if ($act == 'edit') + $input->setReadonly(); + +$section->addInput($input = new Form_Select( + 'type', + 'Type', + $pconfig['type'], + $auth_server_types +))->toggles(); + +if ($act == 'edit') + $input->setDisabled(); + +$form->add($section); +$section = new Form_Section('LDAP Server Settings'); +$section->addClass('toggle-ldap collapse'); + +if (!isset($pconfig['type']) || $pconfig['type'] == 'ldap') + $section->addClass('in'); + +$section->addInput(new Form_Input( + 'ldap_host', + 'Hostname or IP address', + 'text', + $pconfig['ldap_host'] +))->setHelp('NOTE: When using SSL, this hostname MUST match the Common Name '. + '(CN) of the LDAP server"s SSL Certificate.'); + +$section->addInput(new Form_Input( + 'ldap_port', + 'Port value', + 'number', + $pconfig['ldap_port'] +)); + +$section->addInput(new Form_Select( + 'ldap_urltype', + 'Transport', + $pconfig['ldap_urltype'], + array_combine(array_keys($ldap_urltypes), array_keys($ldap_urltypes)) +)); + +if (empty($a_ca)) +{ + $section->addInput(new Form_StaticText( + 'Peer Certificate Authority', + 'No Certificate Authorities defined.<br/>Create one under <a href="system_camanager.php">System > Cert Manager</a>.' + )); +} +else +{ + $ldapCaRef = []; + foreach ($a_ca as $ca) + $ldapCaRef[ $ca['refid'] ] = $ca['descr']; + + $section->addInput(new Form_Select( + 'ldap_caref', + 'Peer Certificate Authority', + $pconfig['ldap_caref'], + $ldapCaRef + ))->setHelp('This option is used if \'SSL Encrypted\' option is choosen. '. + 'It must match with the CA in the AD otherwise problems will arise.'); +} + +$section->addInput(new Form_Select( + 'ldap_protver', + 'Protocol version', + $pconfig['ldap_protver'], + array_combine($ldap_protvers, $ldap_protvers) +)); + +$group = new Form_Group('Search scope'); +$group->add(new Form_Select( + 'ldap_scope', + 'Level', + $pconfig['ldap_scope'], + $ldap_scopes +)); +$group->add(new Form_Input( + 'ldap_basedn', + 'Base DN', + 'text', + $pconfig['ldap_basedn'] +)); +$section->add($group); + +$group = new Form_Group('Authentication containers'); +$group->add(new Form_Input( + 'ldapauthcontainers', + 'Containers', + 'text', + $pconfig['ldap_authcn'] +))->setHelp('Note: Semi-Colon separated. This will be prepended to the search '. + 'base dn above or you can specify full container path containing a dc= '. + 'component.<br/>Example: CN=Users;DC=example,DC=com or OU=Staff;OU=Freelancers'); +#FIXME +$group->add(new Form_Button( + 'Select', + 'Select a container', + '/system_usermanager_settings_ldapacpicker.php?port=389&host=192.168.1.1&scope=one&basedn=CN=pfsense&binddn=&bindpw=&urltype=TCP%20-%20Standard&proto=3&authcn=OU=Staff&cert=' +)); +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'ldap_extended_enabled', + 'Extended query', + 'Enable extended query', + $pconfig['ldap_extended_enabled'] +))->toggles('.toggle-extended'); + +$group = new Form_Group('Query'); +$group->addClass('toggle-extended collapse'); +$group->add(new Form_Input( + 'ldap_extended_query', + 'Query', + 'text', + $pconfig['ldap_extended_query'] +))->setHelp('Example: &(objectClass=inetOrgPerson)(mail=*@example.com)'); + +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'ldap_anon', + 'Bind anonymous', + 'Use anonymous binds to resolve distinguished names', + $pconfig['ldap_anon'] +))->toggles('.toggle-anon'); + +$group = new Form_Group('Bind credentials'); +$group->addClass('toggle-anon collapse'); +$group->add(new Form_Input( + 'ldap_binddn', + 'User DN:', + 'text', + $pconfig['ldap_binddn'] +)); +$group->add(new Form_Input( + 'ldap_bindpw', + 'Password', + 'text', + $pconfig['ldap_bindpw'] +)); +$section->add($group); + +if ($act == 'add') +{ + $ldap_templates = array_map($ldap_templates, function($t){ return $t['desc']; }); + + $section->addInput(new Form_Select( + 'ldap_tmpltype', + 'Initial Template', + $pconfig['ldap_template'], + $ldap_templates + )); +} + +$section->addInput(new Form_Input( + 'ldap_attr_user', + 'User naming attribute', + 'text', + $pconfig['ldap_attr_user'] +)); + +$section->addInput(new Form_Input( + 'ldap_attr_group', + 'Group naming attribute', + 'text', + $pconfig['ldap_attr_group'] +)); + +$section->addInput(new Form_Input( + 'ldap_attr_member', + 'Group member attribute', + 'text', + $pconfig['ldap_attr_member'] +)); + +$section->addInput(new Form_Checkbox( + 'ldap_utf8', + 'UTF8 Encode', + 'UTF8 encode LDAP parameters before sending them to the server.', + $pconfig['ldap_utf8'] +))->setHelp('Required to support international characters, but may not be '. + 'supported by every LDAP server.'); + +$section->addInput(new Form_Checkbox( + 'ldap_nostrip_at', + 'Username Alterations', + 'Do not strip away parts of the username after the @ symbol', + $pconfig['ldap_nostrip_at'] +))->setHelp('e.g. user@host becomes user when unchecked.'); + +$form->add($section); +$section = new Form_Section('Radius Server Settings'); +$section->addClass('toggle-radius collapse'); + +$section->addInput(new Form_Input( + 'radius_host', + 'Hostname or IP address', + 'text', + $pconfig['radius_host'] +)); + +$section->addInput(new Form_Input( + 'radius_secret', + 'Shared Secret', + 'text', + $pconfig['radius_secret'] +)); + +$section->addInput(new Form_Select( + 'radius_srvcs', + 'Services offered', + $pconfig['radius_srvcs'], + $radius_srvcs +)); + +$section->addInput(new Form_Input( + 'radius_auth_port', + 'Authentication port value', + 'number', + $pconfig['radius_secret'] +)); + +$section->addInput(new Form_Input( + 'radius_acct_port', + 'Authentication Timeout', + 'number', + $pconfig['radius_acct_port'] +)); + +$section->addInput(new Form_Input( + 'radius_timeout', + 'Authentication Timeout', + 'number', + $pconfig['radius_timeout'] +))->setHelp('This value controls how long, in seconds, that the RADIUS '. + 'server may take to respond to an authentication request. If left blank, the '. + 'default value is 5 seconds. NOTE: If you are using an interactive two-factor '. + 'authentication system, increase this timeout to account for how long it will '. + 'take the user to receive and enter a token.'); + +if (isset($id) && $a_server[$id]) +{ + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_camanager.php b/src/usr/local/www/system_camanager.php new file mode 100644 index 0000000..f2bb3e5 --- /dev/null +++ b/src/usr/local/www/system_camanager.php @@ -0,0 +1,605 @@ +<?php +/* + system_camanager.php + + Copyright (C) 2008 Shrew Soft Inc. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: certificate_manager +*/ + +##|+PRIV +##|*IDENT=page-system-camanager +##|*NAME=System: CA Manager +##|*DESCR=Allow access to the 'System: CA Manager' page. +##|*MATCH=system_camanager.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("certs.inc"); + +$ca_methods = array( + "existing" => gettext("Import an existing Certificate Authority"), + "internal" => gettext("Create an internal Certificate Authority"), + "intermediate" => gettext("Create an intermediate Certificate Authority")); + +$ca_keylens = array("512", "1024", "2048", "4096"); +$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512"); + +$pgtitle = array(gettext("System"), gettext("Certificate Authority Manager")); + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (!is_array($config['ca'])) { + $config['ca'] = array(); +} + +$a_ca =& $config['ca']; + +if (!is_array($config['cert'])) { + $config['cert'] = array(); +} + +$a_cert =& $config['cert']; + +if (!is_array($config['crl'])) { + $config['crl'] = array(); +} + +$a_crl =& $config['crl']; + +$act = $_GET['act']; +if ($_POST['act']) { + $act = $_POST['act']; +} + +if ($act == "del") { + + if (!isset($a_ca[$id])) { + pfSenseHeader("system_camanager.php"); + exit; + } + + $index = count($a_cert) - 1; + for (;$index >= 0; $index--) { + if ($a_cert[$index]['caref'] == $a_ca[$id]['refid']) { + unset($a_cert[$index]); + } + } + + $index = count($a_crl) - 1; + for (;$index >= 0; $index--) { + if ($a_crl[$index]['caref'] == $a_ca[$id]['refid']) { + unset($a_crl[$index]); + } + } + + $name = $a_ca[$id]['descr']; + unset($a_ca[$id]); + write_config(); + $savemsg = sprintf(gettext("Certificate Authority %s and its CRLs (if any) successfully deleted"), htmlspecialchars($name)) . "<br />"; + pfSenseHeader("system_camanager.php"); + exit; +} + +if ($act == "edit") { + if (!$a_ca[$id]) { + pfSenseHeader("system_camanager.php"); + exit; + } + $pconfig['descr'] = $a_ca[$id]['descr']; + $pconfig['refid'] = $a_ca[$id]['refid']; + $pconfig['cert'] = base64_decode($a_ca[$id]['crt']); + $pconfig['serial'] = $a_ca[$id]['serial']; + if (!empty($a_ca[$id]['prv'])) { + $pconfig['key'] = base64_decode($a_ca[$id]['prv']); + } +} + +if ($act == "new") { + $pconfig['method'] = $_GET['method']; + $pconfig['keylen'] = "2048"; + $pconfig['digest_alg'] = "sha256"; + $pconfig['lifetime'] = "3650"; + $pconfig['dn_commonname'] = "internal-ca"; +} + +if ($act == "exp") { + + if (!$a_ca[$id]) { + pfSenseHeader("system_camanager.php"); + exit; + } + + $exp_name = urlencode("{$a_ca[$id]['descr']}.crt"); + $exp_data = base64_decode($a_ca[$id]['crt']); + $exp_size = strlen($exp_data); + + header("Content-Type: application/octet-stream"); + header("Content-Disposition: attachment; filename={$exp_name}"); + header("Content-Length: $exp_size"); + echo $exp_data; + exit; +} + +if ($act == "expkey") { + + if (!$a_ca[$id]) { + pfSenseHeader("system_camanager.php"); + exit; + } + + $exp_name = urlencode("{$a_ca[$id]['descr']}.key"); + $exp_data = base64_decode($a_ca[$id]['prv']); + $exp_size = strlen($exp_data); + + header("Content-Type: application/octet-stream"); + header("Content-Disposition: attachment; filename={$exp_name}"); + header("Content-Length: $exp_size"); + echo $exp_data; + exit; +} + +if ($_POST) { + + unset($input_errors); + $input_errors = array(); + $pconfig = $_POST; + + /* input validation */ + if ($pconfig['method'] == "existing") { + $reqdfields = explode(" ", "descr cert"); + $reqdfieldsn = array( + gettext("Descriptive name"), + gettext("Certificate data")); + if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE"))) { + $input_errors[] = gettext("This certificate does not appear to be valid."); + } + if ($_POST['key'] && strstr($_POST['key'], "ENCRYPTED")) { + $input_errors[] = gettext("Encrypted private keys are not yet supported."); + } + } + if ($pconfig['method'] == "internal") { + $reqdfields = explode(" ", + "descr keylen lifetime dn_country dn_state dn_city ". + "dn_organization dn_email dn_commonname"); + $reqdfieldsn = array( + gettext("Descriptive name"), + gettext("Key length"), + gettext("Lifetime"), + gettext("Distinguished name Country Code"), + gettext("Distinguished name State or Province"), + gettext("Distinguished name City"), + gettext("Distinguished name Organization"), + gettext("Distinguished name Email Address"), + gettext("Distinguished name Common Name")); + } + if ($pconfig['method'] == "intermediate") { + $reqdfields = explode(" ", + "descr caref keylen lifetime dn_country dn_state dn_city ". + "dn_organization dn_email dn_commonname"); + $reqdfieldsn = array( + gettext("Descriptive name"), + gettext("Signing Certificate Authority"), + gettext("Key length"), + gettext("Lifetime"), + gettext("Distinguished name Country Code"), + gettext("Distinguished name State or Province"), + gettext("Distinguished name City"), + gettext("Distinguished name Organization"), + gettext("Distinguished name Email Address"), + gettext("Distinguished name Common Name")); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + if ($pconfig['method'] != "existing") { + /* Make sure we do not have invalid characters in the fields for the certificate */ + if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) { + array_push($input_errors, "The field 'Descriptive Name' contains invalid characters."); + } + + for ($i = 0; $i < count($reqdfields); $i++) { + if ($reqdfields[$i] == 'dn_email') { + if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST["dn_email"])) { + array_push($input_errors, "The field 'Distinguished name Email Address' contains invalid characters."); + } + } else if ($reqdfields[$i] == 'dn_commonname') { + if (preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST["dn_commonname"])) { + array_push($input_errors, "The field 'Distinguished name Common Name' contains invalid characters."); + } + } else if (($reqdfields[$i] != "descr") && preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\.\"\']/", $_POST["$reqdfields[$i]"])) { + array_push($input_errors, "The field '" . $reqdfieldsn[$i] . "' contains invalid characters."); + } + } + if (!in_array($_POST["keylen"], $ca_keylens)) { + array_push($input_errors, gettext("Please select a valid Key Length.")); + } + if (!in_array($_POST["digest_alg"], $openssl_digest_algs)) { + array_push($input_errors, gettext("Please select a valid Digest Algorithm.")); + } + } + + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + + /* save modifications */ + if (!$input_errors) { + + $ca = array(); + if (!isset($pconfig['refid']) || empty($pconfig['refid'])) { + $ca['refid'] = uniqid(); + } else { + $ca['refid'] = $pconfig['refid']; + } + + if (isset($id) && $a_ca[$id]) { + $ca = $a_ca[$id]; + } + + $ca['descr'] = $pconfig['descr']; + + if ($act == "edit") { + $ca['descr'] = $pconfig['descr']; + $ca['refid'] = $pconfig['refid']; + $ca['serial'] = $pconfig['serial']; + $ca['crt'] = base64_encode($pconfig['cert']); + if (!empty($pconfig['key'])) { + $ca['prv'] = base64_encode($pconfig['key']); + } + } else { + $old_err_level = error_reporting(0); /* otherwise openssl_ functions throw warnings directly to a page screwing menu tab */ + if ($pconfig['method'] == "existing") { + ca_import($ca, $pconfig['cert'], $pconfig['key'], $pconfig['serial']); + } else if ($pconfig['method'] == "internal") { + $dn = array( + 'countryName' => $pconfig['dn_country'], + 'stateOrProvinceName' => $pconfig['dn_state'], + 'localityName' => $pconfig['dn_city'], + 'organizationName' => $pconfig['dn_organization'], + 'emailAddress' => $pconfig['dn_email'], + 'commonName' => $pconfig['dn_commonname']); + if (!ca_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['digest_alg'])) { + while ($ssl_err = openssl_error_string()) { + $input_errors = array(); + array_push($input_errors, "openssl library returns: " . $ssl_err); + } + } + } + else if ($pconfig['method'] == "intermediate") { + $dn = array( + 'countryName' => $pconfig['dn_country'], + 'stateOrProvinceName' => $pconfig['dn_state'], + 'localityName' => $pconfig['dn_city'], + 'organizationName' => $pconfig['dn_organization'], + 'emailAddress' => $pconfig['dn_email'], + 'commonName' => $pconfig['dn_commonname']); + if (!ca_inter_create($ca, $pconfig['keylen'], $pconfig['lifetime'], $dn, $pconfig['caref'], $pconfig['digest_alg'])) { + while ($ssl_err = openssl_error_string()) { + $input_errors = array(); + array_push($input_errors, "openssl library returns: " . $ssl_err); + } + } + } + error_reporting($old_err_level); + } + + if (isset($id) && $a_ca[$id]) { + $a_ca[$id] = $ca; + } else { + $a_ca[] = $ca; + } + + if (!$input_errors) { + write_config(); + } + +// pfSenseHeader("system_camanager.php"); + } +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); + +// Load valid country codes +$dn_cc = array(); +if (file_exists("/etc/ca_countries")){ + $dn_cc_file=file("/etc/ca_countries"); + foreach($dn_cc_file as $line) + if (preg_match('/^(\S*)\s(.*)$/', $line, $matches)) + array_push($dn_cc, $matches[1]); +} + +$tab_array = array(); +$tab_array[] = array(gettext("CAs"), true, "system_camanager.php"); +$tab_array[] = array(gettext("Certificates"), false, "system_certmanager.php"); +$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php"); +display_top_tabs($tab_array); + +if (!($act == "new" || $act == "edit" || $act == gettext("Save") || $input_errors)) +{ +?> +<div class="table-responsive"> +<table class="table table-striped table-hover"> + <thead> + <tr> + <th><?=gettext("Name")?></th> + <th><?=gettext("Internal")?></th> + <th><?=gettext("Issuer")?></th> + <th><?=gettext("Certificates")?></th> + <th><?=gettext("Distinguished Name")?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php +foreach ($a_ca as $i => $ca): + $name = htmlspecialchars($ca['descr']); + $subj = cert_get_subject($ca['crt']); + $issuer = cert_get_issuer($ca['crt']); + list($startdate, $enddate) = cert_get_dates($ca['crt']); + if ($subj == $issuer) + $issuer_name = gettext("self-signed"); + else + $issuer_name = gettext("external"); + $subj = htmlspecialchars($subj); + $issuer = htmlspecialchars($issuer); + $certcount = 0; + + $issuer_ca = lookup_ca($ca['caref']); + if ($issuer_ca) + $issuer_name = $issuer_ca['descr']; + + // TODO : Need gray certificate icon + $internal = (!!$ca['prv']); + + foreach ($a_cert as $cert) + if ($cert['caref'] == $ca['refid']) + $certcount++; + + foreach ($a_ca as $cert) + if ($cert['caref'] == $ca['refid']) + $certcount++; +?> + <tr> + <td><?=$name?></td> + <td><?=$internal?></td> + <td><i><?=$issuer_name?></i></td> + <td><?=$certcount?></td> + <td> + <?=$subj?> + <br /> + <small> + <?=gettext("Valid From")?>: <b><?=$startdate ?></b>, <?=gettext("Valid Until")?>: <b><?=$enddate ?></b> + </small> + </td> + <td> + <a href="system_camanager.php?act=edit&id=<?=$i?>" class="btn btn-xs btn-primary"> + <?=gettext("edit")?> + </a> + <a href="system_camanager.php?act=exp&id=<?=$i?>" class="btn btn-xs btn-default"> + <?=gettext("export cert")?> + </a> + <?php if ($ca['prv']): ?> + <a href="system_camanager.php?act=expkey&id=<?=$i?>" class="btn btn-xs btn-default"> + <?=gettext("export private key")?> + </a> + <?php endif?> + <a href="system_camanager.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger"> + <?=gettext("delete")?> + </a> + </td> + </tr> +<?php endforeach; ?> + </tbody> +</table> + +<nav class="action-buttons"> + <a href="?act=new" class="btn btn-success">add new</a> +</nav> +<? + include("foot.inc"); + exit; +} + +require('classes/Form.class.php'); +$form = new Form; +$form->setAction('system_camanager.php?act=edit'); +if (isset($id) && $a_ca[$id]) +{ + $form->addGlobal(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +if ($act == "edit") +{ + $form->addGlobal(new Form_Input( + 'refid', + null, + 'hidden', + $pconfig['refid'] + )); +} + +$section = new Form_Section('Create / edit CA'); + +$section->addInput(new Form_Input( + 'descr', + 'Descriptive name', + 'text', + $pconfig['descr'] +)); + +if (!isset($id) || $act == "edit") +{ + $section->addInput(new Form_Select( + 'method', + 'Method', + $pconfig['method'], + $ca_methods + ))->toggles(); +} + +$form->add($section); + +$section = new Form_Section('Existing Certificate Authority'); +$section->addClass('toggle-existing collapse'); + +$section->addInput(new Form_Textarea( + 'cert', + 'Certificate data', + $pconfig['cert'] +))->setHelp('Paste a certificate in X.509 PEM format here.'); + +$section->addInput(new Form_Textarea( + 'key', + 'Certificate Private Key (optional)', + $pconfig['key'] +))->setHelp('Paste the private key for the above certificate here. This is '. + 'optional in most cases, but required if you need to generate a '. + 'Certificate Revocation List (CRL).'); + +$section->addInput(new Form_Input( + 'serial', + 'Serial for next certificate', + 'number', + $pconfig['serial'] +))->setHelp('Enter a decimal number to be used as the serial number for the next '. + 'certificate to be created using this CA.'); + +$form->add($section); + +$section = new Form_Section('Internal Certificate Authority'); +$section->addClass('toggle-internal', 'toggle-intermediate', 'collapse'); + +$allCas = array(); +foreach ($a_ca as $ca) +{ + if (!$ca['prv']) + continue; + + $allCas[ $ca['refid'] ] = $ca['descr']; +} + +$group = new Form_Group('Signing Certificate Authority'); +$group->addClass('toggle-intermediate'); +$group->add(new Form_Select( + 'caref', + null, + $pconfig['caref'], + $allCas +)); +$section->add($group); + +$section->addInput(new Form_Select( + 'keylen', + 'Key length (bits)', + $pconfig['keylen'], + array_combine($ca_keylens, $ca_keylens) +)); + +$section->addInput(new Form_Select( + 'digest_alg', + 'Digest Algorithm', + $pconfig['digest_alg'], + array_combine($openssl_digest_algs, $openssl_digest_algs) +))->setHelp('NOTE: It is recommended to use an algorithm stronger than SHA1 '. + 'when possible.'); + +$section->addInput(new Form_Input( + 'lifetime', + 'Lifetime (days)', + 'number', + $pconfig['lifetime'] +)); + +$section->addInput(new Form_Select( + 'dn_country', + 'Country Code', + $pconfig['dn_country'], + $dn_cc +)); + +$section->addInput(new Form_Input( + 'dn_state', + 'State or Province', + 'text', + $pconfig['dn_state'], + ['placeholder' => 'e.g. Texas'] +)); + +$section->addInput(new Form_Input( + 'dn_city', + 'City', + 'text', + $pconfig['dn_city'], + ['placeholder' => 'e.g. Austin'] +)); + +$section->addInput(new Form_Input( + 'dn_organization', + 'Organization', + 'text', + $pconfig['dn_organization'], + ['placeholder' => 'e.g. My Company Inc.'] +)); + +$section->addInput(new Form_Input( + 'dn_email', + 'Email Address', + 'email', + $pconfig['dn_email'], + ['placeholder' => 'e.g. admin@mycompany.com'] +)); + +$section->addInput(new Form_Input( + 'dn_commonname', + 'Common Name', + 'text', + $pconfig['dn_commonname'], + ['placeholder' => 'e.g. internal-ca'] +)); + +$form->add($section); + +print $form; + +include('foot.inc');
\ No newline at end of file diff --git a/src/usr/local/www/system_certmanager.php b/src/usr/local/www/system_certmanager.php new file mode 100644 index 0000000..b26d857 --- /dev/null +++ b/src/usr/local/www/system_certmanager.php @@ -0,0 +1,992 @@ +<?php +/* + system_certmanager.php + + Copyright (C) 2008 Shrew Soft Inc. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: certificate_manager +*/ + +##|+PRIV +##|*IDENT=page-system-certmanager +##|*NAME=System: Certificate Manager +##|*DESCR=Allow access to the 'System: Certificate Manager' page. +##|*MATCH=system_certmanager.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("certs.inc"); + +$cert_methods = array( + "import" => gettext("Import an existing Certificate"), + "internal" => gettext("Create an internal Certificate"), + "external" => gettext("Create a Certificate Signing Request"), +); + +$cert_keylens = array("512", "1024", "2048", "4096"); +$cert_types = array( + "ca" => "Certificate Authority", + "server" => "Server Certificate", + "user" => "User Certificate"); + +$altname_types = array("DNS", "IP", "email", "URI"); +$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512"); + +$pgtitle = array(gettext("System"), gettext("Certificate Manager")); + +if (is_numericint($_GET['userid'])) { + $userid = $_GET['userid']; +} +if (isset($_POST['userid']) && is_numericint($_POST['userid'])) { + $userid = $_POST['userid']; +} + +if (isset($userid)) { + $cert_methods["existing"] = gettext("Choose an existing certificate"); + if (!is_array($config['system']['user'])) { + $config['system']['user'] = array(); + } + $a_user =& $config['system']['user']; +} + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (!is_array($config['ca'])) { + $config['ca'] = array(); +} + +$a_ca =& $config['ca']; + +if (!is_array($config['cert'])) { + $config['cert'] = array(); +} + +$a_cert =& $config['cert']; + +$internal_ca_count = 0; +foreach ($a_ca as $ca) { + if ($ca['prv']) { + $internal_ca_count++; + } +} + +$act = $_GET['act']; + +if ($_POST['act']) { + $act = $_POST['act']; +} + +if ($act == "del") { + + if (!isset($a_cert[$id])) { + pfSenseHeader("system_certmanager.php"); + exit; + } + + unset($a_cert[$id]); + write_config(); + $savemsg = sprintf(gettext("Certificate %s successfully deleted"), htmlspecialchars($a_cert[$id]['descr'])) . "<br />"; + pfSenseHeader("system_certmanager.php"); + exit; +} + + +if ($act == "new") { + $pconfig['method'] = $_GET['method']; + $pconfig['keylen'] = "2048"; + $pconfig['digest_alg'] = "sha256"; + $pconfig['csr_keylen'] = "2048"; + $pconfig['csr_digest_alg'] = "sha256"; + $pconfig['type'] = "user"; + $pconfig['lifetime'] = "3650"; +} + +if ($act == "exp") { + + if (!$a_cert[$id]) { + pfSenseHeader("system_certmanager.php"); + exit; + } + + $exp_name = urlencode("{$a_cert[$id]['descr']}.crt"); + $exp_data = base64_decode($a_cert[$id]['crt']); + $exp_size = strlen($exp_data); + + header("Content-Type: application/octet-stream"); + header("Content-Disposition: attachment; filename={$exp_name}"); + header("Content-Length: $exp_size"); + echo $exp_data; + exit; +} + +if ($act == "key") { + + if (!$a_cert[$id]) { + pfSenseHeader("system_certmanager.php"); + exit; + } + + $exp_name = urlencode("{$a_cert[$id]['descr']}.key"); + $exp_data = base64_decode($a_cert[$id]['prv']); + $exp_size = strlen($exp_data); + + header("Content-Type: application/octet-stream"); + header("Content-Disposition: attachment; filename={$exp_name}"); + header("Content-Length: $exp_size"); + echo $exp_data; + exit; +} + +if ($act == "p12") { + if (!$a_cert[$id]) { + pfSenseHeader("system_certmanager.php"); + exit; + } + + $exp_name = urlencode("{$a_cert[$id]['descr']}.p12"); + $args = array(); + $args['friendly_name'] = $a_cert[$id]['descr']; + + $ca = lookup_ca($a_cert[$id]['caref']); + if ($ca) { + $args['extracerts'] = openssl_x509_read(base64_decode($ca['crt'])); + } + + $res_crt = openssl_x509_read(base64_decode($a_cert[$id]['crt'])); + $res_key = openssl_pkey_get_private(array(0 => base64_decode($a_cert[$id]['prv']) , 1 => "")); + + $exp_data = ""; + openssl_pkcs12_export($res_crt, $exp_data, $res_key, null, $args); + $exp_size = strlen($exp_data); + + header("Content-Type: application/octet-stream"); + header("Content-Disposition: attachment; filename={$exp_name}"); + header("Content-Length: $exp_size"); + echo $exp_data; + exit; +} + +if ($act == "csr") { + + if (!$a_cert[$id]) { + pfSenseHeader("system_certmanager.php"); + exit; + } + + $pconfig['descr'] = $a_cert[$id]['descr']; + $pconfig['csr'] = base64_decode($a_cert[$id]['csr']); +} + +if ($_POST) { + if ($_POST['save'] == gettext("Save")) { + $input_errors = array(); + $pconfig = $_POST; + + /* input validation */ + if ($pconfig['method'] == "import") { + $reqdfields = explode(" ", + "descr cert key"); + $reqdfieldsn = array( + gettext("Descriptive name"), + gettext("Certificate data"), + gettext("Key data")); + if ($_POST['cert'] && (!strstr($_POST['cert'], "BEGIN CERTIFICATE") || !strstr($_POST['cert'], "END CERTIFICATE"))) { + $input_errors[] = gettext("This certificate does not appear to be valid."); + } + } + + if ($pconfig['method'] == "internal") { + $reqdfields = explode(" ", + "descr caref keylen type lifetime dn_country dn_state dn_city ". + "dn_organization dn_email dn_commonname"); + $reqdfieldsn = array( + gettext("Descriptive name"), + gettext("Certificate authority"), + gettext("Key length"), + gettext("Certificate Type"), + gettext("Lifetime"), + gettext("Distinguished name Country Code"), + gettext("Distinguished name State or Province"), + gettext("Distinguished name City"), + gettext("Distinguished name Organization"), + gettext("Distinguished name Email Address"), + gettext("Distinguished name Common Name")); + } + + if ($pconfig['method'] == "external") { + $reqdfields = explode(" ", + "descr csr_keylen csr_dn_country csr_dn_state csr_dn_city ". + "csr_dn_organization csr_dn_email csr_dn_commonname"); + $reqdfieldsn = array( + gettext("Descriptive name"), + gettext("Key length"), + gettext("Distinguished name Country Code"), + gettext("Distinguished name State or Province"), + gettext("Distinguished name City"), + gettext("Distinguished name Organization"), + gettext("Distinguished name Email Address"), + gettext("Distinguished name Common Name")); + } + + if ($pconfig['method'] == "existing") { + $reqdfields = array("certref"); + $reqdfieldsn = array(gettext("Existing Certificate Choice")); + } + + $altnames = array(); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + if ($pconfig['method'] != "import" && $pconfig['method'] != "existing") { + /* subjectAltNames */ + foreach ($_POST['altname_value'] as $idx => $value) { + if (empty($value)) + continue; + + $altnames[$idx] = array( + 'type' => $_POST['altname_type'][$idx], + 'value' => $value + ); + } + $pconfig['altnames']['item'] = $altnames; + + /* Input validation for subjectAltNames */ + foreach ($altnames as $idx => $altname) { + switch ($altname['type']) { + case "DNS": + if (!is_hostname($altname['value'])) { + array_push($input_errors, "DNS subjectAltName values must be valid hostnames or FQDNs"); + } + break; + case "IP": + if (!is_ipaddr($altname['value'])) { + array_push($input_errors, "IP subjectAltName values must be valid IP Addresses"); + } + break; + case "email": + if (empty($altname['value'])) { + array_push($input_errors, "You must provide an e-mail address for this type of subjectAltName"); + } + if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $altname['value'])) { + array_push($input_errors, "The e-mail provided in a subjectAltName contains invalid characters."); + } + break; + case "URI": + /* Close enough? */ + if (!is_URL($altname['value'])) { + $input_errors[] = "URI subjectAltName types must be a valid URI"; + } + break; + default: + $input_errors[] = "Unrecognized subjectAltName type."; + } + } + + /* Make sure we do not have invalid characters in the fields for the certificate */ + + if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) { + array_push($input_errors, "The field 'Descriptive Name' contains invalid characters."); + } + + for ($i = 0; $i < count($reqdfields); $i++) { + if (preg_match('/email/', $reqdfields[$i])) { /* dn_email or csr_dn_name */ + if (preg_match("/[\!\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST[$reqdfields[$i]])) { + array_push($input_errors, "The field 'Distinguished name Email Address' contains invalid characters."); + } + } else if (preg_match('/commonname/', $reqdfields[$i])) { /* dn_commonname or csr_dn_commonname */ + if (preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\"\']/", $_POST[$reqdfields[$i]])) { + array_push($input_errors, "The field 'Distinguished name Common Name' contains invalid characters."); + } + } else if (($reqdfields[$i] != "descr") && preg_match("/[\!\@\#\$\%\^\(\)\~\?\>\<\&\/\\\,\.\"\']/", $_POST[$reqdfields[$i]])) { + array_push($input_errors, "The field '" . $reqdfieldsn[$i] . "' contains invalid characters."); + } + } + + if (($pconfig['method'] != "external") && isset($_POST["keylen"]) && !in_array($_POST["keylen"], $cert_keylens)) { + array_push($input_errors, gettext("Please select a valid Key Length.")); + } + if (($pconfig['method'] != "external") && !in_array($_POST["digest_alg"], $openssl_digest_algs)) { + array_push($input_errors, gettext("Please select a valid Digest Algorithm.")); + } + + if (($pconfig['method'] == "external") && isset($_POST["csr_keylen"]) && !in_array($_POST["csr_keylen"], $cert_keylens)) { + array_push($input_errors, gettext("Please select a valid Key Length.")); + } + if (($pconfig['method'] == "external") && !in_array($_POST["csr_digest_alg"], $openssl_digest_algs)) { + array_push($input_errors, gettext("Please select a valid Digest Algorithm.")); + } + } + + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + + /* save modifications */ + if (!$input_errors) { + + if ($pconfig['method'] == "existing") { + $cert = lookup_cert($pconfig['certref']); + if ($cert && $a_user) { + $a_user[$userid]['cert'][] = $cert['refid']; + } + } else { + $cert = array(); + $cert['refid'] = uniqid(); + if (isset($id) && $a_cert[$id]) { + $cert = $a_cert[$id]; + } + + $cert['descr'] = $pconfig['descr']; + + $old_err_level = error_reporting(0); /* otherwise openssl_ functions throw warnings directly to a page screwing menu tab */ + + if ($pconfig['method'] == "import") { + cert_import($cert, $pconfig['cert'], $pconfig['key']); + } + + if ($pconfig['method'] == "internal") { + $dn = array( + 'countryName' => $pconfig['dn_country'], + 'stateOrProvinceName' => $pconfig['dn_state'], + 'localityName' => $pconfig['dn_city'], + 'organizationName' => $pconfig['dn_organization'], + 'emailAddress' => $pconfig['dn_email'], + 'commonName' => $pconfig['dn_commonname']); + if (count($altnames)) { + $altnames_tmp = ""; + foreach ($altnames as $altname) { + $altnames_tmp[] = "{$altname['type']}:{$altname['value']}"; + } + $dn['subjectAltName'] = implode(",", $altnames_tmp); + } + if (!cert_create($cert, $pconfig['caref'], $pconfig['keylen'], + $pconfig['lifetime'], $dn, $pconfig['type'], $pconfig['digest_alg'])) { + while ($ssl_err = openssl_error_string()) { + $input_errors = array(); + array_push($input_errors, "openssl library returns: " . $ssl_err); + } + } + } + + if ($pconfig['method'] == "external") { + $dn = array( + 'countryName' => $pconfig['csr_dn_country'], + 'stateOrProvinceName' => $pconfig['csr_dn_state'], + 'localityName' => $pconfig['csr_dn_city'], + 'organizationName' => $pconfig['csr_dn_organization'], + 'emailAddress' => $pconfig['csr_dn_email'], + 'commonName' => $pconfig['csr_dn_commonname']); + if (count($altnames)) { + $altnames_tmp = ""; + foreach ($altnames as $altname) { + $altnames_tmp[] = "{$altname['type']}:{$altname['value']}"; + } + $dn['subjectAltName'] = implode(",", $altnames_tmp); + } + if (!csr_generate($cert, $pconfig['csr_keylen'], $dn, $pconfig['csr_digest_alg'])) { + while ($ssl_err = openssl_error_string()) { + $input_errors = array(); + array_push($input_errors, "openssl library returns: " . $ssl_err); + } + } + } + error_reporting($old_err_level); + + if (isset($id) && $a_cert[$id]) { + $a_cert[$id] = $cert; + } else { + $a_cert[] = $cert; + } + if (isset($a_user) && isset($userid)) { + $a_user[$userid]['cert'][] = $cert['refid']; + } + } + + if (!$input_errors) { + write_config(); + } + + if ($userid) { + post_redirect("system_usermanager.php", array('act' => 'edit', 'userid' => $userid)); + exit; + } + } + } + + if ($_POST['save'] == gettext("Update")) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "descr cert"); + $reqdfieldsn = array( + gettext("Descriptive name"), + gettext("Final Certificate data")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) { + array_push($input_errors, "The field 'Descriptive Name' contains invalid characters."); + } + +// old way + /* make sure this csr and certificate subjects match */ +// $subj_csr = csr_get_subject($pconfig['csr'], false); +// $subj_cert = cert_get_subject($pconfig['cert'], false); +// +// if (!isset($_POST['ignoresubjectmismatch']) && !($_POST['ignoresubjectmismatch'] == "yes")) { +// if (strcmp($subj_csr, $subj_cert)) { +// $input_errors[] = sprintf(gettext("The certificate subject '%s' does not match the signing request subject."), $subj_cert); +// $subject_mismatch = true; +// } +// } + $mod_csr = csr_get_modulus($pconfig['csr'], false); + $mod_cert = cert_get_modulus($pconfig['cert'], false); + + if (strcmp($mod_csr, $mod_cert)) { + // simply: if the moduli don't match, then the private key and public key won't match + $input_errors[] = sprintf(gettext("The certificate modulus does not match the signing request modulus."), $subj_cert); + $subject_mismatch = true; + } + + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + + /* save modifications */ + if (!$input_errors) { + + $cert = $a_cert[$id]; + + $cert['descr'] = $pconfig['descr']; + + csr_complete($cert, $pconfig['cert']); + + $a_cert[$id] = $cert; + + write_config(); + + pfSenseHeader("system_certmanager.php"); + } + } +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); + +$tab_array = array(); +$tab_array[] = array(gettext("CAs"), false, "system_camanager.php"); +$tab_array[] = array(gettext("Certificates"), true, "system_certmanager.php"); +$tab_array[] = array(gettext("Certificate Revocation"), false, "system_crlmanager.php"); +display_top_tabs($tab_array); + +// Load valid country codes +$dn_cc = array(); +if (file_exists("/etc/ca_countries")){ + $dn_cc_file=file("/etc/ca_countries"); + foreach($dn_cc_file as $line) + if (preg_match('/^(\S*)\s(.*)$/', $line, $matches)) + array_push($dn_cc, $matches[1]); +} + +if (!($act == "new" || (($_POST['save'] == gettext("Save")) && $input_errors))) +{ +?> +<div class="table-responsive"> +<table class="table table-striped table-hover"> + <thead> + <tr> + <th><?=gettext("Name")?></th> + <th><?=gettext("Issuer")?></th> + <th><?=gettext("Distinguished Name")?></th> + <th><?=gettext("In Use")?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php +foreach($a_cert as $i => $cert): + $name = htmlspecialchars($cert['descr']); + + if ($cert['crt']) { + $subj = cert_get_subject($cert['crt']); + $issuer = cert_get_issuer($cert['crt']); + $purpose = cert_get_purpose($cert['crt']); + list($startdate, $enddate) = cert_get_dates($cert['crt']); + + if ($subj==$issuer) + $caname = '<i>'. gettext("self-signed") .'</i>'; + else + $caname = '<i>'. gettext("external").'</i>'; + + $subj = htmlspecialchars($subj); + } + + if ($cert['csr']) { + $subj = htmlspecialchars(csr_get_subject($cert['csr'])); + $caname = "<em>" . gettext("external - signature pending") . "</em>"; + } + + $ca = lookup_ca($cert['caref']); + if ($ca) + $caname = $ca['descr']; +?> + <tr> + <td> + <?=$name?> + <?php if ($cert['type']): ?> + <i><?=$cert_types[$cert['type']]?></i> + <?php endif?> + <?php if (is_array($purpose)): ?> + CA: <b><?=$purpose['ca']?></b>, Server: <b><?=$purpose['server']?></b> + <?php endif?> + </td> + <td><?=$caname?></td> + <td> + <?=$subj?> + <br /> + <small> + <?=gettext("Valid From")?>: <b><?=$startdate ?></b>, <?=gettext("Valid Until")?>: <b><?=$enddate ?></b> + </small> + </td> + <td> + <?php if (is_cert_revoked($cert)): ?> + <i>Revoked </i> + <?php endif?> + <?php if (is_webgui_cert($cert['refid'])): ?> + webConfigurator + <?php endif?> + <?php if (is_user_cert($cert['refid'])): ?> + User Cert + <?php endif?> + <?php if (is_openvpn_server_cert($cert['refid'])): ?> + OpenVPN Server + <?php endif?> + <?php if (is_openvpn_client_cert($cert['refid'])): ?> + OpenVPN Client + <?php endif?> + <?php if (is_ipsec_cert($cert['refid'])): ?> + IPsec Tunnel + <?php endif?> + <?php if (is_captiveportal_cert($cert['refid'])): ?> + Captive Portal + <?php endif?> + </td> + <td> + <a href="system_certmanager.php?act=exp&id=<?=$i?>" class="btn btn-xs btn-default"> + <?=gettext("export")?> + </a> + <a href="system_certmanager.php?act=key&id=<?=$i?>" class="btn btn-xs btn-default"> + <?=gettext("export key")?> + </a> + <a href="system_certmanager.php?act=p12&id=<?=$i?>" class="btn btn-xs btn-default"> + <?=gettext("export p12")?> + </a> + <?php if (!cert_in_use($cert['refid'])): ?> + <a href="system_certmanager.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger"> + <?=gettext("delete")?> + </a> + <?php endif?> + <?php if ($cert['csr']): ?> + <a href="system_certmanager.php?act=csr&id=<?=$i?>" class="btn btn-xs btn-default"> + <?=gettext("update csr")?> + </a> + <?php endif?> + </td> + </tr> +<?php endforeach; ?> + </tbody> +</table> +</div> + +<nav class="action-buttons"> + <a href="?act=new" class="btn btn-success">add new</a> +</nav> +<? + include("foot.inc"); + exit; +} + +require('classes/Form.class.php'); +$form = new Form; + +if ($act == "csr" || (($_POST['save'] == gettext("Update")) && $input_errors)) +{ + $form->setAction('system_certmanager.php?act=csr'); + + $section = new Form_Section('Complete Signing Request'); + + if (isset($id) && $a_cert[$id]) + { + $form->addGlobal(new Form_Input( + 'id', + null, + 'hidden', + $id + )); + } + + $section->addInput(new Form_Input( + 'descr', + 'Descriptive name', + 'text', + $pconfig['descr'] + )); + + $section->addInput(new Form_Textarea( + 'csr', + 'Signing request data', + $pconfig['csr'] + ))->setReadonly()->setHelp('Copy the certificate signing data from here and '. + 'forward it to your certificate authority for signing.'); + + $section->addInput(new Form_Textarea( + 'cert', + 'Final certificate data', + $pconfig["cert"] + ))->setHelp('Paste the certificate received from your certificate authority here.'); + + $form->add($section); + print $form; + + include("foot.inc"); + exit; +} + +$form->setAction('system_certmanager.php?act=edit'); + +if (isset($userid) && $a_user) +{ + $form->addGlobal(new Form_Input( + 'userid', + null, + 'hidden', + $userid + )); +} + +if (isset($id) && $a_cert[$id]) +{ + $form->addGlobal(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$section = new Form_Section('Add a new certificate'); + +if (!isset($id)) +{ + $section->addInput(new Form_Select( + 'method', + 'Method', + $pconfig['method'], + $cert_methods + ))->toggles(); +} + +$section->addInput(new Form_Input( + 'descr', + 'Descriptive name', + 'text', + ($a_user && empty($pconfig['descr'])) ? $a_user[$userid]['name'] : $pconfig['descr'] +))->addClass('toggle-existing'); + +$form->add($section); +$section = new Form_Section('Import Certificate'); +$section->addClass('toggle-import collapse'); + +$section->addInput(new Form_Textarea( + 'cert', + 'Certificate data', + $pconfig['cert'] +))->setHelp('Paste a certificate in X.509 PEM format here.'); + +$section->addInput(new Form_Textarea( + 'key', + 'Private key data', + $pconfig['key'] +))->setHelp('Paste a private key in X.509 PEM format here.'); + +$form->add($section); +$section = new Form_Section('Internal Certificate'); +$section->addClass('toggle-internal collapse'); + +if (!$internal_ca_count) +{ + $section->addInput(new Form_StaticText( + 'Certificate authority', + gettext('No internal Certificate Authorities have been defined. You must '). + '<a href="system_camanager.php?act=new&method=internal"> '. gettext(" create") .'</a>'. + gettext(' an internal CA before creating an internal certificate.') + )); +} +else +{ + $allCas = array(); + foreach ($a_ca as $ca) + { + if (!$ca['prv']) + continue; + + $allCas[ $ca['refid'] ] = $ca['descr']; + } + + $section->addInput(new Form_Select( + 'caref', + 'Certificate authority', + $pconfig['caref'], + $allCas + )); +} + +print('<br />keylen = ' . $pconfig['keylen'] . '<br />'); + +$section->addInput(new Form_Select( + 'keylen', + 'Key length', + $pconfig['keylen'], + array_combine($cert_keylens, $cert_keylens) +)); + +$section->addInput(new Form_Select( + 'digest_alg', + 'Digest Algorithm', + $pconfig['digest_alg'], + array_combine($openssl_digest_algs, $openssl_digest_algs) +))->setHelp('NOTE: It is recommended to use an algorithm stronger than '. + 'SHA1 when possible.'); + +$section->addInput(new Form_Select( + 'type', + 'Certificate Type', + $pconfig['type'], + $cert_types +))->setHelp('Type of certificate to generate. Used for placing '. + 'restrictions on the usage of the generated certificate.'); + +$section->addInput(new Form_Input( + 'lifetime', + 'Lifetime (days)', + 'number', + $pconfig['lifetime'] +)); + +$section->addInput(new Form_Select( + 'dn_country', + 'Country Code', + $pconfig['dn_country'], + $dn_cc +)); + +$section->addInput(new Form_Input( + 'dn_state', + 'State or Province', + 'text', + $pconfig['dn_state'], + ['placeholder' => 'e.g. Texas'] +)); + +$section->addInput(new Form_Input( + 'dn_city', + 'City', + 'text', + $pconfig['dn_city'], + ['placeholder' => 'e.g. Austin'] +)); + +$section->addInput(new Form_Input( + 'dn_organization', + 'Organization', + 'text', + $pconfig['dn_organization'], + ['placeholder' => 'e.g. My Company Inc.'] +)); + +$section->addInput(new Form_Input( + 'dn_email', + 'Email Address', + 'email', + $pconfig['dn_email'], + ['placeholder' => 'e.g. admin@mycompany.com'] +)); + +$section->addInput(new Form_Input( + 'dn_commonname', + 'Common Name', + 'text', + $pconfig['dn_commonname'], + ['placeholder' => 'e.g. internal-ca'] +)); + +$group = new Form_Group('Alternative Names'); + +if (empty($pconfig['altnames']['item'])) +{ + $pconfig['altnames']['item'] = array( + array('type' => null, 'value' => null) + ); +} + +foreach ($pconfig['altnames']['item'] as $item) +{ + $group->add(new Form_Select( + 'altname_type', + 'Type', + $item['type'], + array( + 'DNS' => 'FQDN or Hostname', + 'IP' => 'IP address', + 'URI' => 'URI', + 'email' => 'email address', + ) + )); + + $group->add(new Form_Input( + 'altname_value', + 'Type', + 'text', + $item['value'] + )); + + $group->enableDuplication(); +} + +$section->add($group); + +$form->add($section); +$section = new Form_Section('External Signing Request'); +$section->addClass('toggle-external collapse'); + +$section->addInput(new Form_Select( + 'csr_keylen', + 'Key length', + $pconfig['csr_keylen'], + $cert_keylens +)); + +$section->addInput(new Form_Select( + 'csr_digest_alg', + 'Digest Algorithm', + $pconfig['csr_digest_alg'], + $openssl_digest_algs +))->setHelp('NOTE: It is recommended to use an algorithm stronger than '. + 'SHA1 when possible'); + +$section->addInput(new Form_Select( + 'dn_country', + 'Country Code', + $pconfig['dn_country'], + $dn_cc +)); + +$section->addInput(new Form_Input( + 'csr_dn_state', + 'State or Province', + 'text', + $pconfig['csr_dn_state'], + ['placeholder' => 'e.g. Texas'] +)); + +$section->addInput(new Form_Input( + 'csr_dn_city', + 'City', + 'text', + $pconfig['csr_dn_city'], + ['placeholder' => 'e.g. Austin'] +)); + +$section->addInput(new Form_Input( + 'csr_dn_organization', + 'Organization', + 'text', + $pconfig['csr_dn_organization'], + ['placeholder' => 'e.g. My Company Inc.'] +)); + +$section->addInput(new Form_Input( + 'csr_dn_email', + 'Email Address', + 'email', + $pconfig['csr_dn_email'], + ['placeholder' => 'e.g. admin@mycompany.com'] +)); + +$section->addInput(new Form_Input( + 'csr_dn_commonname', + 'Common Name', + 'text', + $pconfig['csr_dn_commonname'], + ['placeholder' => 'e.g. internal-ca'] +)); + +$form->add($section); +$section = new Form_Section('Choose an Existing Certificate'); +$section->addClass('toggle-existing collapse'); + +$existCerts = array(); +foreach ($config['cert'] as $cert) +{ + if (isset($userid) && in_array($cert['refid'], $config['system']['user'][$userid]['cert'])) + continue; + + $ca = lookup_ca($cert['caref']); + if ($ca) + $cert['descr'] .= " (CA: {$ca['descr']})"; + + if (cert_in_use($cert['refid'])) + $cert['descr'] .= " <i>In Use</i>"; + if (is_cert_revoked($cert)) + $cert['descr'] .= " <b>Revoked</b>"; + + $existCerts[ $cert['refid'] ] = $cert['descr']; +} + +$section->addInput(new Form_Select( + 'certref', + 'Existing Certificates', + $pconfig['certref'], + $existCerts +)); + +$form->add($section); +print $form; + +include('foot.inc');
\ No newline at end of file diff --git a/src/usr/local/www/system_crlmanager.php b/src/usr/local/www/system_crlmanager.php new file mode 100644 index 0000000..868d383 --- /dev/null +++ b/src/usr/local/www/system_crlmanager.php @@ -0,0 +1,701 @@ +<?php +/* + system_crlmanager.php + + Copyright (C) 2010 Jim Pingle + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: certificate_manager +*/ + +##|+PRIV +##|*IDENT=page-system-crlmanager +##|*NAME=System: CRL Manager +##|*DESCR=Allow access to the 'System: CRL Manager' page. +##|*MATCH=system_crlmanager.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("certs.inc"); +require_once("openvpn.inc"); +require_once("vpn.inc"); + +global $openssl_crl_status; + +$pgtitle = array(gettext("System"), gettext("Certificate Revocation List Manager")); + +$crl_methods = array( + "internal" => gettext("Create an internal Certificate Revocation List"), + "existing" => gettext("Import an existing Certificate Revocation List")); + +if (ctype_alnum($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && ctype_alnum($_POST['id'])) { + $id = $_POST['id']; +} + +if (!is_array($config['ca'])) { + $config['ca'] = array(); +} + +$a_ca =& $config['ca']; + +if (!is_array($config['cert'])) { + $config['cert'] = array(); +} + +$a_cert =& $config['cert']; + +if (!is_array($config['crl'])) { + $config['crl'] = array(); +} + +$a_crl =& $config['crl']; + +foreach ($a_crl as $cid => $acrl) { + if (!isset($acrl['refid'])) { + unset ($a_crl[$cid]); + } +} + +$act = $_GET['act']; +if ($_POST['act']) { + $act = $_POST['act']; +} + +if (!empty($id)) { + $thiscrl =& lookup_crl($id); +} + +// If we were given an invalid crlref in the id, no sense in continuing as it would only cause errors. +if (!$thiscrl && (($act != "") && ($act != "new"))) { + pfSenseHeader("system_crlmanager.php"); + $act=""; + $savemsg = gettext("Invalid CRL reference."); +} + +if ($act == "del") { + $name = htmlspecialchars($thiscrl['descr']); + if (crl_in_use($id)) { + $savemsg = sprintf(gettext("Certificate Revocation List %s is in use and cannot be deleted"), $name) . "<br />"; + } else { + foreach ($a_crl as $cid => $acrl) { + if ($acrl['refid'] == $thiscrl['refid']) { + unset($a_crl[$cid]); + } + } + write_config("Deleted CRL {$name}."); + $savemsg = sprintf(gettext("Certificate Revocation List %s successfully deleted"), $name) . "<br />"; + } +} + +if ($act == "new") { + $pconfig['method'] = $_GET['method']; + $pconfig['caref'] = $_GET['caref']; + $pconfig['lifetime'] = "9999"; + $pconfig['serial'] = "0"; +} + +if ($act == "exp") { + crl_update($thiscrl); + $exp_name = urlencode("{$thiscrl['descr']}.crl"); + $exp_data = base64_decode($thiscrl['text']); + $exp_size = strlen($exp_data); + + header("Content-Type: application/octet-stream"); + header("Content-Disposition: attachment; filename={$exp_name}"); + header("Content-Length: $exp_size"); + echo $exp_data; + exit; +} + +if ($act == "addcert") { + if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + if (!$pconfig['crlref'] || !$pconfig['certref']) { + pfSenseHeader("system_crlmanager.php"); + exit; + } + + // certref, crlref + $crl =& lookup_crl($pconfig['crlref']); + $cert = lookup_cert($pconfig['certref']); + + if (!$crl['caref'] || !$cert['caref']) { + $input_errors[] = gettext("Both the Certificate and CRL must be specified."); + } + + if ($crl['caref'] != $cert['caref']) { + $input_errors[] = gettext("CA mismatch between the Certificate and CRL. Unable to Revoke."); + } + if (!is_crl_internal($crl)) { + $input_errors[] = gettext("Cannot revoke certificates for an imported/external CRL."); + } + + if (!$input_errors) { + $reason = (empty($pconfig['crlreason'])) ? OCSP_REVOKED_STATUS_UNSPECIFIED : $pconfig['crlreason']; + cert_revoke($cert, $crl, $reason); + // refresh IPsec and OpenVPN CRLs + openvpn_refresh_crls(); + vpn_ipsec_configure(); + write_config("Revoked cert {$cert['descr']} in CRL {$crl['descr']}."); + pfSenseHeader("system_crlmanager.php"); + exit; + } + } +} + +if ($act == "delcert") { + if (!is_array($thiscrl['cert'])) { + pfSenseHeader("system_crlmanager.php"); + exit; + } + $found = false; + foreach ($thiscrl['cert'] as $acert) { + if ($acert['refid'] == $_GET['certref']) { + $found = true; + $thiscert = $acert; + } + } + if (!$found) { + pfSenseHeader("system_crlmanager.php"); + exit; + } + $certname = htmlspecialchars($thiscert['descr']); + $crlname = htmlspecialchars($thiscrl['descr']); + if (cert_unrevoke($thiscert, $thiscrl)) { + $savemsg = sprintf(gettext("Deleted Certificate %s from CRL %s"), $certname, $crlname) . "<br />"; + // refresh IPsec and OpenVPN CRLs + openvpn_refresh_crls(); + vpn_ipsec_configure(); + write_config(sprintf(gettext("Deleted Certificate %s from CRL %s"), $certname, $crlname)); + } else { + $savemsg = sprintf(gettext("Failed to delete Certificate %s from CRL %s"), $certname, $crlname) . "<br />"; + } + $act="edit"; +} + +if ($_POST) { + $input_errors = array(); + $pconfig = $_POST; + + /* input validation */ + if (($pconfig['method'] == "existing") || ($act == "editimported")) { + $reqdfields = explode(" ", "descr crltext"); + $reqdfieldsn = array( + gettext("Descriptive name"), + gettext("Certificate Revocation List data")); + } + if ($pconfig['method'] == "internal") { + $reqdfields = explode(" ", "descr caref"); + $reqdfieldsn = array( + gettext("Descriptive name"), + gettext("Certificate Authority")); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (preg_match("/[\?\>\<\&\/\\\"\']/", $pconfig['descr'])) { + array_push($input_errors, "The field 'Descriptive Name' contains invalid characters."); + } + + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + + /* save modifications */ + if (!$input_errors) { + $result = false; + + if ($thiscrl) { + $crl =& $thiscrl; + } else { + $crl = array(); + $crl['refid'] = uniqid(); + } + + $crl['descr'] = $pconfig['descr']; + if ($act != "editimported") { + $crl['caref'] = $pconfig['caref']; + $crl['method'] = $pconfig['method']; + } + + if (($pconfig['method'] == "existing") || ($act == "editimported")) { + $crl['text'] = base64_encode($pconfig['crltext']); + } + + if ($pconfig['method'] == "internal") { + $crl['serial'] = empty($pconfig['serial']) ? 9999 : $pconfig['serial']; + $crl['lifetime'] = empty($pconfig['lifetime']) ? 9999 : $pconfig['lifetime']; + $crl['cert'] = array(); + } + + if (!$thiscrl) { + $a_crl[] = $crl; + } + + write_config("Saved CRL {$crl['descr']}"); + // refresh IPsec and OpenVPN CRLs + openvpn_refresh_crls(); + vpn_ipsec_configure(); + pfSenseHeader("system_crlmanager.php"); + } +} + +include("head.inc"); +?> + +<script type="text/javascript"> +//<![CDATA[ + +function method_change() { + + method = document.iform.method.value; + + switch (method) { + case "internal": + document.getElementById("existing").style.display="none"; + document.getElementById("internal").style.display=""; + break; + case "existing": + document.getElementById("existing").style.display=""; + document.getElementById("internal").style.display="none"; + break; + } +} + +//]]> +</script> + +<?php + +function build_method_list() { + global $_GET, $crl_methods; + + $list = array(); + + foreach($crl_methods as $method => $desc) { + if (($_GET['importonly'] == "yes") && ($method != "existing")) + continue; + + $list[$method] = $desc; + } + + return($list); +} + +function build_ca_list() { + global $a_ca; + + $list = array(); + + foreach($a_ca as $ca) + $list[$ca['refid']] = $ca['descr']; + + return($list); +} + +function build_cacert_list() { + global $ca_certs; + + $list = array(); + + foreach($ca_certs as $cert) + $list[$cert['refid']] = $cert['descr']; + + return($list); +} + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'sucess'); + +$tab_array = array(); +$tab_array[] = array(gettext("CAs"), false, "system_camanager.php"); +$tab_array[] = array(gettext("Certificates"), false, "system_certmanager.php"); +$tab_array[] = array(gettext("Certificate Revocation"), true, "system_crlmanager.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +if ($act == "new" || $act == gettext("Save") || $input_errors) { + if (!isset($id)) { + $form = new Form(); + + $section = new Form_Section('Create new revocation list'); + + $section->addInput(new Form_Select( + 'method', + 'Method', + $pconfig['method'], + build_method_list() + )); + + } + + $section->addInput(new Form_Input( + 'descr', + 'Descriptive name', + 'text', + $pconfig['descr'] + )); + + $section->addInput(new Form_Select( + 'caref', + 'Certificate Authority', + $pconfig['caref'], + build_ca_list() + )); + + $form->add($section); + + $section = new Form_Section('Existing Certificate Revocation List'); + $section->addClass('existing'); + + $section->addInput(new Form_Textarea( + 'crltext', + 'CRL data', + $pconfig['crltext'] + ))->setHelp('Paste a Certificate Revocation List in X.509 CRL format here.'); + + $form->add($section); + + $section = new Form_Section('Internal Certificate Revocation List'); + $section->addClass('internal'); + + $section->addInput(new Form_Input( + 'lifetime', + 'Lifetime (Days)', + 'number', + $pconfig['lifetime'], + [max => '9999'] + )); + + $section->addInput(new Form_Input( + 'serial', + 'Serial', + 'number', + $pconfig['serial'], + [max => '9999'] + )); + + $form->add($section); + + if (isset($id) && $thiscrl) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); + } + + print($form); + +} elseif ($act == "editimported") { + + $form = new Form(); + + $section = new Form_Section('Edit Imported Certificate Revocation List'); + + $section->addInput(new Form_Input( + 'descr', + 'Descriptive name', + 'text', + $pconfig['descr'] + )); + + $section->addInput(new Form_Textarea( + 'crltext', + 'CRL data', + $pconfig['crltext'] + ))->setHelp('Paste a Certificate Revocation List in X.509 CRL format here.'); + + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); + + $section->addInput(new Form_Input( + 'act', + null, + 'hidden', + 'editimported' + )); + + $form->add($section); + + print($form); + +} elseif ($act == "edit") { + $crl = $thiscrl; + + $form = new Form(false); +?> + + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Currently Revoked Certificates for CRL") . ': ' . $crl['descr']?></h2></div> + <div class="panel-body table-responsive"> +<?php + if (!is_array($crl['cert']) || (count($crl['cert']) == 0)) + print_info_box(gettext("No Certificates Found for this CRL."), 'danger'); + else { +?> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Certificate Name")?></th> + <th><?=gettext("Revocation Reason")?></th> + <th><?=gettext("Revoked At")?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php + foreach($crl['cert'] as $i => $cert): + $name = htmlspecialchars($cert['descr']); +?> + <tr> + <td class="listlr"> + <?=$name; ?> + </td> + <td class="listlr"> + <?=$openssl_crl_status[$cert["reason"]]; ?> + </td> + <td class="listlr"> + <?=date("D M j G:i:s T Y", $cert["revoke_time"]); ?> + </td> + <td class="list"> + <a href="system_crlmanager.php?act=delcert&id=<?=$crl['refid']; ?>&certref=<?=$cert['refid']; ?>" onclick="return confirm('<?=gettext("Do you really want to delete this Certificate from the CRL?")?>')"> + <img src="/themes/<?= $g['theme']?>/images/icons/icon_x.gif" title="<?=gettext("Delete this certificate from the CRL ")?>" alt="<?=gettext("Delete this certificate from the CRL ")?>" /> + </a> + </td> + </tr> +<?php + endforeach; +?> + </tbody> + </table> +<?php } ?> + </div> + </div> +<?php + + $ca_certs = array(); + foreach($a_cert as $cert) + if ($cert['caref'] == $crl['caref']) + $ca_certs[] = $cert; + + if (count($ca_certs) == 0) + print_info_box(gettext("No Certificates Found for this CA."), 'danger'); + else + + $section = new Form_Section('Choose a certificate to revoke'); + $group = new Form_Group(null); + + $group->add(new Form_Select( + 'certref', + null, + $pconfig['certref'], + build_cacert_list() + ))->setWidth(4)->setHelp('Certificate'); + + $group->add(new Form_Select( + 'crlreason', + null, + -1, + $openssl_crl_status + ))->setHelp('Reason'); + + $group->add(new Form_Button( + 'submit', + 'Add' + ))->removeClass('btn-primary')->addClass('btn-success btn-sm'); + + $section->add($group); + + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $crl['refid'] + )); + + $section->addInput(new Form_Input( + 'act', + null, + 'hidden', + 'addcert' + )); + + $section->addInput(new Form_Input( + 'crlref', + null, + 'hidden', + $crl['refid'] + )); + + $form->add($section); + print($form); +} else { +?> + + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext("Additional Certificate Revocation Lists")?></h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Name")?></th> + <th><?=gettext("Internal")?></th> + <th><?=gettext("Certificates")?></th> + <th><?=gettext("In Use")?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php + $caimg = "/themes/{$g['theme']}/images/icons/icon_frmfld_cert.png"; + // Map CRLs to CAs in one pass + $ca_crl_map = array(); + foreach($a_crl as $crl) + $ca_crl_map[$crl['caref']][] = $crl['refid']; + + $i = 0; + foreach($a_ca as $ca): + $name = htmlspecialchars($ca['descr']); + + if($ca['prv']) { + $cainternal = "YES"; + } else + $cainternal = "NO"; +?> + <tr> + <td colspan="4"> + <?=$name?> + </td> + <td> +<?php + if ($cainternal == "YES"): ?> + <a href="system_crlmanager.php?act=new&caref=<?=$ca['refid']; ?>" class="btn btn-xs btn-success"> + <?=gettext("Add or Import CRLl")?> + </a> +<?php + else: ?> + <a href="system_crlmanager.php?act=new&caref=<?=$ca['refid']; ?>&importonly=yes" class="btn btn-xs btn-success"> + <?=gettext("Add or Import CRLl")?> + </a> +<?php + endif; ?> + </td> + </tr> +<?php + if (is_array($ca_crl_map[$ca['refid']])): + foreach($ca_crl_map[$ca['refid']] as $crl): + $tmpcrl = lookup_crl($crl); + $internal = is_crl_internal($tmpcrl); + $inuse = crl_in_use($tmpcrl['refid']); +?> + <tr> + <td><?=$tmpcrl['descr']; ?></td> + <td><?=($internal) ? "YES" : "NO"; ?></td> + <td><?=($internal) ? count($tmpcrl['cert']) : "Unknown (imported)"; ?></td> + <td><?=($inuse) ? "YES" : "NO"; ?></td> + <td> + <a href="system_crlmanager.php?act=exp&id=<?=$tmpcrl['refid']?>" class="btn btn-xs btn-success"> + <?=gettext("Export CRL")?>" + </a> +<?php + if ($internal): ?> + <a href="system_crlmanager.php?act=edit&id=<?=$tmpcrl['refid']?>" class="btn btn-xs btn-info"> + <?=gettext("Edit CRL")?> + </a> +<?php + else: ?> + <a href="system_crlmanager.php?act=editimported&id=<?=$tmpcrl['refid']?>" class="btn btn-xs btn-info"> + <?=gettext("Edit CRL")?> + </a> +<?php endif; + if (!$inuse): ?> + <a href="system_crlmanager.php?act=del&id=<?=$tmpcrl['refid']?>" class="btn btn-xs btn-danger"> + <?=gettext("Delete CRL")?> + </a> +<?php + endif; ?> + </td> + </tr> +<?php + $i++; + endforeach; + endif; + $i++; + endforeach; +?> + </tbody> + </table> + </div> + </div> + + +<?php +} +?> + +<script> +//<![CDATA[ +events.push(function(){ + + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // When the 'method" selector is changed, we show/hide certain sections + $('#method').on('change', function() { + hideClass('internal', ($('#method').val() == 'existing')); + hideClass('existing', ($('#method').val() == 'internal')); + }); + + hideClass('internal', ($('#method').val() == 'existing')); + hideClass('existing', ($('#method').val() == 'internal')); +}); +//]]> +</script> + +<?php include("foot.inc"); + diff --git a/src/usr/local/www/system_firmware.php b/src/usr/local/www/system_firmware.php new file mode 100644 index 0000000..4a6cc5a --- /dev/null +++ b/src/usr/local/www/system_firmware.php @@ -0,0 +1,331 @@ +<?php +/* $Id$ */ +/* + system_firmware.php + Copyright (C) 2008 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/bin/tar + pfSense_MODULE: firmware +*/ + +##|+PRIV +##|*IDENT=page-system-firmware-manualupdate +##|*NAME=System: Firmware: Manual Update page +##|*DESCR=Allow access to the 'System: Firmware: Manual Update' page. +##|*MATCH=system_firmware.php* +##|-PRIV + +$d_isfwfile = 1; +$nocsrf = true; + +require_once("globals.inc"); +require_once("functions.inc"); +require_once("guiconfig.inc"); +require_once("xmlrpc_client.inc"); + +$curcfg = $config['system']['firmware']; + +/* Allow additional execution time 0 = no limit. */ +ini_set('max_execution_time', '9999'); +ini_set('max_input_time', '9999'); + +function file_is_for_platform($filename, $ul_name) { + global $g; + if ($g['platform'] == "nanobsd") { + if (stristr($ul_name, "nanobsd")) { + return true; + } else { + return false; + } + } + $_gb = exec("/usr/bin/tar xzf $filename -C /tmp/ etc/platform"); + unset($_gb); + if (!file_exists("/tmp/etc/platform")) { + return false; + } + $upgrade_is_for_platform = trim(file_get_contents("/tmp/etc/platform", " \n\t\r")); + if ($g['platform'] == $upgrade_is_for_platform) { + @unlink("/tmp/etc/platform"); + return true; + } + return false; +} + +function file_upload_error_message($error_code) { + switch ($error_code) { + case UPLOAD_ERR_INI_SIZE: + return gettext('The uploaded file exceeds the upload_max_filesize directive in php.ini'); + case UPLOAD_ERR_FORM_SIZE: + return gettext('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'); + case UPLOAD_ERR_PARTIAL: + return gettext('The uploaded file was only partially uploaded'); + case UPLOAD_ERR_NO_FILE: + return gettext('No file was uploaded'); + case UPLOAD_ERR_NO_TMP_DIR: + return gettext('Missing a temporary folder'); + case UPLOAD_ERR_CANT_WRITE: + return gettext('Failed to write file to disk'); + case UPLOAD_ERR_EXTENSION: + return gettext('File upload stopped by extension'); + default: + return gettext('Unknown upload error'); + } +} + +/* if upgrade in progress, alert user */ +if (is_subsystem_dirty('firmwarelock')) { + $pgtitle = array(gettext("System"), gettext("Firmware"), gettext("Manual Update")); + include("head.inc"); + include("fbegin.inc"); + print_info_box(gettext("An upgrade is currently in progress. The firewall will reboot when the operation is complete.") . "<p><img src='/themes/{$g['theme']}/images/icons/icon_fw-update.gif' alt='update' /></p>"); + include("foot.inc"); + exit; +} + +if ($_POST['backupbeforeupgrade']) { + touch("/tmp/perform_full_backup.txt"); +} + +/* Handle manual upgrade */ +if ($_POST && !is_subsystem_dirty('firmwarelock')) { + + unset($input_errors); + unset($sig_warning); + + if (stristr($_POST['Submit'], gettext("Enable"))) { + $mode = "enable"; + } else if (stristr($_POST['Submit'], gettext("Disable"))) { + $mode = "disable"; + } else if (stristr($_POST['Submit'], gettext("Upgrade")) || $_POST['sig_override']) { + $mode = "upgrade"; + } else if ($_POST['sig_no']) { + if (file_exists("{$g['upload_path']}/firmware.tgz")) { + unlink("{$g['upload_path']}/firmware.tgz"); + } + } + if ($mode) { + if ($mode == "enable") { + conf_mount_rw(); + mark_subsystem_dirty('firmware'); + } else if ($mode == "disable") { + conf_mount_ro(); + clear_subsystem_dirty('firmware'); + } else if ($mode == "upgrade") { + if ($_FILES['ulfile']['error']) { + $errortext = "(" . file_upload_error_message($_FILES['ulfile']['error']) . ")"; + } + if (is_uploaded_file($_FILES['ulfile']['tmp_name'])) { + /* verify firmware image(s) */ + if (file_is_for_platform($_FILES['ulfile']['tmp_name'], $_FILES['ulfile']['name']) == false && !$_POST['sig_override']) { + $input_errors[] = gettext("The uploaded image file is not for this platform."); + } else if (!file_exists($_FILES['ulfile']['tmp_name'])) { + /* probably out of memory for the MFS */ + $input_errors[] = gettext("Image upload failed (out of memory?)"); + mwexec("/etc/rc.firmware disable"); + clear_subsystem_dirty('firmware'); + } else { + /* move the image so PHP won't delete it */ + rename($_FILES['ulfile']['tmp_name'], "{$g['upload_path']}/firmware.tgz"); + + /* check digital signature */ + $sigchk = verify_digital_signature("{$g['upload_path']}/firmware.tgz"); + + if ($sigchk == 1) { + $sig_warning = gettext("The digital signature on this image is invalid."); + } else if ($sigchk == 2 && !isset($config['system']['firmware']['allowinvalidsig'])) { + $sig_warning = gettext("This image is not digitally signed."); + } else if (($sigchk >= 3)) { + $sig_warning = gettext("There has been an error verifying the signature on this image."); + } + + if (!verify_gzip_file("{$g['upload_path']}/firmware.tgz")) { + $input_errors[] = gettext("The image file is corrupt."); + unlink("{$g['upload_path']}/firmware.tgz"); + } + } + } + + run_plugins("/usr/local/pkg/firmware_upgrade"); + + /* Check for input errors, firmware locks, warnings, then check for firmware if sig_override is set */ + if (!$input_errors && !is_subsystem_dirty('firmwarelock') && (!$sig_warning || $_POST['sig_override'])) { + if (file_exists("{$g['upload_path']}/firmware.tgz")) { + /* fire up the update script in the background */ + mark_subsystem_dirty('firmwarelock'); + $savemsg = gettext("The firmware is now being updated. The firewall will reboot automatically."); + if (stristr($_FILES['ulfile']['name'], "nanobsd") or $_POST['isnano'] == "yes") { + mwexec_bg("/etc/rc.firmware pfSenseNanoBSDupgrade {$g['upload_path']}/firmware.tgz"); + } else { + if ($g['platform'] == "nanobsd") { + $whichone = "pfSenseNanoBSDupgrade"; + } else { + $whichone = "pfSenseupgrade"; + } + mwexec_bg("/etc/rc.firmware {$whichone} {$g['upload_path']}/firmware.tgz"); + unset($whichone); + } + } else { + $savemsg = sprintf(gettext("Firmware image missing or other error, please try again %s."), $errortext); + } + } + } + } +} + +$pgtitle = array(gettext("System"), gettext("Firmware")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg); + +if ($fwinfo != "") + print_info_box($fwinfo); + +$tab_array = array(); +$tab_array[] = array(gettext("Manual Update"), true, "system_firmware.php"); +$tab_array[] = array(gettext("Auto Update"), false, "system_firmware_check.php"); +$tab_array[] = array(gettext("Updater Settings"), false, "system_firmware_settings.php"); +if($g['hidedownloadbackup'] == false) + $tab_array[] = array(gettext("Restore Full Backup"), false, "system_firmware_restorefullbackup.php"); + +display_top_tabs($tab_array); + +// For a simple yes/no we can use an HTML form +if ($sig_warning && !$input_errors) { + $sig_warning = gettext("The image you uploaded " . + "is not an official/supported image and may lead to unexpected behavior or security " . + "compromises. Only install images that come from sources that you trust, and make sure ". + "that the image has not been tampered with.") . "<br /><br />". + gettext("Do you want to install this image anyway (on your own risk)?"); + + print_info_box($sig_warning); +?> + <form action="system_firmware.php" method="post" enctype="multipart/form-data"> + <input name="sig_override" type="submit" class="btn btn-danger" id="sig_override" value=" <?=gettext("Yes");?> " /> + <input name="sig_no" type="submit" class="btn btn-default" id="sig_no" value=" <?=gettext("No"); ?> " /> + </form> +<?php + +} else { + // This is where the work gets done so Forms.classes will be used from this point + if (!is_subsystem_dirty('firmwarelock')) { + require('classes/Form.class.php'); + + if (!is_subsystem_dirty('rebootreq')) { + // Provide a button to enable firmware upgrades. Upgrades should be disabled on initial page load + if (!is_subsystem_dirty('firmware') || !$_POST || $_POST['save']) { + $enablebtn = new Form_Button( + 'Submit', + 'Enable firmware upload' + ); + + $enablebtn->addClass('btn-warning'); + $form = new Form($enablebtn); + $section = new Form_Section('Invoke ' . $g['product_name'] .' Manual Upgrade'); + $section->addInput(new Form_StaticText('Enable', 'Click the "Enable firmware upload" button below to begin.')); + } + else { + // Upgrades are now enabled + $form = new Form('Disable firmware upload'); + + $form->setMultipartEncoding(); + + $section = new Form_Section('Perform ' . $g['product_name'] .' Manual Upgrade'); + + if (!session_id()) + $upload_id = uniqid(); + else + $upload_id = session_id(); + + $section->addInput(new Form_Input( + 'UPLOAD_IDENTIFIER', + '', + 'hidden', + $upload_id + )); + + if(stristr($_FILES['ulfile']['name'],"nanobsd")) { + $section->addInput(new Form_Input( + 'isnano', + '', + 'hidden', + 'yes' + )); + } + + if ($g['platform'] == "nanobsd") + $type = "*.img.gz"; + else + $type = "*.tgz"; + + $filepicker = new Form_Input( + 'ulfile', + 'File to upload (' . $type . ')', + 'file', + '' + ); + + $section->addInput($filepicker)->setHelp('Choose the file you wish to upload'); + + if ($g['hidebackupbeforeupgrade'] === false) { + $section->addInput(new Form_Checkbox( + 'backupbeforeupgrade', + Backup, + 'Perform a full backup prior to upgrade"', + false + )); + } + + $section->addInput(new Form_Button( + 'submit', + 'Upgrade firmware' + ))->addClass('btn-danger btn-sm')->setHelp('Click the "Upgrade firmware" button below to start the upgrade process'); + } + + $form->add($section); + print($form); + } + } + else { + print_info_box('<strong>' . gettext("You must reboot the system before you can upgrade the firmware.") . '</strong>'); + } + + if (is_subsystem_dirty('firmware') && !is_subsystem_dirty('firmwarelock')) { + print_info_box('<strong>' . gettext("DO NOT ") . '</strong>' . gettext('abort the firmware upgrade once it ' . + 'has started. The firewall will reboot automatically after ' . + 'storing the new firmware. The configuration will be maintained.')); + } +} + +include("foot.inc"); ?>
\ No newline at end of file diff --git a/src/usr/local/www/system_firmware_auto.php b/src/usr/local/www/system_firmware_auto.php new file mode 100755 index 0000000..d57d8d0 --- /dev/null +++ b/src/usr/local/www/system_firmware_auto.php @@ -0,0 +1,273 @@ +<?php +/* $Id$ */ +/* + system_firmware_auto.php + Copyright (C) 2005 Scott Ullrich + Copyright (C) 2008 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Based originally on system_firmware.php + (C)2003-2004 Manuel Kasper + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/bin/tar /usr/bin/nohup /bin/cat /sbin/sha256 + pfSense_MODULE: firmware +*/ + +##|+PRIV +##|*IDENT=page-system-firmware-checkforupdate +##|*NAME=System: Firmware: Check For Update page +##|*DESCR=Allow access to the 'System: Firmware: Check For Update' page. +##|*MATCH=system_firmware_auto.php* +##|-PRIV + +$nocsrf = true; + +require("guiconfig.inc"); +require_once("pfsense-utils.inc"); + +$curcfg = $config['system']['firmware']; + +if (isset($curcfg['alturl']['enable'])) { + $updater_url = "{$config['system']['firmware']['alturl']['firmwareurl']}"; +} else { + $updater_url = $g['update_url']; +} + +if ($_POST['backupbeforeupgrade']) { + touch("/tmp/perform_full_backup.txt"); +} + +$closehead = false; +$pgtitle = array(gettext("Diagnostics"), gettext("Firmware"), gettext("Auto Update")); +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("Manual Update"), false, "system_firmware.php"); +$tab_array[] = array(gettext("Auto Update"), true, "system_firmware_check.php"); +$tab_array[] = array(gettext("Updater Settings"), false, "system_firmware_settings.php"); +if($g['hidedownloadbackup'] == false) + $tab_array[] = array(gettext("Restore Full Backup"), false, "system_firmware_restorefullbackup.php"); + +display_top_tabs($tab_array); +?> + + +<div id="statusheading" name="statusheading" class="panel panel-default"> + <div class="panel-heading" id="status" name="status"><?=gettext("Beginning firmware upgrade")?></div> + <div id='output' name='output' class="panel-body"></div> +</div> + + +<?php +include("foot.inc"); ?> + +<?php + +panel_heading_text(gettext("Downloading current version information") . "..."); +panel_heading_class('info'); + +$nanosize = ""; +if ($g['platform'] == "nanobsd") { + if (file_exists("/etc/nano_use_vga.txt")) { + $nanosize = "-nanobsd-vga-"; + } else { + $nanosize = "-nanobsd-"; + } + + $nanosize .= strtolower(trim(file_get_contents("/etc/nanosize.txt"))); +} + +@unlink("/tmp/{$g['product_name']}_version"); +download_file_with_progress_bar("{$updater_url}/version{$nanosize}", "/tmp/{$g['product_name']}_version"); +$latest_version = str_replace("\n", "", @file_get_contents("/tmp/{$g['product_name']}_version")); +if (!$latest_version) { + update_output_window(gettext("Unable to check for updates.")); + require("foot.inc"); + exit; +} else { + $current_installed_buildtime = trim(file_get_contents("/etc/version.buildtime")); + $latest_version = trim(@file_get_contents("/tmp/{$g['product_name']}_version")); + $latest_version_pfsense = strtotime($latest_version); + if(!$latest_version) { + panel_heading_class('danger'); + panel_heading_text(gettext('Version check')); + update_output_window(gettext("Unable to check for updates.")); + require("foot.inc"); + exit; + } else { + if (pfs_version_compare($current_installed_buildtime, $current_installed_version, $latest_version) == -1) { + panel_heading_text(gettext("Downloading updates") . '...'); + panel_heading_class('info'); + + conf_mount_rw(); + if ($g['platform'] == "nanobsd") { + $update_filename = "latest{$nanosize}.img.gz"; + } else { + $update_filename = "latest.tgz"; + } + + $status = download_file_with_progress_bar("{$updater_url}/{$update_filename}", "{$g['upload_path']}/latest.tgz", "read_body_firmware"); + $status = download_file_with_progress_bar("{$updater_url}/{$update_filename}.sha256", "{$g['upload_path']}/latest.tgz.sha256"); + conf_mount_ro(); + update_output_window("{$g['product_name']} " . gettext("download complete.")); + } else { + panel_heading_class('success'); + panel_heading_text(gettext('Version check complete')); + update_output_window(gettext("You are on the latest version.")); + require("foot.inc"); + exit; + } + } +} + +/* launch external upgrade helper */ +$external_upgrade_helper_text = "/etc/rc.firmware "; + +if ($g['platform'] == "nanobsd") { + $external_upgrade_helper_text .= "pfSenseNanoBSDupgrade "; +} else { + $external_upgrade_helper_text .= "pfSenseupgrade "; +} + +$external_upgrade_helper_text .= "{$g['upload_path']}/latest.tgz"; + +$downloaded_latest_tgz_sha256 = str_replace("\n", "", `/sbin/sha256 -q {$g['upload_path']}/latest.tgz`); +$upgrade_latest_tgz_sha256 = str_replace("\n", "", `/bin/cat {$g['upload_path']}/latest.tgz.sha256 | awk '{ print $4 }'`); + +$sigchk = 0; + +if (!isset($curcfg['alturl']['enable'])) { + $sigchk = verify_digital_signature("{$g['upload_path']}/latest.tgz"); +} + +$exitstatus = 0; +if ($sigchk == 1) { + $sig_warning = gettext("The digital signature on this image is invalid."); + $exitstatus = 1; +} else if ($sigchk == 2) { + $sig_warning = gettext("This image is not digitally signed."); + if (!isset($config['system']['firmware']['allowinvalidsig'])) { + $exitstatus = 1; + } +} else if (($sigchk >= 3)) { + $sig_warning = gettext("There has been an error verifying the signature on this image."); + $exitstatus = 1; +} + +if ($exitstatus) { + panel_heading_text($sig_warning); + panel_heading_class('danger'); + + update_output_window(gettext("Update cannot continue. You can disable this check on the Updater Settings tab.")); + require("foot.inc"); + exit; +} else if ($sigchk == 2) { + panel_heading_text(gettext('Upgrade in progress...')); + panel_heading_class('info'); + + update_output_window("\n" . gettext("Upgrade Image does not contain a signature but the system has been configured to allow unsigned images. One moment please...") . "\n"); +} + +if (!verify_gzip_file("{$g['upload_path']}/latest.tgz")) { + panel_heading_text(gettext("The image file is corrupt.")); + panel_heading_class('danger'); + + update_output_window(gettext("Update cannot continue")); + if (file_exists("{$g['upload_path']}/latest.tgz")) { + conf_mount_rw(); + unlink("{$g['upload_path']}/latest.tgz"); + conf_mount_ro(); + } + require("foot.inc"); + exit; +} + +if($downloaded_latest_tgz_sha256 <> $upgrade_latest_tgz_sha256) { + panel_heading_text(gettext("Downloading complete but sha256 does not match.")); + panel_heading_class('danger'); + + update_output_window(gettext("Auto upgrade aborted.") . " \n\n" . gettext("Downloaded SHA256") . ": " . $downloaded_latest_tgz_sha256 . "\n\n" . gettext("Needed SHA256") . ": " . $upgrade_latest_tgz_sha256); +} else { + update_output_window($g['product_name'] . " " . gettext("is now upgrading.") . "\\n\\n" . gettext("The firewall will reboot once the operation is completed.")); + mwexec_bg($external_upgrade_helper_text); +} + +/* + Helper functions +*/ + +function read_body_firmware($ch, $string) { + global $g, $fout, $file_size, $downloaded, $counter, $version, $latest_version; + $length = strlen($string); + $downloaded += intval($length); + $downloadProgress = round(100 * (1 - $downloaded / $file_size), 0); + $downloadProgress = 100 - $downloadProgress; + $a = $file_size; + $b = $downloaded; + $c = $downloadProgress; + $text = " " . gettext("Auto Update Download Status") . "\\n"; + $text .= "----------------------------------------------------\\n"; + $text .= " " . gettext("Current Version") . " : {$g['product_version']}\\n"; + $text .= " " . gettext("Latest Version") . " : {$latest_version}\\n"; + $text .= " " . gettext("File size") . " : {$a}\\n"; + $text .= " " . gettext("Downloaded") . " : {$b}\\n"; + $text .= " " . gettext("Percent") . " : {$c}%\\n"; + $text .= "----------------------------------------------------\\n"; + $counter++; + if ($counter > 150) { + update_output_window($text); + update_progress_bar($downloadProgress); + $counter = 0; + } + fwrite($fout, $string); + return $length; +} + +// Update the text in the panel-heading +function panel_heading_text($text) { +?> + <script> + events.push(function(){ + $('#status').html('<?=$text?>'); + }); + </script> +<?php +} + +// Update the class of the message panel so that it's color changes +// Use danger, success, info, warning, default etc +function panel_heading_class($newclass = 'default') { +?> + <script> + events.push(function(){ + $('#statusheading').removeClass().addClass('panel panel-' + '<?=$newclass?>'); + }); + </script> +<?php +} + +?> + diff --git a/src/usr/local/www/system_firmware_check.php b/src/usr/local/www/system_firmware_check.php new file mode 100644 index 0000000..c2cc9a1 --- /dev/null +++ b/src/usr/local/www/system_firmware_check.php @@ -0,0 +1,181 @@ +<?php +/* $Id$ */ +/* + system_firmware_check.php + Copyright (C) 2008 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: firmware +*/ + +##|+PRIV +##|*IDENT=page-system-firmware-autoupdate +##|*NAME=System: Firmware: Auto Update page +##|*DESCR=Allow access to the 'System: Firmware: Auto Update' page. +##|*MATCH=system_firmware_check.php* +##|-PRIV + +$d_isfwfile = 1; +require("guiconfig.inc"); +require_once("pfsense-utils.inc"); + +$curcfg = $config['system']['firmware']; +$pgtitle = array(gettext("System"), gettext("Firmware"), gettext("Auto Update")); +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("Manual Update"), false, "system_firmware.php"); +$tab_array[] = array(gettext("Auto Update"), true, "system_firmware_check.php"); +$tab_array[] = array(gettext("Updater Settings"), false, "system_firmware_settings.php"); +if($g['hidedownloadbackup'] == false) + $tab_array[] = array(gettext("Restore Full Backup"), false, "system_firmware_restorefullbackup.php"); +display_top_tabs($tab_array); +?> + +<form action="system_firmware_auto.php" method="post"> + <div id="statusheading" class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Update progress')?></h2></div> + <div class="panel-body" name="output" id="output"></div> + </div> + + <div id="backupdiv" style="visibility:hidden"> + <?php if ($g['hidebackupbeforeupgrade'] === false): ?> + <br /><input type="checkbox" name="backupbeforeupgrade" id="backupbeforeupgrade" /><?=gettext("Perform full backup prior to upgrade")?> + <?php endif; ?> + </div> + <br /> + <input id='invokeupgrade' class="btn btn-warning" style='visibility:hidden' type="submit" value="<?=gettext("Invoke Auto Upgrade"); ?>" /> + +<?php + +/* Define necessary variables. */ +if (isset($curcfg['alturl']['enable'])) { + $updater_url = "{$config['system']['firmware']['alturl']['firmwareurl']}"; +} else { + $updater_url = $g['update_url']; +} +$needs_system_upgrade = false; +$static_text .= gettext("Downloading new version information..."); + +$nanosize = ""; +if ($g['platform'] == "nanobsd") { + if (file_exists("/etc/nano_use_vga.txt")) { + $nanosize = "-nanobsd-vga-"; + } else { + $nanosize = "-nanobsd-"; + } + + $nanosize .= strtolower(trim(file_get_contents("/etc/nanosize.txt"))); +} + +if (download_file_with_progress_bar("{$updater_url}/version{$nanosize}", "/tmp/{$g['product_name']}_version", 'read_body', 5, 5) === true) { + $remote_version = trim(@file_get_contents("/tmp/{$g['product_name']}_version")); +} +$static_text .= gettext("done") . "\\n"; +if (!$remote_version) { + $static_text .= gettext("Unable to check for updates.") . "\\n"; + if (isset($curcfg['alturl']['enable'])) { + $static_text .= gettext("Could not contact custom update server.") . "\\n"; + } else { + $static_text .= sprintf(gettext('Could not contact %1$s update server %2$s%3$s'), $g['product_name'], $updater_url, "\\n"); + } +} else { + $static_text .= gettext("Obtaining current version information..."); + panel_text($static_text); + + $current_installed_buildtime = trim(file_get_contents("/etc/version.buildtime")); + + $static_text .= "done<br />"; + panel_text($static_text); + + if (pfs_version_compare($current_installed_buildtime, $g['product_version'], $remote_version) == -1) { + $needs_system_upgrade = true; + } else { + $static_text .= "<br />" . gettext("You are on the latest version.") . "<br />"; + panel_text($static_text); + panel_heading_class('success'); + } +} + +update_output_window($static_text); +if ($needs_system_upgrade == false) { + print("</form>"); + require("foot.inc"); + + exit; +} +?> +<script> + events.push(function(){ + $('#invokeupgrade').css('visibility','visible'); + $('#backupdiv').css('visibility','visible'); + }); +</script> +<?php + +$txt = gettext("A new version is now available") . "<br />"; +$txt .= gettext("Current version") .": ". $current_installed_version . "<br />"; +if ($g['platform'] == "nanobsd") { + $txt .= " " . gettext("NanoBSD Size") . " : " . trim(file_get_contents("/etc/nanosize.txt")) . "<br />"; +} +$txt .= " " . gettext("Built On") .": ". $current_installed_buildtime . "<br />"; +$txt .= " " . gettext("New version") .": ". htmlspecialchars($remote_version, ENT_QUOTES | ENT_HTML401). "<br /><br />"; +$txt .= " " . gettext("Update source") .": ". $updater_url . "<br />"; +panel_text($txt); +panel_heading_class('info'); +?> + +</form> +<?php + +// Update the class of the message panel so that it's color changes +// Use danger, success, info, warning, default etc +function panel_heading_class($newclass = 'default') { +?> + <script> + events.push(function(){ + $('#statusheading').removeClass().addClass('panel panel-' + '<?=$newclass?>'); + }); + </script> +<?php +} + +// Update the text in the panel-heading +function panel_text($text) { +?> + <script> + events.push(function(){ + $('#output').html('<?=$text?>'); + }); + </script> +<?php +} + +include("foot.inc"); diff --git a/src/usr/local/www/system_firmware_restorefullbackup.php b/src/usr/local/www/system_firmware_restorefullbackup.php new file mode 100644 index 0000000..54ba297 --- /dev/null +++ b/src/usr/local/www/system_firmware_restorefullbackup.php @@ -0,0 +1,229 @@ +<?php +/* $Id$ */ +/* + system_firmware_restorefullbackup.php + Copyright (C) 2011 Scott Ullrich + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + pfSense_BUILDER_BINARIES: /etc/rc.restore_full_backup + pfSense_MODULE: backup +*/ + +##|+PRIV +##|*IDENT=page-diagnostics-restore-full-backup +##|*NAME=Diagnostics: Restore full backup +##|*DESCR=Allow access to the 'Diagnostics: Restore Full Backup' page. +##|*MATCH=system_firmware_restorefullbackup.php +##|-PRIV + +// Don't really restore or reboot while testing. Should be 'false' for production of course +define(DEBUG, false); + +/* Allow additional execution time 0 = no limit. */ +ini_set('max_execution_time', '0'); +ini_set('max_input_time', '0'); + +require_once("functions.inc"); +require("guiconfig.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +if ($_POST['overwriteconfigxml']) { + touch("/tmp/do_not_restore_config.xml"); +} + +if ($_GET['backupnow']) { + mwexec_bg("/etc/rc.create_full_backup"); +} + +if($_POST['downloadbackup']) { + $filename = basename($_POST['downloadbackup']); + + if(DEBUG) + print_info_box('DEBUG: Simulating download of ' . htmlspecialchars($filename)); + else { + $path = "/root/{$filename}"; + if(file_exists($path)) { + session_write_close(); + ob_end_clean(); + session_cache_limiter('public'); + //$fd = fopen("/root/{$filename}", "rb"); + $filesize = filesize("/root/{$filename}"); + header("Cache-Control: "); + header("Pragma: "); + header("Content-Type: application/octet-stream"); + header("Content-Length: " .(string)(filesize($path)) ); + header('Content-Disposition: attachment; filename="'.$filename.'"'); + header("Content-Transfer-Encoding: binary\n"); + if($file = fopen("/root/{$filename}", 'rb')){ + while( (!feof($file)) && (connection_status()==0) ){ + print(fread($file, 1024*8)); + flush(); + } + fclose($file); + } + + exit; + } + } +} +else if ($_POST['deletefile']) { + $filename = basename($_POST['deletefile']); + if(DEBUG) + print_info_box('DEBUG: Simulating deletion of ' . htmlspecialchars($filename)); + else { + if(file_exists("/root/{$filename}") && (preg_match("/pfSense-full-backup-\d+-\d+\.tgz/", $filename) == 1)) { + unlink("/root/" . $filename); + $savemsg = htmlspecialchars($filename) . " " . gettext("has been deleted."); + } else { + $savemsg = htmlspecialchars($filename) . " " . gettext("has not been been deleted (invalid backup file or file does not exist)."); + } + } +} +else if ($_POST['restorefile']) { + $filename = basename($_POST['restorefile']); + if(DEBUG) + print_info_box('DEBUG: Restoration of ' . $filename . ' simulated'); + else { + if(file_exists("/root/{$filename}") && (preg_match("/pfSense-full-backup-\d+-\d+\.tgz/", $filename) == 1)) { + mwexec_bg("/etc/rc.restore_full_backup /root/" . escapeshellcmd($filename)); + $savemsg = gettext("The firewall is currently restoring") . " " . htmlspecialchars($filename); + } else { + $savemsg = htmlspecialchars($filename) . " " . gettext("has not been been restored (invalid backup file or file does not exist)."); + } + } +} + +$pgtitle = array(gettext("Diagnostics"), gettext("Restore full backup")); +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg); + +if (is_subsystem_dirty('restore')) { + ?> + <form action="reboot.php" method="post"> + <input name="Submit" type="hidden" value="Yes" /> +<?php print_info_box(gettext("The firewall configuration has been changed. The firewall is now rebooting.")) ?> + </form> +<?php +} +?> + +<?php +$tab_array = array(); +$tab_array[] = array(gettext("Manual Update"), false, "system_firmware.php"); +$tab_array[] = array(gettext("Auto Update"), false, "system_firmware_check.php"); +$tab_array[] = array(gettext("Updater Settings"), false, "system_firmware_settings.php"); +if($g['hidedownloadbackup'] == false) + $tab_array[] = array(gettext("Restore Full Backup"), true, "system_firmware_restorefullbackup.php"); + +display_top_tabs($tab_array); +?> + +<form action="system_firmware_restorefullbackup.php" method="post"> + <div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('Available backup files')?></h2></div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-hover table-striped table-condensed"> + <thead> + <tr> + <th><?=gettext("File to restore")?></th> + <th><?=gettext("Date") ?></th> + <th><?=gettext("Size") ?></th> + <th><?=gettext("Action")?></th> + </tr> + </thead> + <tbody> +<?php + $home = getcwd(); + chdir("/root"); + $available_restore_files = glob("pfSense-full-backup-*"); + $counter = 0; + foreach($available_restore_files as $arf) { + $counter++; + $size = exec("gzip -l /root/$arf | grep -v compressed | awk '{ print $2 }'"); +?> + <tr> + <td> + <input type="radio" class="radio-inline" name="restorefile" value="<?=$arf?>" /> <?=$arf?> + </td> + <td> + <?=date ("F d Y H:i:s", filemtime($arf))?> + </td> + <td> + <?=format_bytes($size)?> + </td> + <td> + <button class="btn btn-xs btn-danger" type="submit" name="deletefile" value="<?=$arf?>" title="Delete backup file">Delete</button> + <button class="btn btn-xs btn-default" type="submit" name="downloadbackup" value="<?=$arf?>" title="Download backup file">Download</button> + </td> + </tr> +<?php + } // e-o-foreach backup file + + chdir($home); // Je me souvien +?> + </tbody> + </table> + </div> + </div> + </div> +<?php + if($counter == 0) + print_info_box(gettext("Could not locate any previous backups.")); + else { +?> + <p><input type="checkbox" name="overwriteconfigxml" id="overwriteconfigxml" checked="checked"/> <?=gettext(" When checked, DO NOT restore the config.xml file."); ?></p> + <p><button name="Restore" type="submit" class="btn btn-danger" id="restore" value="<?=gettext("Restore")?>"><?=gettext("Restore")?></button></p> +<?php + } +?> +</form> + +<script type="text/javascript"> +//<![CDATA[ +encrypt_change(); +decrypt_change(); +//]]> +</script> + +<?php + +if (is_subsystem_dirty('restore')) { + system_reboot(); +} + +include("foot.inc");?>
\ No newline at end of file diff --git a/src/usr/local/www/system_firmware_settings.php b/src/usr/local/www/system_firmware_settings.php new file mode 100644 index 0000000..e43ca1c --- /dev/null +++ b/src/usr/local/www/system_firmware_settings.php @@ -0,0 +1,260 @@ +<?php +/* $Id$ */ +/* + system_firmware_settings.php + part of pfSense + Copyright (C) 2005 Colin Smith + Copyright (C) 2008 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/bin/fetch + pfSense_MODULE: firmware +*/ + +##|+PRIV +##|*IDENT=page-system-firmware-settings +##|*NAME=System: Firmware: Settings page +##|*DESCR=Allow access to the 'System: Firmware: Settings' page. +##|*MATCH=system_firmware_settings.php* +##|-PRIV + +require("guiconfig.inc"); + +if ($_POST) { + unset($input_errors); + + /* input validation */ + if (($_POST['alturlenable'] == "yes") && (empty($_POST['firmwareurl']))) { + $input_errors[] = gettext("A Firmware Auto Update Base URL must be specified when \"Use an unofficial server for firmware upgrades\" is enabled."); + } + + if (!$input_errors) { + if ($_POST['alturlenable'] == "yes") { + $config['system']['firmware']['alturl']['enable'] = true; + $config['system']['firmware']['alturl']['firmwareurl'] = $_POST['firmwareurl']; + } else { + unset($config['system']['firmware']['alturl']['enable']); + unset($config['system']['firmware']['alturl']['firmwareurl']); + unset($config['system']['firmware']['alturl']); + unset($config['system']['firmware']); + } + if ($_POST['allowinvalidsig'] == "yes") { + $config['system']['firmware']['allowinvalidsig'] = true; + } else { + unset($config['system']['firmware']['allowinvalidsig']); + } + + if ($_POST['disablecheck'] == "yes") { + $config['system']['firmware']['disablecheck'] = true; + } else { + unset($config['system']['firmware']['disablecheck']); + } + + if ($_POST['synconupgrade'] == "yes") { + $config['system']['gitsync']['synconupgrade'] = true; + } else { + unset($config['system']['gitsync']['synconupgrade']); + } + $config['system']['gitsync']['repositoryurl'] = $_POST['repositoryurl']; + $config['system']['gitsync']['branch'] = $_POST['branch']; + + write_config(); + } +} + +$curcfg = $config['system']['firmware']; +$gitcfg = $config['system']['gitsync']; + +$pgtitle = array(gettext("System"), gettext("Firmware"), gettext("Settings")); +$closehead = false; + +exec("/usr/bin/fetch -q -o {$g['tmp_path']}/manifest \"{$g['update_manifest']}\""); +if (file_exists("{$g['tmp_path']}/manifest")) { + $preset_urls_split = explode("\n", file_get_contents("{$g['tmp_path']}/manifest")); +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +$tab_array = array(); +$tab_array[] = array(gettext("Manual Update"), false, "system_firmware.php"); +$tab_array[] = array(gettext("Auto Update"), false, "system_firmware_check.php"); +$tab_array[] = array(gettext("Updater Settings"), true, "system_firmware_settings.php"); + +if($g['hidedownloadbackup'] == false) + $tab_array[] = array(gettext("Restore Full Backup"), false, "system_firmware_restorefullbackup.php"); + +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Firmware Branch'); + +if(is_array($preset_urls_split)) { + $urllist = array(); + + foreach($preset_urls_split as $pus) { + $pus_text = explode("\t", $pus); + if (empty($pus_text[0])) + continue; + if (stristr($pus_text[0], php_uname("m")) !== false) { + $yourarch = " (Current architecture)"; + $choice = $pus_text[1]; + } else { + $yourarch = ""; + } + + $urllist[$pus_text[1]] = $pus_text[0] . $yourarch; + } + + $section->addInput(new Form_Select( + 'preseturls', + 'Default Auto Update URLs', + $choice, + $urllist + ))->setHelp('Entries denoted by "Current architecture" match the architecture of your current installation, ' . + 'such as %s. Changing architectures during an upgrade is not recommended, and may require a manual reboot after the update completes.', [php_uname("m")]); + + $form->add($section); +} + +$section = new Form_Section('Firmware Auto Update URL'); + +$section->addInput(new Form_Checkbox( + 'alturlenable', + 'Unofficial', + 'Allow the use of an "unofficial" server for firmware upgrades', + isset($curcfg['alturl']['enable']) + )); + +$section->addInput(new Form_Input( + 'firmwareurl', + 'Base URL', + 'text' + ))->setHelp('This is where %s will check for newer firmware versions when the <a href="system_firmware_check.php">' . + 'System: Firmware: Auto Update</a> page is viewed', [$g['product_name']]); + +$form->add($section); + +$section = new Form_Section('Updates'); + +$section->addInput(new Form_Checkbox( + 'allowinvalidsig', + 'Unsigned images', + 'Allow auto-update firmware images with a missing or invalid digital signature to be used', + isset($curcfg['allowinvalidsig']) + )); + +$section->addInput(new Form_Checkbox( + 'disablecheck', + 'Dashboard check', + 'Disable the automatic dashboard auto-update check', + isset($curcfg['disablecheck']) + )); + +$form->add($section); + +if(file_exists("/usr/local/bin/git") && $g['platform'] == "pfSense") { + $section = new Form_Section('GitSync'); + + $section->addInput(new Form_Checkbox( + 'synconupgrade', + 'Auto sync on update', + 'After updating, sync with the following repository/branch before reboot', + isset($gitcfg['synconupgrade']) + ))->setHelp('After updating, sync with the following repository/branch before reboot'); + + if(is_dir("/root/pfsense/pfSenseGITREPO/pfSenseGITREPO")) { + exec("cd /root/pfsense/pfSenseGITREPO/pfSenseGITREPO && git config remote.origin.url", $output_str); + if(is_array($output_str) && !empty($output_str[0])) + $lastrepositoryurl = $output_str[0]; + unset($output_str); + } + + $section->addInput(new Form_Input( + 'repositoryurl', + 'Repository URL', + 'text', + ($gitcfg['repositoryurl'] ? $gitcfg['repositoryurl'] : '') + ))->setHelp('The most recently used repository was %s. This repository will be used if the field is left blank.', [$lastrepositoryurl]); + + if(is_dir("/root/pfsense/pfSenseGITREPO/pfSenseGITREPO")) { + exec("cd /root/pfsense/pfSenseGITREPO/pfSenseGITREPO && git branch", $output_str); + if(is_array($output_str)) { + foreach($output_str as $output_line) { + if(strstr($output_line, '* ')) { + $lastbranch = substr($output_line, 2); + break; + } + } + unset($output_str); + } + unset($output_str); + } + + $section->addInput(new Form_Input( + 'branch', + 'Branch name', + 'text', + ($gitcfg['branch'] ? $gitcfg['branch'] : '') + ))->setHelp('The most recently used branch was "%s". (Usually the branch name is master)' . + '<br />Note: Sync will not be performed if a branch is not specified', [$lastbranch]); + + $form->add($section); +} // e-o-if(file_exista() + +print($form); +?> + +<script> +// Update firmwareurl from preseturls +function update_firmwareurl() { + var pre = document.getElementById("preseturls"); + var preVal = pre.options[pre.selectedIndex].value; + var firm = document.getElementById("firmwareurl"); + firm.value = preVal; +} + +// Call it when preseturls changes +events.push(function(){ + $('#preseturls').on('change', function(){ + update_firmwareurl(); + }) +}); + +// And call it on page load +update_firmwareurl(); + +</script> +<?php + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_gateway_groups.php b/src/usr/local/www/system_gateway_groups.php new file mode 100644 index 0000000..7abe04c --- /dev/null +++ b/src/usr/local/www/system_gateway_groups.php @@ -0,0 +1,183 @@ +<?php +/* $Id$ */ +/* + system_gateway_groups.php + part of pfSense (https://www.pfsense.org) + + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-system-gatewaygroups +##|*NAME=System: Gateway Groups page +##|*DESCR=Allow access to the 'System: Gateway Groups' page. +##|*MATCH=system_gateway_groups.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("openvpn.inc"); + +if (!is_array($config['gateways']['gateway_group'])) { + $config['gateways']['gateway_group'] = array(); +} + +$a_gateway_groups = &$config['gateways']['gateway_group']; +$a_gateways = &$config['gateways']['gateway_item']; +$changedesc = gettext("Gateway Groups") . ": "; + +if ($_POST) { + + $pconfig = $_POST; + + if ($_POST['apply']) { + + $retval = 0; + + $retval = system_routing_configure(); + send_multiple_events(array("service reload dyndnsall", "service reload ipsecdns", "filter reload")); + + /* reconfigure our gateway monitor */ + setup_gateways_monitor(); + + $savemsg = get_std_save_message($retval); + if ($retval == 0) { + clear_subsystem_dirty('staticroutes'); + } + + foreach ($a_gateway_groups as $gateway_group) { + $gw_subsystem = 'gwgroup.' . $gateway_group['name']; + if (is_subsystem_dirty($gw_subsystem)) { + openvpn_resync_gwgroup($gateway_group['name']); + clear_subsystem_dirty($gw_subsystem); + } + } + } +} + +if ($_GET['act'] == "del") { + if ($a_gateway_groups[$_GET['id']]) { + $changedesc .= gettext("removed gateway group") . " {$_GET['id']}"; + foreach ($config['filter']['rule'] as $idx => $rule) { + if ($rule['gateway'] == $a_gateway_groups[$_GET['id']]['name']) { + unset($config['filter']['rule'][$idx]['gateway']); + } + } + + unset($a_gateway_groups[$_GET['id']]); + write_config($changedesc); + mark_subsystem_dirty('staticroutes'); + header("Location: system_gateway_groups.php"); + exit; + } +} + +$pgtitle = array(gettext("System"), gettext("Gateway Groups")); +$shortcut_section = "gateway-groups"; + +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('staticroutes')) + print_info_box_np(sprintf(gettext("The gateway configuration has been changed.%sYou must apply the changes in order for them to take effect."), "<br />")); + +$tab_array = array(); +$tab_array[] = array(gettext("Gateways"), false, "system_gateways.php"); +$tab_array[] = array(gettext("Routes"), false, "system_routes.php"); +$tab_array[] = array(gettext("Groups"), true, "system_gateway_groups.php"); +display_top_tabs($tab_array); +?> + +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Group Name")?></th> + <th><?=gettext("Gateways")?></th> + <th><?=gettext("Priority")?></th> + <th><?=gettext("Description")?></th> + <th><!-- Action Buttons --></th> + </tr> + </thead> + <tbody> +<?php +$i = 0; +foreach ($a_gateway_groups as $gateway_group): +?> + <tr> + <td> + <?=$gateway_group['name']?> + </td> + <td> +<?php + foreach($gateway_group['item'] as $item) { + $itemsplit = explode("|", $item); + print(htmlspecialchars(strtoupper($itemsplit[0])) . "<br />\n"); + } +?> + </td> + <td> +<?php + foreach($gateway_group['item'] as $item) { + $itemsplit = explode("|", $item); + print("Tier ". htmlspecialchars($itemsplit[1]) . "<br />\n"); + } +?> + </td> + <td> + <?=htmlspecialchars($gateway_group['descr'])?> + </td> + <td> + <a href="system_gateway_groups_edit.php?id=<?=$i?>" class="btn btn-xs btn-success"><?=gettext('Edit')?></a> + <a href="system_gateway_groups_edit.php?dup=<?=$i?>" class="btn btn-xs btn-info"><?=gettext('Duplicate')?></a> + <a href="system_gateway_groups.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger" ><?=gettext('Delete')?></a> + </td> + </tr> +<?php + $i++; +endforeach; +?> + </tbody> + </table> +</div> + +<nav class="action-buttons"> + <a href="system_gateway_groups_edit.php" class="btn btn-default"><?=gettext('Add')?></a> +</nav> + +<?php + print_info_box(gettext('Remember to use these Gateway Groups in firewall rules in order to enable load balancing, failover, ' . + 'or policy-based routing.' . '<br />' . + 'Without rules directing traffic into the Gateway Groups, they will not be used.')); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_gateway_groups_edit.php b/src/usr/local/www/system_gateway_groups_edit.php new file mode 100644 index 0000000..70d46e8 --- /dev/null +++ b/src/usr/local/www/system_gateway_groups_edit.php @@ -0,0 +1,356 @@ +<?php +/* $Id$ */ +/* + system_gateway_groups_edit.php + part of pfSense (https://www.pfsense.org) + + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-system-gateways-editgatewaygroups +##|*NAME=System: Gateways: Edit Gateway Groups page +##|*DESCR=Allow access to the 'System: Gateways: Edit Gateway Groups' page. +##|*MATCH=system_gateway_groups_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("ipsec.inc"); +require_once("vpn.inc"); + +if (!is_array($config['gateways']['gateway_group'])) + $config['gateways']['gateway_group'] = array(); + +$a_gateway_groups = &$config['gateways']['gateway_group']; +$a_gateways = return_gateways_array(); + +$categories = array( + 'down' => gettext("Member Down"), + 'downloss' => gettext("Packet Loss"), + 'downlatency' => gettext("High Latency"), + 'downlosslatency' => gettext("Packet Loss or High Latency")); + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + $id = $_GET['dup']; +} + +if (isset($id) && $a_gateway_groups[$id]) { + $pconfig['name'] = $a_gateway_groups[$id]['name']; + $pconfig['item'] = &$a_gateway_groups[$id]['item']; + $pconfig['descr'] = $a_gateway_groups[$id]['descr']; + $pconfig['trigger'] = $a_gateway_groups[$id]['trigger']; +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + unset($id); +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "name"); + $reqdfieldsn = explode(",", "Name"); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (!isset($_POST['name'])) { + $input_errors[] = gettext("A valid gateway group name must be specified."); + } + if (!is_validaliasname($_POST['name'])) { + $input_errors[] = gettext("The gateway name must not contain invalid characters."); + } + + if (isset($_POST['name'])) { + /* check for overlaps */ + if (is_array($a_gateway_groups)) { + foreach ($a_gateway_groups as $gateway_group) { + if (isset($id) && ($a_gateway_groups[$id]) && ($a_gateway_groups[$id] === $gateway_group)) { + if ($gateway_group['name'] != $_POST['name']) { + $input_errors[] = gettext("Changing name on a gateway group is not allowed."); + } + continue; + } + + if ($gateway_group['name'] == $_POST['name']) { + $input_errors[] = sprintf(gettext('A gateway group with this name "%s" already exists.'), $_POST['name']); + break; + } + } + } + } + + /* Build list of items in group with priority */ + $pconfig['item'] = array(); + foreach ($a_gateways as $gwname => $gateway) { + if ($_POST[$gwname] > 0) { + $vipname = "{$gwname}_vip"; + /* we have a priority above 0 (disabled), add item to list */ + $pconfig['item'][] = "{$gwname}|{$_POST[$gwname]}|{$_POST[$vipname]}"; + } + /* check for overlaps */ + if ($_POST['name'] == $gwname) { + $input_errors[] = sprintf(gettext('A gateway group cannot have the same name with a gateway "%s" please choose another name.'), $_POST['name']); + } + + } + if (count($pconfig['item']) == 0) { + $input_errors[] = gettext("No gateway(s) have been selected to be used in this group"); + } + + if (!$input_errors) { + $gateway_group = array(); + $gateway_group['name'] = $_POST['name']; + $gateway_group['item'] = $pconfig['item']; + $gateway_group['trigger'] = $_POST['trigger']; + $gateway_group['descr'] = $_POST['descr']; + + if (isset($id) && $a_gateway_groups[$id]) { + $a_gateway_groups[$id] = $gateway_group; + } else { + $a_gateway_groups[] = $gateway_group; + } + + mark_subsystem_dirty('staticroutes'); + mark_subsystem_dirty('gwgroup.' . $gateway_group['name']); + + write_config(); + + header("Location: system_gateway_groups.php"); + exit; + } +} + +$pgtitle = array(gettext("System"), gettext("Gateways"), gettext("Edit gateway group")); +$shortcut_section = "gateway-groups"; + +function build_gateway_protocol_map (&$a_gateways) { + $result = array(); + foreach ($a_gateways as $gwname => $gateway) { + $result[$gwname] = $gateway['ipprotocol']; + } + + return $result; +} + +function build_carp_list() { + global $carplist; + + $list = array('address' => gettext('Interface Address')); + + foreach($carplist as $vip => $address) { + if(($gateway['ipprotocol'] == "inet") && (!is_ipaddrv4($address))) + continue; + if(($gateway['ipprotocol'] == "inet6") && (!is_ipaddrv6($address))) + continue; + + $list[$vip] = "$vip - $address"; + } + + return($list); +} + +include("head.inc"); + +$gateway_protocol = build_gateway_protocol_map($a_gateways); +$gateway_array = array_keys($a_gateways); +$protocol_array = array_values($gateway_protocol); +$protocol_array = array_values(array_unique($gateway_protocol)); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Edit gateway group entry'); + +$section->addInput(new Form_Input( + 'nentries', + 'Group Name', + 'text' +)); + + +$carplist = get_configured_carp_interface_list($interface); +$row == 0; +$numrows = count($a_gateways) - 1; + +foreach($a_gateways as $gwname => $gateway) { + if(!empty($pconfig['item'])) { + $af = explode("|", $pconfig['item'][0]); + $family = $a_gateways[$af[0]]['ipprotocol']; + if($gateway['ipprotocol'] != $family) { + $rows++; + continue; + } + } + + $interface = $gateway['friendlyiface']; + $selected = array(); + + foreach((array)$pconfig['item'] as $item) { + $itemsplit = explode("|", $item); + if($itemsplit[0] == $gwname) { + $selected[$itemsplit[1]] = "selected=\"selected\""; + break; + } else { + $selected[0] = "selected=\"selected\""; + } + } + + $group = new Form_Group($row == 0 ? 'Gateway Priority':null); + $group->addClass($gateway['ipprotocol']); + + $group->add(new Form_Input( + 'gwname' . $row, + 'Group Name', + 'text', + $gateway['name'] + ))->setHelp($row == $numrows ? 'Gateway':null); + + $group->add(new Form_Select( + $gwname, + 'Tier', + isset($pconfig['filterdescriptions']) ? $pconfig['filterdescriptions']:'0', + array( + '0' => 'Never', + '1' => 'Tier 1', + '2' => 'Tier 2', + '3' => 'Tier 3', + '4' => 'Tier 4', + '5' => 'Tier 5' + ) + ))->setHelp($row == $numrows ? 'Tier':null)->addClass('row')->addClass($gateway['ipprotocol']); + + $group->add(new Form_Select( + $gwname . '_vip', + 'Virtual IP', + !isset($pconfig['filterdescriptions']) ? '0':$pconfig['filterdescriptions'], + build_carp_list() + ))->setHelp($row == $numrows ? 'Virtual IP':null); + + $group->add(new Form_Input( + 'nentries', + 'Group Name', + 'text', + $gateway['descr'] + ))->setWidth(3)->setHelp($row == $numrows ? 'Description':null); + + $section->add($group); + + $row++; +} // e-o-forwach + +$section->addInput(new Form_StaticText( + 'Link Priority', + 'The priority selected here defines in what order failover and balancing of links will be done. ' . + 'Multiple links of the same priority will balance connections until all links in the priority will be exhausted. ' . + 'If all links in a priority level are exhausted we will use the next available link(s) in the next priority level.' +)); + +$section->addInput(new Form_StaticText( + 'Virtual IP', + 'The virtual IP field selects which (virtual) IP should be used when this group applies to a local Dynamic DNS, IPsec or OpenVPN endpoint.' +)); + +$section->addInput(new Form_Select( + 'trigger', + 'Trigger Level', + $pconfig['trigger'], + array( + '0' => 'Member down', + '1' => 'Packet Loss', + '2' => 'High Latency', + '3' => 'Packet Loss or High latency' + ) +))->setHelp('When to trigger exclusion of a member'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +if (isset($id) && $a_gateway_groups[$id]){ + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->add($section); + +print($form); +?> + +<script type="text/javascript"> +//<![CDATA[ +events.push(function(){ + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // On changing a Tier selector on any row, find which protocol it uses (class) + // and disable the opposite + $('.row').on('change', function() { + // If user selects 'Never', unhide all rows + if($(this).find(":selected").index() == 0) { + hideClass('inet', false); + hideClass('inet6', false); + } + else { // Otherwise hide the rows that are ont of 'this' protocol + if($(this).hasClass('inet6')) + hideClass('inet', true); + else + hideClass('inet6', true); + } + }); +}); +//]]> +</script> + +<?php +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_gateways.php b/src/usr/local/www/system_gateways.php new file mode 100644 index 0000000..86786a4 --- /dev/null +++ b/src/usr/local/www/system_gateways.php @@ -0,0 +1,307 @@ +<?php +/* $Id$ */ +/* + system_gateways.php + part of pfSense (https://www.pfsense.org) + + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-system-gateways +##|*NAME=System: Gateways page +##|*DESCR=Allow access to the 'System: Gateways' page. +##|*MATCH=system_gateways.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +$a_gateways = return_gateways_array(true, false, true); +$a_gateways_arr = array(); +foreach ($a_gateways as $gw) { + $a_gateways_arr[] = $gw; +} +$a_gateways = $a_gateways_arr; + +if (!is_array($config['gateways']['gateway_item'])) { + $config['gateways']['gateway_item'] = array(); +} + +$a_gateway_item = &$config['gateways']['gateway_item']; + +if ($_POST) { + + $pconfig = $_POST; + + if ($_POST['apply']) { + + $retval = 0; + + $retval = system_routing_configure(); + $retval |= filter_configure(); + /* reconfigure our gateway monitor */ + setup_gateways_monitor(); + + $savemsg = get_std_save_message($retval); + if ($retval == 0) { + clear_subsystem_dirty('staticroutes'); + } + } +} + +function can_delete_disable_gateway_item($id, $disable = false) { + global $config, $input_errors, $a_gateways; + + if (!isset($a_gateways[$id])) { + return false; + } + + if (is_array($config['gateways']['gateway_group'])) { + foreach ($config['gateways']['gateway_group'] as $group) { + foreach ($group['item'] as $item) { + $items = explode("|", $item); + if ($items[0] == $a_gateways[$id]['name']) { + if (!$disable) { + $input_errors[] = sprintf(gettext("Gateway '%s' cannot be deleted because it is in use on Gateway Group '%s'"), $a_gateways[$id]['name'], $group['name']); + } else { + $input_errors[] = sprintf(gettext("Gateway '%s' cannot be disabled because it is in use on Gateway Group '%s'"), $a_gateways[$id]['name'], $group['name']); + } + } + } + } + } + + if (is_array($config['staticroutes']['route'])) { + foreach ($config['staticroutes']['route'] as $route) { + if ($route['gateway'] == $a_gateways[$id]['name']) { + if (!$disable) { + // The user wants to delete this gateway, but there is a static route (enabled or disabled) that refers to the gateway. + $input_errors[] = sprintf(gettext("Gateway '%s' cannot be deleted because it is in use on Static Route '%s'"), $a_gateways[$id]['name'], $route['network']); + } else if (!isset($route['disabled'])) { + // The user wants to disable this gateway. + // But there is a static route that uses this gateway and is enabled (not disabled). + $input_errors[] = sprintf(gettext("Gateway '%s' cannot be disabled because it is in use on Static Route '%s'"), $a_gateways[$id]['name'], $route['network']); + } + } + } + } + + if (isset($input_errors)) { + return false; + } + + return true; +} + +function delete_gateway_item($id) { + global $config, $a_gateways; + + if (!isset($a_gateways[$id])) { + return; + } + + /* NOTE: Cleanup static routes for the monitor ip if any */ + if (!empty($a_gateways[$id]['monitor']) && + $a_gateways[$id]['monitor'] != "dynamic" && + is_ipaddr($a_gateways[$id]['monitor']) && + $a_gateways[$id]['gateway'] != $a_gateways[$id]['monitor']) { + if (is_ipaddrv4($a_gateways[$id]['monitor'])) { + mwexec("/sbin/route delete " . escapeshellarg($a_gateways[$id]['monitor'])); + } else { + mwexec("/sbin/route delete -inet6 " . escapeshellarg($a_gateways[$id]['monitor'])); + } + } + + if ($config['interfaces'][$a_gateways[$id]['friendlyiface']]['gateway'] == $a_gateways[$id]['name']) { + unset($config['interfaces'][$a_gateways[$id]['friendlyiface']]['gateway']); + } + unset($config['gateways']['gateway_item'][$a_gateways[$id]['attribute']]); +} + +unset($input_errors); +if ($_GET['act'] == "del") { + if (can_delete_disable_gateway_item($_GET['id'])) { + $realid = $a_gateways[$_GET['id']]['attribute']; + delete_gateway_item($_GET['id']); + write_config("Gateways: removed gateway {$realid}"); + mark_subsystem_dirty('staticroutes'); + header("Location: system_gateways.php"); + exit; + } +} + +if (isset($_POST['del_x'])) { + /* delete selected items */ + if (is_array($_POST['rule']) && count($_POST['rule'])) { + foreach ($_POST['rule'] as $rulei) { + if (!can_delete_disable_gateway_item($rulei)) { + break; + } + } + + if (!isset($input_errors)) { + $items_deleted = ""; + foreach ($_POST['rule'] as $rulei) { + delete_gateway_item($rulei); + $items_deleted .= "{$rulei} "; + } + if (!empty($items_deleted)) { + write_config("Gateways: removed gateways {$items_deleted}"); + mark_subsystem_dirty('staticroutes'); + } + header("Location: system_gateways.php"); + exit; + } + } + +} else if ($_GET['act'] == "toggle" && $a_gateways[$_GET['id']]) { + $realid = $a_gateways[$_GET['id']]['attribute']; + $disable_gw = !isset($a_gateway_item[$realid]['disabled']); + if ($disable_gw) { + // The user wants to disable the gateway, so check if that is OK. + $ok_to_toggle = can_delete_disable_gateway_item($_GET['id'], $disable_gw); + } else { + // The user wants to enable the gateway. That is always OK. + $ok_to_toggle = true; + } + if ($ok_to_toggle) { + if ($disable_gw) { + $a_gateway_item[$realid]['disabled'] = true; + } else { + unset($a_gateway_item[$realid]['disabled']); + } + + if (write_config("Gateways: enable/disable")) { + mark_subsystem_dirty('staticroutes'); + } + + header("Location: system_gateways.php"); + exit; + } +} + +$pgtitle = array(gettext("System"), gettext("Gateways")); +$shortcut_section = "gateways"; + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); +if (is_subsystem_dirty('staticroutes')) + print_info_box_np(gettext("The gateway configuration has been changed.") . "<br />" . gettext("You must apply the changes in order for them to take effect.")); + +$tab_array = array(); +$tab_array[0] = array(gettext("Gateways"), true, "system_gateways.php"); +$tab_array[1] = array(gettext("Routes"), false, "system_routes.php"); +$tab_array[2] = array(gettext("Groups"), false, "system_gateway_groups.php"); +display_top_tabs($tab_array); + +?> +<table class="table"> +<thead> + <tr> + <th></th> + <th><?=gettext("Name")?></th> + <th><?=gettext("Interface")?></th> + <th><?=gettext("Gateway")?></th> + <th><?=gettext("Monitor IP")?></th> + <th><?=gettext("Description")?></th> + <th></th> + </tr> +</thead> +<tbody> +<?php +foreach ($a_gateways as $i => $gateway): + if (isset($gateway['inactive'])) + $icon = 'icon-remove-circle'; + elseif (isset($gateway['disabled'])) + $icon = 'icon-ban-circle'; + else + $icon = 'icon-ok-circle'; + + if (isset($gateway['inactive'])) + $title = gettext("This gateway is inactive because interface is missing"); + else + $title = ''; +?> + <tr<?=($icon != 'icon-ok-circle')? ' class="disabled"' : ''?>> + <td title="<?=$title?>"><i class="icon <?=$icon?>"></i></td> + <td> + <?=$gateway['name']?> +<?php + if (isset($gateway['defaultgw'])) + echo " <strong>(default)</strong>"; +?> + </td> + <td> + <?=htmlspecialchars(convert_friendly_interface_to_friendly_descr($gateway['friendlyiface']))?> + </td> + <td> + <?=$gateway['gateway']?> + </td> + <td> + <?=htmlspecialchars($gateway['monitor'])?> + </td> + <td> + <?=htmlspecialchars($gateway['descr'])?> + </td> + <td> + <a class="btn btn-xs btn-primary" href="system_gateways_edit.php?id=<?=$i?>"> + edit + </a> + <a class="btn btn-xs btn-default" href="system_gateways_edit.php?dup=<?=$i?>"> + copy + </a> +<? if (is_numeric($gateway['attribute'])): ?> + <a class="btn btn-xs btn-danger" href="system_gateways.php?act=del&id=<?=$i?>"> + delete + </a> + <a class="btn btn-xs btn-default" href="?act=toggle&id=<?=$i?>"> + toggle + </a> +<? endif?> + </td> + </tr> +<? endforeach?> +</tbody> +</table> + +<nav class="action-buttons"> + <a href="system_gateways_edit.php" role="button" class="btn btn-success"> + <?=gettext("edit default");?> + </a> +</nav> +<?php + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_gateways_edit.php b/src/usr/local/www/system_gateways_edit.php new file mode 100644 index 0000000..abc0870 --- /dev/null +++ b/src/usr/local/www/system_gateways_edit.php @@ -0,0 +1,921 @@ +<?php +/* $Id$ */ +/* + system_gateways_edit.php + part of pfSense (https://www.pfsense.org) + + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-system-gateways-editgateway +##|*NAME=System: Gateways: Edit Gateway page +##|*DESCR=Allow access to the 'System: Gateways: Edit Gateway' page. +##|*MATCH=system_gateways_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require("pkg-utils.inc"); + +if (isset($_POST['referer'])) { + $referer = $_POST['referer']; +} else { + $referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/system_gateways.php'); +} + +$a_gateways = return_gateways_array(true, false, true); +$a_gateways_arr = array(); +foreach ($a_gateways as $gw) { + $a_gateways_arr[] = $gw; +} +$a_gateways = $a_gateways_arr; + +if (!is_array($config['gateways']['gateway_item'])) { + $config['gateways']['gateway_item'] = array(); +} + +$a_gateway_item = &$config['gateways']['gateway_item']; +$apinger_default = return_apinger_defaults(); + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + $id = $_GET['dup']; +} + +if (isset($id) && $a_gateways[$id]) { + $pconfig = array(); + $pconfig['name'] = $a_gateways[$id]['name']; + $pconfig['weight'] = $a_gateways[$id]['weight']; + $pconfig['interval'] = $a_gateways[$id]['interval']; + $pconfig['avg_delay_samples'] = $a_gateways[$id]['avg_delay_samples']; + $pconfig['avg_delay_samples_calculated'] = isset($a_gateways[$id]['avg_delay_samples_calculated']); + $pconfig['avg_loss_samples'] = $a_gateways[$id]['avg_loss_samples']; + $pconfig['avg_loss_samples_calculated'] = isset($a_gateways[$id]['avg_loss_samples_calculated']); + $pconfig['avg_loss_delay_samples'] = $a_gateways[$id]['avg_loss_delay_samples']; + $pconfig['avg_loss_delay_samples_calculated'] = isset($a_gateways[$id]['avg_loss_delay_samples_calculated']); + $pconfig['interface'] = $a_gateways[$id]['interface']; + $pconfig['friendlyiface'] = $a_gateways[$id]['friendlyiface']; + $pconfig['ipprotocol'] = $a_gateways[$id]['ipprotocol']; + if (isset($a_gateways[$id]['dynamic'])) { + $pconfig['dynamic'] = true; + } + $pconfig['gateway'] = $a_gateways[$id]['gateway']; + $pconfig['defaultgw'] = isset($a_gateways[$id]['defaultgw']); + $pconfig['force_down'] = isset($a_gateways[$id]['force_down']); + $pconfig['latencylow'] = $a_gateways[$id]['latencylow']; + $pconfig['latencyhigh'] = $a_gateways[$id]['latencyhigh']; + $pconfig['losslow'] = $a_gateways[$id]['losslow']; + $pconfig['losshigh'] = $a_gateways[$id]['losshigh']; + $pconfig['down'] = $a_gateways[$id]['down']; + $pconfig['monitor'] = $a_gateways[$id]['monitor']; + $pconfig['monitor_disable'] = isset($a_gateways[$id]['monitor_disable']); + $pconfig['descr'] = $a_gateways[$id]['descr']; + $pconfig['attribute'] = $a_gateways[$id]['attribute']; + $pconfig['disabled'] = isset($a_gateways[$id]['disabled']); +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + unset($id); + unset($pconfig['attribute']); +} + +if (isset($id) && $a_gateways[$id]) { + $realid = $a_gateways[$id]['attribute']; +} + +if ($_POST) { + + unset($input_errors); + + /* input validation */ + $reqdfields = explode(" ", "name interface"); + $reqdfieldsn = array(gettext("Name"), gettext("Interface")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (!isset($_POST['name'])) { + $input_errors[] = "A valid gateway name must be specified."; + } + if (!is_validaliasname($_POST['name'])) { + $input_errors[] = gettext("The gateway name must not contain invalid characters."); + } else if (isset($_POST['disabled'])) { + // We have a valid gateway name that the user wants to mark as disabled. + // Check if the gateway name is used in any gateway group. + if (is_array($config['gateways']['gateway_group'])) { + foreach ($config['gateways']['gateway_group'] as $group) { + foreach ($group['item'] as $item) { + $items = explode("|", $item); + if ($items[0] == $_POST['name']) { + $input_errors[] = sprintf(gettext("Gateway '%s' cannot be disabled because it is in use on Gateway Group '%s'"), $_POST['name'], $group['name']); + } + } + } + } + + // Check if the gateway name is used in any enabled Static Route. + if (is_array($config['staticroutes']['route'])) { + foreach ($config['staticroutes']['route'] as $route) { + if ($route['gateway'] == $_POST['name']) { + if (!isset($route['disabled'])) { + // There is a static route that uses this gateway and is enabled (not disabled). + $input_errors[] = sprintf(gettext("Gateway '%s' cannot be disabled because it is in use on Static Route '%s'"), $_POST['name'], $route['network']); + } + } + } + } + } + /* skip system gateways which have been automatically added */ + if (($_POST['gateway'] && (!is_ipaddr($_POST['gateway'])) && ($_POST['attribute'] !== "system")) && ($_POST['gateway'] != "dynamic")) { + $input_errors[] = gettext("A valid gateway IP address must be specified."); + } + + if ($_POST['gateway'] && (is_ipaddr($_POST['gateway'])) && !$_REQUEST['isAjax']) { + if (is_ipaddrv4($_POST['gateway'])) { + $parent_ip = get_interface_ip($_POST['interface']); + $parent_sn = get_interface_subnet($_POST['interface']); + if (empty($parent_ip) || empty($parent_sn)) { + $input_errors[] = gettext("Cannot add IPv4 Gateway Address because no IPv4 address could be found on the interface."); + } else { + $subnets = array(gen_subnet($parent_ip, $parent_sn) . "/" . $parent_sn); + $vips = link_interface_to_vips($_POST['interface']); + if (is_array($vips)) { + foreach ($vips as $vip) { + if (!is_ipaddrv4($vip['subnet'])) { + continue; + } + $subnets[] = gen_subnet($vip['subnet'], $vip['subnet_bits']) . "/" . $vip['subnet_bits']; + } + } + + $found = false; + foreach ($subnets as $subnet) { + if (ip_in_subnet($_POST['gateway'], $subnet)) { + $found = true; + break; + } + } + + if ($found === false) { + $input_errors[] = sprintf(gettext("The gateway address %1\$s does not lie within one of the chosen interface's subnets."), $_POST['gateway']); + } + } + } else if (is_ipaddrv6($_POST['gateway'])) { + /* do not do a subnet match on a link local address, it's valid */ + if (!is_linklocal($_POST['gateway'])) { + $parent_ip = get_interface_ipv6($_POST['interface']); + $parent_sn = get_interface_subnetv6($_POST['interface']); + if (empty($parent_ip) || empty($parent_sn)) { + $input_errors[] = gettext("Cannot add IPv6 Gateway Address because no IPv6 address could be found on the interface."); + } else { + $subnets = array(gen_subnetv6($parent_ip, $parent_sn) . "/" . $parent_sn); + $vips = link_interface_to_vips($_POST['interface']); + if (is_array($vips)) { + foreach ($vips as $vip) { + if (!is_ipaddrv6($vip['subnet'])) { + continue; + } + $subnets[] = gen_subnetv6($vip['subnet'], $vip['subnet_bits']) . "/" . $vip['subnet_bits']; + } + } + + $found = false; + foreach ($subnets as $subnet) { + if (ip_in_subnet($_POST['gateway'], $subnet)) { + $found = true; + break; + } + } + + if ($found === false) { + $input_errors[] = sprintf(gettext("The gateway address %1\$s does not lie within one of the chosen interface's subnets."), $_POST['gateway']); + } + } + } + } + + if (!empty($config['interfaces'][$_POST['interface']]['ipaddr'])) { + if (is_ipaddr($config['interfaces'][$_POST['interface']]['ipaddr']) && (empty($_POST['gateway']) || $_POST['gateway'] == "dynamic")) { + $input_errors[] = gettext("Dynamic gateway values cannot be specified for interfaces with a static IPv4 configuration."); + } + } + if (!empty($config['interfaces'][$_POST['interface']]['ipaddrv6'])) { + if (is_ipaddr($config['interfaces'][$_POST['interface']]['ipaddrv6']) && (empty($_POST['gateway']) || $_POST['gateway'] == "dynamic")) { + $input_errors[] = gettext("Dynamic gateway values cannot be specified for interfaces with a static IPv6 configuration."); + } + } + } + if (($_POST['monitor'] != "") && !is_ipaddr($_POST['monitor']) && $_POST['monitor'] != "dynamic") { + $input_errors[] = gettext("A valid monitor IP address must be specified."); + } + /* only allow correct IPv4 and IPv6 gateway addresses */ + if (($_POST['gateway'] <> "") && is_ipaddr($_POST['gateway']) && $_POST['gateway'] != "dynamic") { + if (is_ipaddrv6($_POST['gateway']) && ($_POST['ipprotocol'] == "inet")) { + $input_errors[] = gettext("The IPv6 gateway address '{$_POST['gateway']}' can not be used as a IPv4 gateway'."); + } + if (is_ipaddrv4($_POST['gateway']) && ($_POST['ipprotocol'] == "inet6")) { + $input_errors[] = gettext("The IPv4 gateway address '{$_POST['gateway']}' can not be used as a IPv6 gateway'."); + } + } + /* only allow correct IPv4 and IPv6 monitor addresses */ + if (($_POST['monitor'] <> "") && is_ipaddr($_POST['monitor']) && $_POST['monitor'] != "dynamic") { + if (is_ipaddrv6($_POST['monitor']) && ($_POST['ipprotocol'] == "inet")) { + $input_errors[] = gettext("The IPv6 monitor address '{$_POST['monitor']}' can not be used on a IPv4 gateway'."); + } + if (is_ipaddrv4($_POST['monitor']) && ($_POST['ipprotocol'] == "inet6")) { + $input_errors[] = gettext("The IPv4 monitor address '{$_POST['monitor']}' can not be used on a IPv6 gateway'."); + } + } + + if (isset($_POST['name'])) { + /* check for overlaps */ + foreach ($a_gateways as $gateway) { + if (isset($id) && ($a_gateways[$id]) && ($a_gateways[$id] === $gateway)) { + if ($gateway['name'] != $_POST['name']) { + $input_errors[] = gettext("Changing name on a gateway is not allowed."); + } + continue; + } + if ($_POST['name'] <> "") { + if (($gateway['name'] <> "") && ($_POST['name'] == $gateway['name']) && ($gateway['attribute'] !== "system")) { + $input_errors[] = sprintf(gettext('The gateway name "%s" already exists.'), $_POST['name']); + break; + } + } + if (is_ipaddr($_POST['gateway'])) { + if (($gateway['gateway'] <> "") && ($_POST['gateway'] == $gateway['gateway']) && ($gateway['attribute'] !== "system")) { + $input_errors[] = sprintf(gettext('The gateway IP address "%s" already exists.'), $_POST['gateway']); + break; + } + } + if (is_ipaddr($_POST['monitor'])) { + if (($gateway['monitor'] <> "") && ($_POST['monitor'] == $gateway['monitor']) && ($gateway['attribute'] !== "system")) { + $input_errors[] = sprintf(gettext('The monitor IP address "%s" is already in use. You must choose a different monitor IP.'), $_POST['monitor']); + break; + } + } + } + } + + /* input validation of apinger advanced parameters */ + if ($_POST['latencylow']) { + if (!is_numeric($_POST['latencylow'])) { + $input_errors[] = gettext("The low latency threshold needs to be a numeric value."); + } else { + if ($_POST['latencylow'] < 1) { + $input_errors[] = gettext("The low latency threshold needs to be positive."); + } + } + } + + if ($_POST['latencyhigh']) { + if (!is_numeric($_POST['latencyhigh'])) { + $input_errors[] = gettext("The high latency threshold needs to be a numeric value."); + } else { + if ($_POST['latencyhigh'] < 1) { + $input_errors[] = gettext("The high latency threshold needs to be positive."); + } + } + } + + if ($_POST['losslow']) { + if (!is_numeric($_POST['losslow'])) { + $input_errors[] = gettext("The low Packet Loss threshold needs to be a numeric value."); + } else { + if ($_POST['losslow'] < 1) { + $input_errors[] = gettext("The low Packet Loss threshold needs to be positive."); + } + if ($_POST['losslow'] >= 100) { + $input_errors[] = gettext("The low Packet Loss threshold needs to be less than 100."); + } + } + } + + if ($_POST['losshigh']) { + if (!is_numeric($_POST['losshigh'])) { + $input_errors[] = gettext("The high Packet Loss threshold needs to be a numeric value."); + } else { + if ($_POST['losshigh'] < 1) { + $input_errors[] = gettext("The high Packet Loss threshold needs to be positive."); + } + if ($_POST['losshigh'] > 100) { + $input_errors[] = gettext("The high Packet Loss threshold needs to be 100 or less."); + } + } + } + + if (($_POST['latencylow']) && ($_POST['latencyhigh'])) { + if ((is_numeric($_POST['latencylow'])) && (is_numeric($_POST['latencyhigh']))) { + if (($_POST['latencylow'] > $_POST['latencyhigh'])) { + $input_errors[] = gettext("The high latency threshold needs to be higher than the low latency threshold"); + } + } + } else { + if ($_POST['latencylow']) { + if (is_numeric($_POST['latencylow'])) { + if ($_POST['latencylow'] > $apinger_default['latencyhigh']) { + $input_errors[] = gettext(sprintf("The low latency threshold needs to be less than the default high latency threshold (%d)", $apinger_default['latencyhigh'])); + } + } + } + if ($_POST['latencyhigh']) { + if (is_numeric($_POST['latencyhigh'])) { + if ($_POST['latencyhigh'] < $apinger_default['latencylow']) { + $input_errors[] = gettext(sprintf("The high latency threshold needs to be higher than the default low latency threshold (%d)", $apinger_default['latencylow'])); + } + } + } + } + + if (($_POST['losslow']) && ($_POST['losshigh'])) { + if ((is_numeric($_POST['losslow'])) && (is_numeric($_POST['losshigh']))) { + if ($_POST['losslow'] > $_POST['losshigh']) { + $input_errors[] = gettext("The high Packet Loss threshold needs to be higher than the low Packet Loss threshold"); + } + } + } else { + if ($_POST['losslow']) { + if (is_numeric($_POST['losslow'])) { + if ($_POST['losslow'] > $apinger_default['losshigh']) { + $input_errors[] = gettext(sprintf("The low Packet Loss threshold needs to be less than the default high Packet Loss threshold (%d)", $apinger_default['losshigh'])); + } + } + } + if ($_POST['losshigh']) { + if (is_numeric($_POST['losshigh'])) { + if ($_POST['losshigh'] < $apinger_default['losslow']) { + $input_errors[] = gettext(sprintf("The high Packet Loss threshold needs to be higher than the default low Packet Loss threshold (%d)", $apinger_default['losslow'])); + } + } + } + } + + if ($_POST['interval']) { + if (!is_numeric($_POST['interval'])) { + $input_errors[] = gettext("The probe interval needs to be a numeric value."); + } else { + if ($_POST['interval'] < 1) { + $input_errors[] = gettext("The probe interval needs to be positive."); + } + } + } + + if ($_POST['down']) { + if (!is_numeric($_POST['down'])) { + $input_errors[] = gettext("The down time setting needs to be a numeric value."); + } else { + if ($_POST['down'] < 1) { + $input_errors[] = gettext("The down time setting needs to be positive."); + } + } + } + + if (($_POST['interval']) && ($_POST['down'])) { + if ((is_numeric($_POST['interval'])) && (is_numeric($_POST['down']))) { + if ($_POST['interval'] > $_POST['down']) { + $input_errors[] = gettext("The probe interval needs to be less than the down time setting."); + } + } + } else { + if ($_POST['interval']) { + if (is_numeric($_POST['interval'])) { + if ($_POST['interval'] > $apinger_default['down']) { + $input_errors[] = gettext(sprintf("The probe interval needs to be less than the default down time setting (%d)", $apinger_default['down'])); + } + } + } + if ($_POST['down']) { + if (is_numeric($_POST['down'])) { + if ($_POST['down'] < $apinger_default['interval']) { + $input_errors[] = gettext(sprintf("The down time setting needs to be higher than the default probe interval (%d)", $apinger_default['interval'])); + } + } + } + } + + if ($_POST['avg_delay_samples']) { + if (!is_numeric($_POST['avg_delay_samples'])) { + $input_errors[] = gettext("The average delay replies qty needs to be a numeric value."); + } else { + if ($_POST['avg_delay_samples'] < 1) { + $input_errors[] = gettext("The average delay replies qty needs to be positive."); + } + } + } + + if ($_POST['avg_loss_samples']) { + if (!is_numeric($_POST['avg_loss_samples'])) { + $input_errors[] = gettext("The average packet loss probes qty needs to be a numeric value."); + } else { + if ($_POST['avg_loss_samples'] < 1) { + $input_errors[] = gettext("The average packet loss probes qty needs to be positive."); + } + } + } + + if ($_POST['avg_loss_delay_samples']) { + if (!is_numeric($_POST['avg_loss_delay_samples'])) { + $input_errors[] = gettext("The lost probe delay needs to be a numeric value."); + } else { + if ($_POST['avg_loss_delay_samples'] < 1) { + $input_errors[] = gettext("The lost probe delay needs to be positive."); + } + } + } + + if (!$input_errors) { + $reloadif = ""; + $gateway = array(); + + if (empty($_POST['interface'])) { + $gateway['interface'] = $pconfig['friendlyiface']; + } else { + $gateway['interface'] = $_POST['interface']; + } + if (is_ipaddr($_POST['gateway'])) { + $gateway['gateway'] = $_POST['gateway']; + } else { + $gateway['gateway'] = "dynamic"; + } + $gateway['name'] = $_POST['name']; + $gateway['weight'] = $_POST['weight']; + $gateway['ipprotocol'] = $_POST['ipprotocol']; + $gateway['interval'] = $_POST['interval']; + + $gateway['avg_delay_samples'] = $_POST['avg_delay_samples']; + if ($_POST['avg_delay_samples_calculated'] == "yes" || $_POST['avg_delay_samples_calculated'] == "on") { + $gateway['avg_delay_samples_calculated'] = true; + } + + $gateway['avg_loss_samples'] = $_POST['avg_loss_samples']; + if ($_POST['avg_loss_samples_calculated'] == "yes" || $_POST['avg_loss_samples_calculated'] == "on") { + $gateway['avg_loss_samples_calculated'] = true; + } + + $gateway['avg_loss_delay_samples'] = $_POST['avg_loss_delay_samples']; + if ($_POST['avg_loss_delay_samples_calculated'] == "yes" || $_POST['avg_loss_delay_samples_calculated'] == "on") { + $gateway['avg_loss_delay_samples_calculated'] = true; + } + + $gateway['descr'] = $_POST['descr']; + if ($_POST['monitor_disable'] == "yes") { + $gateway['monitor_disable'] = true; + } + if ($_POST['force_down'] == "yes") { + $gateway['force_down'] = true; + } + if (is_ipaddr($_POST['monitor'])) { + $gateway['monitor'] = $_POST['monitor']; + } + + /* NOTE: If monitor ip is changed need to cleanup the old static route */ + if ($_POST['monitor'] != "dynamic" && !empty($a_gateway_item[$realid]) && is_ipaddr($a_gateway_item[$realid]['monitor']) && + $_POST['monitor'] != $a_gateway_item[$realid]['monitor'] && $gateway['gateway'] != $a_gateway_item[$realid]['monitor']) { + if (is_ipaddrv4($a_gateway_item[$realid]['monitor'])) { + mwexec("/sbin/route delete " . escapeshellarg($a_gateway_item[$realid]['monitor'])); + } else { + mwexec("/sbin/route delete -inet6 " . escapeshellarg($a_gateway_item[$realid]['monitor'])); + } + } + + if ($_POST['defaultgw'] == "yes" || $_POST['defaultgw'] == "on") { + $i = 0; + /* remove the default gateway bits for all gateways with the same address family */ + foreach ($a_gateway_item as $gw) { + if ($gateway['ipprotocol'] == $gw['ipprotocol']) { + unset($config['gateways']['gateway_item'][$i]['defaultgw']); + if ($gw['interface'] != $_POST['interface'] && $gw['defaultgw']) { + $reloadif = $gw['interface']; + } + } + $i++; + } + $gateway['defaultgw'] = true; + } + + if ($_POST['latencylow']) { + $gateway['latencylow'] = $_POST['latencylow']; + } + if ($_POST['latencyhigh']) { + $gateway['latencyhigh'] = $_POST['latencyhigh']; + } + if ($_POST['losslow']) { + $gateway['losslow'] = $_POST['losslow']; + } + if ($_POST['losshigh']) { + $gateway['losshigh'] = $_POST['losshigh']; + } + if ($_POST['down']) { + $gateway['down'] = $_POST['down']; + } + + if (isset($_POST['disabled'])) { + $gateway['disabled'] = true; + } else { + unset($gateway['disabled']); + } + + /* when saving the manual gateway we use the attribute which has the corresponding id */ + if (isset($realid) && $a_gateway_item[$realid]) { + $a_gateway_item[$realid] = $gateway; + } else { + $a_gateway_item[] = $gateway; + } + + mark_subsystem_dirty('staticroutes'); + + write_config(); + + if ($_REQUEST['isAjax']) { + echo $_POST['name']; + exit; + } else if (!empty($reloadif)) { + send_event("interface reconfigure {$reloadif}"); + } + + header("Location: system_gateways.php"); + exit; + } else { + if ($_REQUEST['isAjax']) { + header("HTTP/1.0 500 Internal Server Error"); + header("Content-type: text/plain"); + foreach ($input_errors as $error) { + echo("$error\n"); + } + exit; + } + + $pconfig = $_POST; + if (empty($_POST['friendlyiface'])) { + $pconfig['friendlyiface'] = $_POST['interface']; + } + } +} + + +$pgtitle = array(gettext("System"), gettext("Gateways"), gettext("Edit gateway")); +$shortcut_section = "gateways"; + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +?> +<script> +var systemGatewaysEditRecalculate = function(){ + var interval = $('#interval')[0].value; + + $('input[name$=_calculated]').each(function (idx, c){ + c = $(c); + i = $(c.parents('.form-group').find('input[type=number]')[0]); + + c.prop('disabled', interval==0); + + if (interval==0) + c.prop('checked', false); + + if (!c.prop('checked')) + return i.prop('readonly', false); + + switch (i.attr('name')) + { + case 'avg_delay_samples': + // How many replies should be used to compute average delay + // for controlling "delay" alarms. + // Calculate a reasonable value based on gateway probe interval and RRD 1 minute average graph step size (60). + i.attr('value', Math.round(60 * (1/6) / Math.pow(interval, 0.333))); + break; + + case 'avg_loss_samples': + // How many probes should be used to compute average loss. + // Calculate a reasonable value based on gateway probe interval and RRD 1 minute average graph step size (60). + i.attr('value', Math.round(60 / interval)); + break; + + case 'avg_loss_delay_samples': + // The delay (in samples) after which loss is computed + // without this delays larger than interval would be treated as loss. + // Calculate a reasonable value based on gateway probe interval and RRD 1 minute average graph step size (60). + i.attr('value', Math.round(60 * (1/3) / interval)); + break; + } + + i.prop('readonly', true); + }); +}; + +events.push(function(){ + $('#interval').on('change', systemGatewaysEditRecalculate); + $('input[name$=_calculated]').on('change', systemGatewaysEditRecalculate); + + systemGatewaysEditRecalculate(); +}); +</script> +<?php + +require('classes/Form.class.php'); +$form = new Form; + +/* If this is a system gateway we need this var */ +if(($pconfig['attribute'] == "system") || is_numeric($pconfig['attribute'])) { + $form->addGlobal(new Form_Input( + 'attribute', + null, + 'hidden', + $pconfig['attribute'] + )); +} + +if (isset($id) && $a_gateways[$id]) { + $form->addGlobal(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$form->addGlobal(new Form_Input( + 'friendlyiface', + null, + 'hidden', + $pconfig['friendlyiface'] +)); + +$section = new Form_Section('Edit gateway'); + +$section->addInput(new Form_Checkbox( + 'disabled', + 'Disabled', + 'Disable this gateway', + $pconfig['disabled'] +))->setHelp('Set this option to disable this gateway without removing it from the '. + 'list.'); + +$section->addInput(new Form_Select( + 'interface', + 'Interface', + $pconfig['friendlyiface'], + get_configured_interface_with_descr(false, true) +))->setHelp('Choose which interface this gateway applies to.'); + +$section->addInput(new Form_Select( + 'ipprotocol', + 'Address Family', + $pconfig['ipprotocol'], + array( + "inet" => "IPv4", + "inet6" => "IPv6" + ) +))->setHelp('Choose the Internet Protocol this gateway uses.'); + +$section->addInput(new Form_Input( + 'name', + 'Name', + 'text', + $pconfig['name'] +))->setHelp('Gateway name'); + +$section->addInput(new Form_Input( + 'gateway', + 'Gateway', + 'text', + ($pconfig['dynamic'] ? 'dynamic' : $pconfig['gateway']) +))->setHelp('Gateway IP address'); + +$section->addInput(new Form_Checkbox( + 'defaultgw', + 'Default Gateway', + 'This will select the above gateway as the default gateway', + $pconfig['defaultgw'] +)); + +$section->addInput(new Form_Checkbox( + 'monitor_disable', + 'Gateway Monitoring', + 'Disable Gateway Monitoring', + $pconfig['monitor_disable'] +))->toggles('.toggle-monitor-ip')->setHelp('This will consider this gateway as always being up'); + +$group = new Form_Group('Monitor IP'); +$group->addClass('toggle-monitor-ip', 'collapse'); + +if (!$pconfig['monitor_disable']) + $group->addClass('in'); + +$group->add(new Form_Input( + 'monitor', + null, + 'text', + ($pconfig['gateway'] == $pconfig['monitor'] ? '' : $pconfig['monitor']) +))->setHelp('Enter an alternative address here to be '. + 'used to monitor the link. This is used for the quality RRD graphs as well as the '. + 'load balancer entries. Use this if the gateway does not respond to ICMP echo '. + 'requests (pings).'); +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'force_down', + 'Force state', + 'Mark Gateway as Down', + $pconfig['force_down'] +))->setHelp('This will force this gateway to be considered Down'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$form->add($section); +$section = new Form_Section('Advanced'); + +$section->addInput(new Form_Select( + 'weight', + 'Weight', + $pconfig['weight'], + array_combine(range(1, 5), range(1, 5)) +))->setHelp('Weight for this gateway when used in a Gateway Group.'); + +$group = new Form_Group('Latency thresholds'); +$group->add(new Form_Input( + 'latencylow', + 'From', + 'number', + $pconfig['latencylow'], + ['placeholder' => $apinger_default['latencylow']] +)); +$group->add(new Form_Input( + 'latencyhigh', + 'To', + 'number', + $pconfig['latencyhigh'], + ['placeholder' => $apinger_default['latencyhigh']] +)); +$group->setHelp('Low and high thresholds for latency in milliseconds. + Default is %d/%d.', [$apinger_default['latencylow'], $apinger_default['latencyhigh']]); + +$section->add($group); + +$group = new Form_Group('Packet Loss thresholds'); +$group->add(new Form_Input( + 'losslow', + 'From', + 'number', + $pconfig['losslow'], + ['placeholder' => $apinger_default['losslow']] +)); +$group->add(new Form_Input( + 'losshigh', + 'To', + 'number', + $pconfig['losshigh'], + ['placeholder' => $apinger_default['losshigh']] +)); +$group->setHelp('Low and high thresholds for packet loss in milliseconds. + Default is %d/%d.', [$apinger_default['losslow'], $apinger_default['losshigh']]); +$section->add($group); + +$section->addInput(new Form_Input( + 'interval', + 'Probe Interval', + 'number', + $pconfig['interval'], + [ + 'placeholder' => $apinger_default['interval'], + 'max' => 86400 + ] +))->setHelp('How often an ICMP probe will be sent in seconds. Default is %d.'. + 'NOTE: The quality graph is averaged over seconds, not intervals, so as '. + 'the probe interval is increased the accuracy of the quality graph is '. + 'decreased.', [$apinger_default['interval']]); + +$section->addInput(new Form_Input( + 'down', + 'Down', + 'number', + $pconfig['down'], + ['placeholder' => $apinger_default['down']] +))->setHelp('The number of seconds of failed probes before the alarm '. + 'will fire. Default is %d.', [$apinger_default['down']]); + +$group = new Form_Group('Avg. Delay Replies Qty'); +$group->add(new Form_Input( + 'avg_delay_samples', + null, + 'number', + $pconfig['avg_delay_samples'], + [ + 'placeholder' => $apinger_default['avg_delay_samples'], + 'max' => 100 + ] +)); +$group->add(new Form_Checkbox( + 'avg_delay_samples_calculated', + null, + 'Use calculated value.', + $pconfig['avg_delay_samples_calculated'] +)); +$group->setHelp('How many replies should be used to compute average delay for '. + 'controlling "delay" alarms? Default is %d.', + [$apinger_default['avg_delay_samples']]); +$section->add($group); + +$group = new Form_Group('Avg. Packet Loss Probes'); +$group->add(new Form_Input( + 'avg_loss_samples', + null, + 'number', + $pconfig['avg_loss_samples'], + [ + 'placeholder' => $apinger_default['avg_loss_samples'], + 'max' => 1000 + ] +)); +$group->add(new Form_Checkbox( + 'avg_loss_samples_calculated', + null, + 'Use calculated value.', + $pconfig['avg_loss_samples_calculated'] +)); +$group->setHelp('How many probes should be useds to compute average packet loss? '. + 'Default is %d.', + [$apinger_default['avg_loss_samples']]); +$section->add($group); + +$group = new Form_Group('Lost Probe Delay'); +$group->add(new Form_Input( + 'avg_loss_delay_samples', + null, + 'number', + $pconfig['avg_loss_delay_samples'], + [ + 'placeholder' => $apinger_default['avg_loss_delay_samples'], + 'max' => 200 + ] +)); +$group->add(new Form_Checkbox( + 'avg_loss_delay_samples_calculated', + null, + 'Use calculated value.', + $pconfig['avg_loss_samples_calculated'] +)); +$group->setHelp('The delay (in qty of probe samples) after which loss is '. + 'computed. Without this, delays longer than the probe interval would be '. + 'treated as packet loss. Default is %d.', + [$apinger_default['avg_loss_delay_samples']]); +$section->add($group); + +$section->addInput(new Form_StaticText( + 'Additional information', + '<span class="help-block">'. + gettext('The probe interval must be less than the down time, otherwise the '. + 'gateway will seem to go down then come up again at the next probe.'). + '<br/><br/>'. + gettext('The down time defines the length of time before the gateway is marked '. + 'as down, but the accuracy is controlled by the probe interval. For example, '. + 'if your down time is 40 seconds but on a 30 second probe interval, only one '. + 'probe would have to fail before the gateway is marked down at the 40 second '. + 'mark. By default, the gateway is considered down after 10 seconds, and the '. + 'probe interval is 1 second, so 10 probes would have to fail before the gateway '. + 'is marked down.'). + '</span>' +)); + +$form->add($section); + +print $form; +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_groupmanager.php b/src/usr/local/www/system_groupmanager.php new file mode 100644 index 0000000..77384e3 --- /dev/null +++ b/src/usr/local/www/system_groupmanager.php @@ -0,0 +1,377 @@ +<?php +/* + $Id: system_groupmanager.php + part of m0n0wall (http://m0n0.ch/wall) + part of pfSense + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Copyright (C) 2008 Shrew Soft Inc. + All rights reserved. + + Copyright (C) 2005 Paul Taylor <paultaylor@winn-dixie.com>. + All rights reserved. + + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: auth +*/ + +##|+PRIV +##|*IDENT=page-system-groupmanager +##|*NAME=System: Group manager page +##|*DESCR=Allow access to the 'System: Group manager' page. +##|*MATCH=system_groupmanager.php* +##|-PRIV + +require("guiconfig.inc"); + +$pgtitle = array(gettext("System"), gettext("Group manager")); + +if (!is_array($config['system']['group'])) { + $config['system']['group'] = array(); +} + +$a_group = &$config['system']['group']; + +unset($id); +if (isset($_POST['groupid']) && is_numericint($_POST['groupid'])) { + $id = $_POST['groupid']; +} + +$act = (isset($_POST['act']) ? $_POST['act'] : ''); + +if ($act == "delgroup") { + + if (!isset($id) || !isset($_POST['groupname']) || !isset($a_group[$id]) || ($_POST['groupname'] != $a_group[$id]['name'])) { + pfSenseHeader("system_groupmanager.php"); + exit; + } + + conf_mount_rw(); + local_group_del($a_group[$id]); + conf_mount_ro(); + $groupdeleted = $a_group[$id]['name']; + unset($a_group[$id]); + write_config(); + $savemsg = gettext("Group") . " {$groupdeleted} " . + gettext("successfully deleted") . "<br />"; +} + +if ($act == "delpriv") { + + if (!isset($id) || !isset($a_group[$id])) { + pfSenseHeader("system_groupmanager.php"); + exit; + } + + $privdeleted = $priv_list[$a_group[$id]['priv'][$_POST['privid']]]['name']; + unset($a_group[$id]['priv'][$_POST['privid']]); + + if (is_array($a_group[$id]['member'])) { + foreach ($a_group[$id]['member'] as $uid) { + $user = getUserEntryByUID($uid); + if ($user) { + local_user_set($user); + } + } + } + + write_config(); + $act = "edit"; + $savemsg = gettext("Privilege") . " {$privdeleted} " . + gettext("successfully deleted") . "<br />"; +} + +if ($act == "edit") { + if (isset($id) && isset($a_group[$id])) { + $pconfig['name'] = $a_group[$id]['name']; + $pconfig['gid'] = $a_group[$id]['gid']; + $pconfig['gtype'] = $a_group[$id]['scope']; + $pconfig['description'] = $a_group[$id]['description']; + $pconfig['members'] = $a_group[$id]['member']; + $pconfig['priv'] = $a_group[$id]['priv']; + } +} + +if (isset($_POST['dellall_x'])) { + + $del_groups = $_POST['delete_check']; + + if (!empty($del_groups)) { + foreach ($del_groups as $groupid) { + if (isset($a_group[$groupid]) && $a_group[$groupid]['scope'] != "system") { + conf_mount_rw(); + local_group_del($a_group[$groupid]); + conf_mount_ro(); + unset($a_group[$groupid]); + } + } + $savemsg = gettext("Selected groups removed successfully!"); + write_config($savemsg); + } +} + +if (isset($_POST['save'])) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "groupname"); + $reqdfieldsn = array(gettext("Group Name")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (preg_match("/[^a-zA-Z0-9\.\-_ ]/", $_POST['groupname'])) { + $input_errors[] = gettext("The group name contains invalid characters."); + } + + if (strlen($_POST['groupname']) > 16) { + $input_errors[] = gettext("The group name is longer than 16 characters."); + } + + if (!$input_errors && !(isset($id) && $a_group[$id])) { + /* make sure there are no dupes */ + foreach ($a_group as $group) { + if ($group['name'] == $_POST['groupname']) { + $input_errors[] = gettext("Another entry with the same group name already exists."); + break; + } + } + } + + if (!$input_errors) { + $group = array(); + if (isset($id) && $a_group[$id]) { + $group = $a_group[$id]; + } + + $group['name'] = $_POST['groupname']; + $group['description'] = $_POST['description']; + + if (empty($_POST['members'])) { + unset($group['member']); + } else if ($group['gid'] != 1998) { // all group + $group['member'] = $_POST['members']; + } + + if (isset($id) && $a_group[$id]) { + $a_group[$id] = $group; + } else { + $group['gid'] = $config['system']['nextgid']++; + $a_group[] = $group; + } + + conf_mount_rw(); + local_group_set($group); + conf_mount_ro(); + + /* Refresh users in this group since their privileges may have changed. */ + if (is_array($group['member'])) { + $a_user = &$config['system']['user']; + foreach ($a_user as & $user) { + if (in_array($user['uid'], $group['member'])) { + local_user_set($user); + } + } + } + + write_config(); + + header("Location: system_groupmanager.php"); + exit; + } +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); + +$tab_array = array(); +$tab_array[] = array(gettext("Users"), false, "system_usermanager.php"); +$tab_array[] = array(gettext("Groups"), true, "system_groupmanager.php"); +$tab_array[] = array(gettext("Settings"), false, "system_usermanager_settings.php"); +$tab_array[] = array(gettext("Servers"), false, "system_authservers.php"); +display_top_tabs($tab_array); + +if (!($_GET['act'] == "new" || $_GET['act'] == "edit")) +{ +?> + <div class="table-responsive"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th><?=gettext("Group name")?></th> + <th><?=gettext("Description")?></th> + <th><?=gettext("Member Count")?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php + foreach($a_group as $i => $group): + if ($group["name"] == "all") + $groupcount = count($config['system']['user']); + else + $groupcount = count($group['member']); +?> + <tr> + <td> + <?=htmlspecialchars($group['name'])?> + </td> + <td> + <?=htmlspecialchars($group['description'])?> + </td> + <td> + <?=$groupcount?> + </td> + <td> + <a href="?act=edit&groupid=<?=$i?>" class="btn btn-xs btn-primary">edit</a> + <?php if($group['scope'] != "system"): ?> + <a href="?act=delgroup&groupid=<?=$i?>&groupname=<?=$group['name']?>" class="btn btn-xs btn-danger">delete</a> + <?php endif;?> + </td> + </tr> +<?php + endforeach; +?> + </tbody> + </table> + </div> + + <nav class="action-buttons"> + <a href="?act=new" class="btn btn-success">add new</a> + </nav> +<?php + include('foot.inc'); + exit; +} + +require('classes/Form.class.php'); +$form = new Form; +$form->setAction('system_groupmanager.php?act=edit'); +$form->addGlobal(new Form_Input( + 'groupid', + null, + 'hidden', + $id +)); + +if (isset($id) && $a_group[$id]){ + $form->addGlobal(new Form_Input( + 'id', + null, + 'hidden', + $id + )); + + $form->addGlobal(new Form_Input( + 'gid', + null, + 'hidden', + $pconfig['gid'] + )); +} + +$section = new Form_Section('Group properties'); + +if ($_GET['act'] != "new") +{ + $section->addInput(new Form_StaticText( + 'Defined by', + strtoupper($pconfig['gtype']) + )); +} + +$section->addInput($input = new Form_Input( + 'groupname', + 'Group name', + 'text', + $pconfig['name'] +)); + +if ($pconfig['gtype'] == "system") + $input->setReadonly(); + +$section->addInput(new Form_Input( + 'description', + 'Description', + 'text', + $pconfig['description'] +))->setHelp('Group description, for your own information only'); + +$form->add($section); +if ($pconfig['gid'] != 1998) // all users group +{ + $section = new Form_Section('Group Memberships'); + + $allUsers = array_map(function($u){ return $u['name']; }, $config['system']['user']); + $section->addInput(new Form_Select( + 'members', + 'Members', + $pconfig['members'], + $allUsers, + true + ))->setHelp('Hold down CTRL (pc)/COMMAND (mac) key to select'); + + $form->add($section); +} + +if ($_GET['act'] != "new") +{ + $section = new Form_Section('Assigned Privileges'); + + foreach ((array)$pconfig['priv'] as $i => $priv) + { + // We reverse name and action for readability of longer names + $group = new Form_Group('Revoke privilege'); + + $group->add(new Form_Checkbox( + 'delpriv[]', + null, + $priv_list[ $priv ]['name'], + false, + $i + )); + + $section->add($group); + } + + $section->addInput(new Form_StaticText( + null, + new Form_Button(null, 'grant more privileges', 'system_groupmanager_addprivs.php?groupid='. $id) + )); + + $form->add($section); +} + +print $form; + +include('foot.inc');
\ No newline at end of file diff --git a/src/usr/local/www/system_groupmanager_addprivs.php b/src/usr/local/www/system_groupmanager_addprivs.php new file mode 100644 index 0000000..d9134bf --- /dev/null +++ b/src/usr/local/www/system_groupmanager_addprivs.php @@ -0,0 +1,174 @@ +<?php +/* $Id$ */ +/* + system_groupmanager_addprivs.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2006 Daniel S. Haischt. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: auth +*/ + +##|+PRIV +##|*IDENT=page-system-groupmanager-addprivs +##|*NAME=System: Group Manager: Add Privileges page +##|*DESCR=Allow access to the 'System: Group Manager: Add Privileges' page. +##|*MATCH=system_groupmanager_addprivs.php* +##|-PRIV + +function cpusercmp($a, $b) { + return strcasecmp($a['name'], $b['name']); +} + +function admin_groups_sort() { + global $config; + + if (!is_array($config['system']['group'])) { + return; + } + + usort($config['system']['group'], "cpusercmp"); +} + +require("guiconfig.inc"); + +$pgtitle = array(gettext("System"), gettext("Group manager"), gettext("Add privileges")); + +if (is_numericint($_GET['groupid'])) { + $groupid = $_GET['groupid']; +} +if (isset($_POST['groupid']) && is_numericint($_POST['groupid'])) { + $groupid = $_POST['groupid']; +} + +$a_group = & $config['system']['group'][$groupid]; + +if (!is_array($a_group)) { + pfSenseHeader("system_groupmanager.php?id={$groupid}"); + exit; +} + +if (!is_array($a_group['priv'])) { + $a_group['priv'] = array(); +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "sysprivs"); + $reqdfieldsn = array(gettext("Selected privileges")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + + if (!$input_errors) { + + if (!is_array($pconfig['sysprivs'])) { + $pconfig['sysprivs'] = array(); + } + + if (!count($a_group['priv'])) { + $a_group['priv'] = $pconfig['sysprivs']; + } else { + $a_group['priv'] = array_merge($a_group['priv'], $pconfig['sysprivs']); + } + + if (is_array($a_group['member'])) { + foreach ($a_group['member'] as $uid) { + $user = getUserEntryByUID($uid); + if ($user) { + local_user_set($user); + } + } + } + + admin_groups_sort(); + + $retval = write_config(); + $savemsg = get_std_save_message($retval); + + pfSenseHeader("system_groupmanager.php?act=edit&groupid={$groupid}"); + exit; + } +} + +/* if ajax is calling, give them an update message */ +if (isAjax()) { + print_info_box_np($savemsg); +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); + +$tab_array = array(); +$tab_array[] = array(gettext("Users"), false, "system_usermanager.php"); +$tab_array[] = array(gettext("Groups"), true, "system_groupmanager.php"); +$tab_array[] = array(gettext("Settings"), false, "system_usermanager_settings.php"); +$tab_array[] = array(gettext("Servers"), false, "system_authservers.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); +$form = new Form; +if (isset($groupid)) +{ + $form->addGlobal(new Form_Input( + 'groupid', + null, + 'hidden', + $groupid + )); +} + +$section = new Form_Section('Add privileges for '. $a_group['name']); + +$priv_list = array_map(function($p){ return $p['name']; }, $priv_list); +asort($priv_list); + +$section->addInput(new Form_Select( + 'sysprivs', + 'Assigned privileges', + $a_group['priv'], + $priv_list, + true +))->setHelp('Hold down CTRL (pc)/COMMAND (mac) key to select'); + +$form->add($section); + +print $form; + +include('foot.inc');
\ No newline at end of file diff --git a/src/usr/local/www/system_hasync.php b/src/usr/local/www/system_hasync.php new file mode 100755 index 0000000..47f6ea6 --- /dev/null +++ b/src/usr/local/www/system_hasync.php @@ -0,0 +1,336 @@ +<?php +/* $Id$ */ +/* + system_hasync.php + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2012 Darren Embry <dse@webonastick.com>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: system +*/ + +##|+PRIV +##|*IDENT=page-system-hasync +##|*NAME=System: High Availability Sync +##|*DESCR=Allow access to the 'System: High Availability Sync' page. +##|*MATCH=system_hasync.php* +##|-PRIV + +require("guiconfig.inc"); + +if (!is_array($config['hasync'])) + $config['hasync'] = array(); + +$a_hasync = &$config['hasync']; + +$checkbox_names = array( + 'pfsyncenabled', + 'synchronizeusers', + 'synchronizeauthservers', + 'synchronizecerts', + 'synchronizerules', + 'synchronizeschedules', + 'synchronizealiases', + 'synchronizenat', + 'synchronizeipsec', + 'synchronizeopenvpn', + 'synchronizedhcpd', + 'synchronizewol', + 'synchronizestaticroutes', + 'synchronizelb', + 'synchronizevirtualip', + 'synchronizetrafficshaper', + 'synchronizetrafficshaperlimiter', + 'synchronizetrafficshaperlayer7', + 'synchronizednsforwarder', + 'synchronizecaptiveportal'); + +if ($_POST) { + $pconfig = $_POST; + foreach ($checkbox_names as $name) { + $a_hasync[$name] = $pconfig[$name] ? $pconfig[$name] : false; + } + $a_hasync['pfsyncpeerip'] = $pconfig['pfsyncpeerip']; + $a_hasync['pfsyncinterface'] = $pconfig['pfsyncinterface']; + $a_hasync['synchronizetoip'] = $pconfig['synchronizetoip']; + $a_hasync['username'] = $pconfig['username']; + $a_hasync['password'] = $pconfig['passwordfld']; + write_config("Updated High Availability Sync configuration"); + interfaces_sync_setup(); + header("Location: system_hasync.php"); + exit(); +} + +foreach ($checkbox_names as $name) { + $pconfig[$name] = $a_hasync[$name]; +} +$pconfig['pfsyncpeerip'] = $a_hasync['pfsyncpeerip']; +$pconfig['pfsyncinterface'] = $a_hasync['pfsyncinterface']; +$pconfig['synchronizetoip'] = $a_hasync['synchronizetoip']; +$pconfig['username'] = $a_hasync['username']; +$pconfig['passwordfld'] = $a_hasync['password']; + +$ifaces = get_configured_interface_with_descr(); +$ifaces["lo0"] = "loopback"; + +$pgtitle = array(gettext("System"), gettext("High Availability Sync")); +$shortcut_section = "carp"; + +// Build a list of available interfaces +$iflist = array(); +foreach ($ifaces as $ifname => $iface) { + $iflist[$ifname] = $iface; +} + +include("head.inc"); + +require('classes/Form.class.php'); + +$form = new Form; + +$section = new Form_Section('State Synchronization Settings (pfsync)'); + +$section->addInput(new Form_Checkbox( + 'pfsyncenabled', + 'Synchronize states', + 'pfsync transfers state insertion, update, and deletion messages between firewalls.', + ($pconfig['pfsyncenabled'] === 'on'), + 'on' +))->setHelp('Each firewall sends these messages out via multicast on a specified interface, using the PFSYNC protocol (IP Protocol 240).' . + ' It also listens on that interface for similar messages from other firewalls, and imports them into the local state table.<br />' . + 'This setting should be enabled on all members of a failover group.<br />' . + 'Clicking "Save" will force a configuration sync if it is enabled! (see Configuration Synchronization Settings below)'); + +$section->addInput(new Form_Select( + 'pfsyncinterface', + 'Synchorize Interface', + $pconfig['pfsyncinterface'], + $iflist +))->setHelp('If Synchronize States is enabled this interface will be used for communication.<br />' . + 'We recommend setting this to an interface other than LAN! A dedicated interface works the best.<br />' . + 'You must define a IP on each machine participating in this failover group.<br />' . + 'You must have an IP assigned to the interface on any participating sync nodes.'); + +$section->addInput(new Form_Input( + 'pfsyncpeerip', + 'pfsync Synchronize Peer IP', + 'text', + $pconfig['pfsyncpeerip'], + ['placeholder' => 'IP Address'] +))->setHelp('Setting this option will force pfsync to synchronize its state table to this IP address. The default is directed multicast.'); + +$form->add($section); + +$section = new Form_Section('Configuration Synchronization Settings (XMLRPC Sync)'); + +$section->addInput(new Form_Input( + 'synchronizetoip', + 'Synchronize Config to IP', + 'text', + $pconfig['synchronizetoip'], + ['placeholder' => 'IP Address'] +))->setHelp('Enter the IP address of the firewall to which the selected configuration sections should be synchronized.<br /><br />' . + 'XMLRPC sync is currently only supported over connections using the same protocol and port as this system - make sure the remote system\'s port and protocol are set accordingly!<br />' . + 'Do not use the Synchronize Config to IP and password option on backup cluster members!'); + +$section->addInput(new Form_Input( + 'username', + 'Remote System Username', + 'text', + $pconfig['username'] +))->setHelp('Enter the webConfigurator username of the system entered above for synchronizing your configuration.<br />' . + 'Do not use the Synchronize Config to IP and username option on backup cluster members!'); + +$section->addInput(new Form_Input( + 'passwordfld', + 'Remote System Password', + 'password', + $pconfig['passwordfld'] +))->setHelp('Enter the webConfigurator password of the system entered above for synchronizing your configuration.<br />' . + 'Do not use the Synchronize Config to IP and password option on backup cluster members!'); + +$group = new Form_MultiCheckboxGroup('Select options to sync'); + +$group->add(new Form_MultiCheckbox( + 'synchronizeusers', + 'Synchronize Users and Groups', + 'Sync the users and groups over ', + ($pconfig['synchronizeusers'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizeauthservers', + 'Synchronize Auth Servers', + 'Sync the authentication servers (e.g. LDAP, RADIUS) over ', + ($pconfig['synchronizeauthservers'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizecerts', + 'Synchronize Certificates', + 'Sync the Certificate Authorities, Certificates, and Certificate Revocation Lists over ', + ($pconfig['synchronizecerts'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizerules', + 'Synchronize Rules', + 'Sync the firewall rules ', + ($pconfig['synchronizerules'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizeschedules', + 'Synchronize Firewall schedules', + 'Sync the firewall schedules ', + ($pconfig['synchronizeschedules'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizealiases', + 'Synchronize Firewall aliasas', + 'Sync the firewall aliasas ', + ($pconfig['synchronizealiasas'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizenat', + 'Synchronize NAT', + 'Sync NAT rules ', + ($pconfig['synchronizenat'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizeipsec', + 'Synchronize IPsec', + 'Automatically IPsec configuration ', + ($pconfig['synchronizeipsec'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizeopenvpn', + 'Synchronize OpenVPN', + 'Automatically OpenVPN configuration ', + ($pconfig['synchronizeopenvpn'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizedhcpd', + 'Synchronize DHCPD', + 'Sync DHCP Server settings ', + ($pconfig['synchronizedhcpd'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizewol', + 'Synchronize Wake on LAN', + 'Sync WoL Server settings ', + ($pconfig['synchronizewol'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizestaticroutes', + 'Synchronize Static Routes', + 'Sync Static Route configuration ', + ($pconfig['synchronizestaticroutes'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizelb', + 'Synchronize Load Balancer', + 'Sync Load Balancer configuration ', + ($pconfig['synchronizelb'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizevirtualip', + 'Synchronize Virtual IPs', + 'Sync Virtual IPs ', + ($pconfig['synchronizevirtualip'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizetrafficshaper', + 'Synchronize traffic shaper (queues)', + 'Sync the Traffic Shaper configuration ', + ($pconfig['synchronizetrafficshaper'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizetrafficshaperlimiter', + 'Synchronize traffic shaper (limiter)', + 'Sync the Traffic Shaper configuration for limiters ', + ($pconfig['synchronizetrafficshaperlimiter'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizetrafficshaperlayer7', + 'Synchronize traffic shaper (layer 7)', + 'Sync the Traffic Shaper configuration for layer 7 ', + ($pconfig['synchronizetrafficshaperlayer7'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizednsforwarder', + 'Synchronize traffic shaper (Forwarder/Resolver)', + 'Sync the DNS Forwarder and DNS Resolver configurations ', + ($pconfig['synchronizednsforwarder'] === 'on'), + 'on' +)); + +$group->add(new Form_MultiCheckbox( + 'synchronizecaptiveportal', + 'Synchronize Captive Portal)', + 'Sync the Captive Portal configurations ', + ($pconfig['synchronizecaptiveportal'] === 'on'), + 'on' +)); + +$section->add($group); + +$form->add($section); + +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_routes.php b/src/usr/local/www/system_routes.php new file mode 100644 index 0000000..600d872 --- /dev/null +++ b/src/usr/local/www/system_routes.php @@ -0,0 +1,303 @@ +<?php +/* $Id$ */ +/* + system_routes.php + part of m0n0wall (http://m0n0.ch/wall) + part of pfSense + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-system-staticroutes +##|*NAME=System: Static Routes page +##|*DESCR=Allow access to the 'System: Static Routes' page. +##|*MATCH=system_routes.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +if (!is_array($config['staticroutes']['route'])) { + $config['staticroutes']['route'] = array(); +} + +$a_routes = &$config['staticroutes']['route']; +$a_gateways = return_gateways_array(true, true, true); +$changedesc_prefix = gettext("Static Routes") . ": "; +unset($input_errors); + +if ($_POST) { + + $pconfig = $_POST; + + if ($_POST['apply']) { + + $retval = 0; + + if (file_exists("{$g['tmp_path']}/.system_routes.apply")) { + $toapplylist = unserialize(file_get_contents("{$g['tmp_path']}/.system_routes.apply")); + foreach ($toapplylist as $toapply) { + mwexec("{$toapply}"); + } + + @unlink("{$g['tmp_path']}/.system_routes.apply"); + } + + $retval = system_routing_configure(); + $retval |= filter_configure(); + /* reconfigure our gateway monitor */ + setup_gateways_monitor(); + + $savemsg = get_std_save_message($retval); + if ($retval == 0) { + clear_subsystem_dirty('staticroutes'); + } + } +} + +function delete_static_route($id) { + global $config, $a_routes, $changedesc_prefix; + + if (!isset($a_routes[$id])) { + return; + } + + $targets = array(); + if (is_alias($a_routes[$id]['network'])) { + foreach (filter_expand_alias_array($a_routes[$id]['network']) as $tgt) { + if (is_ipaddrv4($tgt)) { + $tgt .= "/32"; + } else if (is_ipaddrv6($tgt)) { + $tgt .= "/128"; + } + if (!is_subnet($tgt)) { + continue; + } + $targets[] = $tgt; + } + } else { + $targets[] = $a_routes[$id]['network']; + } + + foreach ($targets as $tgt) { + $family = (is_subnetv6($tgt) ? "-inet6" : "-inet"); + mwexec("/sbin/route delete {$family} " . escapeshellarg($tgt)); + } + + unset($targets); +} + +if ($_GET['act'] == "del") { + if ($a_routes[$_GET['id']]) { + $changedesc = $changedesc_prefix . gettext("removed route to") . " " . $a_routes[$_GET['id']]['network']; + delete_static_route($_GET['id']); + unset($a_routes[$_GET['id']]); + write_config($changedesc); + header("Location: system_routes.php"); + exit; + } +} + +if (isset($_POST['del_x'])) { + /* delete selected routes */ + if (is_array($_POST['route']) && count($_POST['route'])) { + $changedesc = $changedesc_prefix . gettext("removed route to"); + foreach ($_POST['route'] as $routei) { + $changedesc .= " " . $a_routes[$routei]['network']; + delete_static_route($routei); + unset($a_routes[$routei]); + } + write_config($changedesc); + header("Location: system_routes.php"); + exit; + } + +} else if ($_GET['act'] == "toggle") { + if ($a_routes[$_GET['id']]) { + $do_update_config = true; + if (isset($a_routes[$_GET['id']]['disabled'])) { + // Do not enable a route whose gateway is disabled + if (isset($a_gateways[$a_routes[$_GET['id']]['gateway']]['disabled'])) { + $do_update_config = false; + $input_errors[] = $changedesc_prefix . gettext("gateway is disabled, cannot enable route to") . " " . $a_routes[$_GET['id']]['network']; + } else { + unset($a_routes[$_GET['id']]['disabled']); + $changedesc = $changedesc_prefix . gettext("enabled route to") . " " . $a_routes[$_GET['id']]['network']; + } + } else { + delete_static_route($_GET['id']); + $a_routes[$_GET['id']]['disabled'] = true; + $changedesc = $changedesc_prefix . gettext("disabled route to") . " " . $a_routes[$_GET['id']]['network']; + } + + if ($do_update_config) { + if (write_config($changedesc)) { + mark_subsystem_dirty('staticroutes'); + } + header("Location: system_routes.php"); + exit; + } + } +} else { + /* yuck - IE won't send value attributes for image buttons, while Mozilla does - so we use .x/.y to find move button clicks instead... */ + unset($movebtn); + foreach ($_POST as $pn => $pd) { + if (preg_match("/move_(\d+)_x/", $pn, $matches)) { + $movebtn = $matches[1]; + break; + } + } + /* move selected routes before this route */ + if (isset($movebtn) && is_array($_POST['route']) && count($_POST['route'])) { + $a_routes_new = array(); + + /* copy all routes < $movebtn and not selected */ + for ($i = 0; $i < $movebtn; $i++) { + if (!in_array($i, $_POST['route'])) { + $a_routes_new[] = $a_routes[$i]; + } + } + + /* copy all selected routes */ + for ($i = 0; $i < count($a_routes); $i++) { + if ($i == $movebtn) { + continue; + } + if (in_array($i, $_POST['route'])) { + $a_routes_new[] = $a_routes[$i]; + } + } + + /* copy $movebtn route */ + if ($movebtn < count($a_routes)) { + $a_routes_new[] = $a_routes[$movebtn]; + } + + /* copy all routes > $movebtn and not selected */ + for ($i = $movebtn+1; $i < count($a_routes); $i++) { + if (!in_array($i, $_POST['route'])) { + $a_routes_new[] = $a_routes[$i]; + } + } + if (count($a_routes_new) > 0) { + $a_routes = $a_routes_new; + } + + if (write_config()) { + mark_subsystem_dirty('staticroutes'); + } + header("Location: system_routes.php"); + exit; + } +} + +$pgtitle = array(gettext("System"), gettext("Static Routes")); +$shortcut_section = "routing"; + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); +if (is_subsystem_dirty('staticroutes')) + print_info_box_np(gettext("The static route configuration has been changed.") . "<br />" . gettext("You must apply the changes in order for them to take effect.")); + +$tab_array = array(); +$tab_array[0] = array(gettext("Gateways"), false, "system_gateways.php"); +$tab_array[1] = array(gettext("Routes"), true, "system_routes.php"); +$tab_array[2] = array(gettext("Groups"), false, "system_gateway_groups.php"); +display_top_tabs($tab_array); + +?> +<table class="table"> +<thead> + <tr> + <th></th> + <th><?=gettext("Network")?></th> + <th><?=gettext("Gateway")?></th> + <th><?=gettext("Interface")?></th> + <th><?=gettext("Description")?></th> + <th></th> + </tr> +</thead> +<tbody> +<?php +foreach ($a_routes as $i => $route): + if (isset($route['disabled'])) + $icon = 'icon-ban-circle'; + else + $icon = 'icon-ok-circle'; +?> + <tr<?=($icon != 'icon-ok-circle')? ' class="disabled"' : ''?>> + <td><i class="icon <?=$icon?>"></i></td> + <td> + <?=strtolower($route['network'])?> + </td> + <td> + <?=htmlentities($a_gateways[$route['gateway']]['name']) . " - " . htmlentities($a_gateways[$route['gateway']]['gateway'])?> + </td> + <td> + <?=convert_friendly_interface_to_friendly_descr($a_gateways[$route['gateway']]['friendlyiface'])?> + </td> + <td> + <?=htmlspecialchars($route['descr'])?> + </td> + <td> + <a class="btn btn-xs btn-primary" href="system_routes_edit.php?id=<?=$i?>"> + edit + </a> + + <a class="btn btn-xs btn-default" href="system_routes_edit.php?dup=<?=$i?>"> + copy + </a> + + <a class="btn btn-xs btn-danger" href="system_routes.php?act=del&id=<?=$i?>"> + delete + </a> + + <a class="btn btn-xs btn-default" href="?act=toggle&id=<?=$i?>"> + toggle + </a> + </td> +<? endforeach?> + </tr> +</table> + +<nav class="action-buttons"> + <a href="system_routes_edit.php" role="button" class="btn btn-success"> + <?=gettext("add new route")?> + </a> +</nav> +<?php + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_routes_edit.php b/src/usr/local/www/system_routes_edit.php new file mode 100644 index 0000000..1a8974f --- /dev/null +++ b/src/usr/local/www/system_routes_edit.php @@ -0,0 +1,292 @@ +<?php +/* + system_routes_edit.php + part of m0n0wall (http://m0n0.ch/wall) + part of pfSense + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2010 Scott Ullrich + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: routing +*/ + +##|+PRIV +##|*IDENT=page-system-staticroutes-editroute +##|*NAME=System: Static Routes: Edit route page +##|*DESCR=Allow access to the 'System: Static Routes: Edit route' page. +##|*MATCH=system_routes_edit.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("filter.inc"); +require_once("util.inc"); +require_once("gwlb.inc"); + +$referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/system_routes.php'); + +if (!is_array($config['staticroutes']['route'])) { + $config['staticroutes']['route'] = array(); +} + +$a_routes = &$config['staticroutes']['route']; +$a_gateways = return_gateways_array(true, true); + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + $id = $_GET['dup']; +} + +if (isset($id) && $a_routes[$id]) { + list($pconfig['network'], $pconfig['network_subnet']) = + explode('/', $a_routes[$id]['network']); + $pconfig['gateway'] = $a_routes[$id]['gateway']; + $pconfig['descr'] = $a_routes[$id]['descr']; + $pconfig['disabled'] = isset($a_routes[$id]['disabled']); +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + unset($id); +} + +if ($_POST) { + + global $aliastable; + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "network network_subnet gateway"); + $reqdfieldsn = explode(",", + gettext("Destination network") . "," . + gettext("Destination network bit count") . "," . + gettext("Gateway")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($_POST['network'] && !is_ipaddr($_POST['network']) && !is_alias($_POST['network']))) { + $input_errors[] = gettext("A valid IPv4 or IPv6 destination network must be specified."); + } + if (($_POST['network_subnet'] && !is_numeric($_POST['network_subnet']))) { + $input_errors[] = gettext("A valid destination network bit count must be specified."); + } + if (($_POST['gateway']) && is_ipaddr($_POST['network'])) { + if (!isset($a_gateways[$_POST['gateway']])) { + $input_errors[] = gettext("A valid gateway must be specified."); + } else if (isset($a_gateways[$_POST['gateway']]['disabled']) && !$_POST['disabled']) { + $input_errors[] = gettext("The gateway is disabled but the route is not. You must disable the route in order to choose a disabled gateway."); + } else { + // Note that the 3rd parameter "disabled" must be passed as explicitly true or false. + if (!validate_address_family($_POST['network'], $_POST['gateway'], $_POST['disabled'] ? true : false)) { + $input_errors[] = gettext("The gateway '{$a_gateways[$_POST['gateway']]['gateway']}' is a different Address Family than network '{$_POST['network']}'."); + } + } + } + + /* check for overlaps */ + $current_targets = get_staticroutes(true); + $new_targets = array(); + if (is_ipaddrv6($_POST['network'])) { + $osn = gen_subnetv6($_POST['network'], $_POST['network_subnet']) . "/" . $_POST['network_subnet']; + $new_targets[] = $osn; + } + if (is_ipaddrv4($_POST['network'])) { + if ($_POST['network_subnet'] > 32) { + $input_errors[] = gettext("A IPv4 subnet can not be over 32 bits."); + } else { + $osn = gen_subnet($_POST['network'], $_POST['network_subnet']) . "/" . $_POST['network_subnet']; + $new_targets[] = $osn; + } + } elseif (is_alias($_POST['network'])) { + $osn = $_POST['network']; + foreach (preg_split('/\s+/', $aliastable[$osn]) as $tgt) { + if (is_ipaddrv4($tgt)) { + $tgt .= "/32"; + } + if (is_ipaddrv6($tgt)) { + $tgt .= "/128"; + } + if (!is_subnet($tgt)) { + continue; + } + if (!is_subnetv6($tgt)) { + continue; + } + $new_targets[] = $tgt; + } + } + if (!isset($id)) { + $id = count($a_routes); + } + $oroute = $a_routes[$id]; + $old_targets = array(); + if (!empty($oroute)) { + if (is_alias($oroute['network'])) { + foreach (filter_expand_alias_array($oroute['network']) as $tgt) { + if (is_ipaddrv4($tgt)) { + $tgt .= "/32"; + } else if (is_ipaddrv6($tgt)) { + $tgt .= "/128"; + } + if (!is_subnet($tgt)) { + continue; + } + $old_targets[] = $tgt; + } + } else { + $old_targets[] = $oroute['network']; + } + } + + $overlaps = array_intersect($current_targets, $new_targets); + $overlaps = array_diff($overlaps, $old_targets); + if (count($overlaps)) { + $input_errors[] = gettext("A route to these destination networks already exists") . ": " . implode(", ", $overlaps); + } + + if (is_array($config['interfaces'])) { + foreach ($config['interfaces'] as $if) { + if (is_ipaddrv4($_POST['network']) && + isset($if['ipaddr']) && isset($if['subnet']) && + is_ipaddrv4($if['ipaddr']) && is_numeric($if['subnet']) && + ($_POST['network_subnet'] == $if['subnet']) && + (gen_subnet($_POST['network'], $_POST['network_subnet']) == gen_subnet($if['ipaddr'], $if['subnet']))) { + $input_errors[] = sprintf(gettext("This network conflicts with address configured on interface %s."), $if['descr']); + } else if (is_ipaddrv6($_POST['network']) && + isset($if['ipaddrv6']) && isset($if['subnetv6']) && + is_ipaddrv6($if['ipaddrv6']) && is_numeric($if['subnetv6']) && + ($_POST['network_subnet'] == $if['subnetv6']) && + (gen_subnetv6($_POST['network'], $_POST['network_subnet']) == gen_subnetv6($if['ipaddrv6'], $if['subnetv6']))) { + $input_errors[] = sprintf(gettext("This network conflicts with address configured on interface %s."), $if['descr']); + } + } + } + + if (!$input_errors) { + $route = array(); + $route['network'] = $osn; + $route['gateway'] = $_POST['gateway']; + $route['descr'] = $_POST['descr']; + if ($_POST['disabled']) { + $route['disabled'] = true; + } else { + unset($route['disabled']); + } + + if (file_exists("{$g['tmp_path']}/.system_routes.apply")) { + $toapplylist = unserialize(file_get_contents("{$g['tmp_path']}/.system_routes.apply")); + } else { + $toapplylist = array(); + } + $a_routes[$id] = $route; + + if (!empty($oroute)) { + $delete_targets = array_diff($old_targets, $new_targets); + if (count($delete_targets)) { + foreach ($delete_targets as $dts) { + if (is_ipaddrv6($dts)) { + $family = "-inet6"; + } + $toapplylist[] = "/sbin/route delete {$family} {$dts}"; + } + } + } + file_put_contents("{$g['tmp_path']}/.system_routes.apply", serialize($toapplylist)); + + mark_subsystem_dirty('staticroutes'); + + write_config(); + + header("Location: system_routes.php"); + exit; + } +} + +$pgtitle = array(gettext("System"), gettext("Static Routes"), gettext("Edit route")); +$shortcut_section = "routing"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +require('classes/Form.class.php'); +$form = new Form; + +if (isset($id) && $a_routes[$id]) { + $form->addGlobal(new Form_Input( + 'id', + null, + 'hidden', + $id + )); +} + +$section = new Form_Section('Edit route entry'); + +$section->addInput(new Form_IpAddress( + 'network_subnet', + 'Destination network', + $pconfig['network'] +))->addMask('network_subnet', $pconfig['network_subnet'])->setHelp('Destination network for this static route'); + +$allGateways = array_combine( + array_map(function($g){ return $g['name']; }, $a_gateways), + array_map(function($g){ return $g['name'] .' - '. $g['gateway']; }, $a_gateways) +); +$section->addInput(new Form_Select( + 'gateway', + 'Gateway', + $pconfig['gateway'], + $allGateways +))->setHelp('Choose which gateway this route applies to or <a href="'. + '/system_gateways_edit.php">add a new one first</a>'); + +$section->addInput(new Form_Checkbox( + 'disabled', + 'Disabled', + 'Disable this static route', + $pconfig['disabled'] +))->setHelp('Set this option to disable this static route without removing it from '. + 'the list.'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + htmlspecialchars($pconfig['descr']) +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$form->add($section); + +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_usermanager.php b/src/usr/local/www/system_usermanager.php new file mode 100644 index 0000000..4a3802b --- /dev/null +++ b/src/usr/local/www/system_usermanager.php @@ -0,0 +1,651 @@ +<?php +/* $Id$ */ +/* + system_usermanager.php + part of m0n0wall (http://m0n0.ch/wall) + + part of pfSense + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Copyright (C) 2008 Shrew Soft Inc. + All rights reserved. + + Copyright (C) 2005 Paul Taylor <paultaylor@winn-dixie.com>. + All rights reserved. + + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: + pfSense_MODULE: auth +*/ + +##|+PRIV +##|*IDENT=page-system-usermanager +##|*NAME=System: User Manager page +##|*DESCR=Allow access to the 'System: User Manager' page. +##|*MATCH=system_usermanager.php* +##|-PRIV + +require("certs.inc"); +require("guiconfig.inc"); + +// start admin user code +$pgtitle = array(gettext("System"), gettext("User Manager")); + +if (isset($_POST['userid']) && is_numericint($_POST['userid'])) { + $id = $_POST['userid']; +} + +if (!isset($config['system']['user']) || !is_array($config['system']['user'])) { + $config['system']['user'] = array(); +} + +$a_user = &$config['system']['user']; + +if (isset($_SERVER['HTTP_REFERER'])) { + $referer = $_SERVER['HTTP_REFERER']; +} else { + $referer = '/system_usermanager.php'; +} + +if (isset($id) && $a_user[$id]) { + $pconfig['usernamefld'] = $a_user[$id]['name']; + $pconfig['descr'] = $a_user[$id]['descr']; + $pconfig['expires'] = $a_user[$id]['expires']; + $pconfig['groups'] = local_user_get_groups($a_user[$id]); + $pconfig['utype'] = $a_user[$id]['scope']; + $pconfig['uid'] = $a_user[$id]['uid']; + $pconfig['authorizedkeys'] = base64_decode($a_user[$id]['authorizedkeys']); + $pconfig['priv'] = $a_user[$id]['priv']; + $pconfig['ipsecpsk'] = $a_user[$id]['ipsecpsk']; + $pconfig['disabled'] = isset($a_user[$id]['disabled']); +} + +if ($_POST['act'] == "deluser") { + + if (!isset($_POST['username']) || !isset($a_user[$id]) || ($_POST['username'] != $a_user[$id]['name'])) { + pfSenseHeader("system_usermanager.php"); + exit; + } + + conf_mount_rw(); + local_user_del($a_user[$id]); + conf_mount_ro(); + $userdeleted = $a_user[$id]['name']; + unset($a_user[$id]); + write_config(); + $savemsg = gettext("User")." {$userdeleted} ". + gettext("successfully deleted")."<br />"; +} +else if ($_GET['act'] == "new") { + /* + * set this value cause the text field is read only + * and the user should not be able to mess with this + * setting. + */ + $pconfig['utype'] = "user"; + $pconfig['lifetime'] = 3650; +} + +if (isset($_POST['dellall_x'])) { + + $del_users = $_POST['delete_check']; + + if (!empty($del_users)) { + foreach ($del_users as $userid) { + if (isset($a_user[$userid]) && $a_user[$userid]['scope'] != "system") { + conf_mount_rw(); + local_user_del($a_user[$userid]); + conf_mount_ro(); + unset($a_user[$userid]); + } + } + $savemsg = gettext("Selected users removed successfully!"); + write_config($savemsg); + } +} + +if ($_POST['save']) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if (isset($id) && ($a_user[$id])) { + $reqdfields = explode(" ", "usernamefld"); + $reqdfieldsn = array(gettext("Username")); + } else { + if (empty($_POST['name'])) { + $reqdfields = explode(" ", "usernamefld passwordfld1"); + $reqdfieldsn = array( + gettext("Username"), + gettext("Password")); + } else { + $reqdfields = explode(" ", "usernamefld passwordfld1 name caref keylen lifetime"); + $reqdfieldsn = array( + gettext("Username"), + gettext("Password"), + gettext("Descriptive name"), + gettext("Certificate authority"), + gettext("Key length"), + gettext("Lifetime")); + } + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (preg_match("/[^a-zA-Z0-9\.\-_]/", $_POST['usernamefld'])) { + $input_errors[] = gettext("The username contains invalid characters."); + } + + if (strlen($_POST['usernamefld']) > 16) { + $input_errors[] = gettext("The username is longer than 16 characters."); + } + + if (($_POST['passwordfld1']) && ($_POST['passwordfld1'] != $_POST['passwordfld2'])) { + $input_errors[] = gettext("The passwords do not match."); + } + + if (isset($_POST['ipsecpsk']) && !preg_match('/^[[:ascii:]]*$/', $_POST['ipsecpsk'])) { + $input_errors[] = gettext("IPsec Pre-Shared Key contains invalid characters."); + } + + if (isset($id) && $a_user[$id]) { + $oldusername = $a_user[$id]['name']; + } else { + $oldusername = ""; + } + /* make sure this user name is unique */ + if (!$input_errors) { + foreach ($a_user as $userent) { + if ($userent['name'] == $_POST['usernamefld'] && $oldusername != $_POST['usernamefld']) { + $input_errors[] = gettext("Another entry with the same username already exists."); + break; + } + } + } + /* also make sure it is not reserved */ + if (!$input_errors) { + $system_users = explode("\n", file_get_contents("/etc/passwd")); + foreach ($system_users as $s_user) { + $ent = explode(":", $s_user); + if ($ent[0] == $_POST['usernamefld'] && $oldusername != $_POST['usernamefld']) { + $input_errors[] = gettext("That username is reserved by the system."); + break; + } + } + } + + /* + * Check for a valid expiration date if one is set at all (valid means, + * DateTime puts out a time stamp so any DateTime compatible time + * format may be used. to keep it simple for the enduser, we only + * claim to accept MM/DD/YYYY as inputs. Advanced users may use inputs + * like "+1 day", which will be converted to MM/DD/YYYY based on "now". + * Otherwise such an entry would lead to an invalid expiration data. + */ + if ($_POST['expires']) { + try { + $expdate = new DateTime($_POST['expires']); + //convert from any DateTime compatible date to MM/DD/YYYY + $_POST['expires'] = $expdate->format("m/d/Y"); + } catch (Exception $ex) { + $input_errors[] = gettext("Invalid expiration date format; use MM/DD/YYYY instead."); + } + } + + if (!empty($_POST['name'])) { + $ca = lookup_ca($_POST['caref']); + if (!$ca) { + $input_errors[] = gettext("Invalid internal Certificate Authority") . "\n"; + } + } + + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + + if (!$input_errors) { + // This used to be a separate act=delpriv + if ($a_user[$id] && !empty($_POST['privid'])) { + foreach ($_POST['privid'] as $i) + unset($a_user[$id]['priv'][$i]); + local_user_set($a_user[$id]); + write_config(); + } + + // This used to be a separate act=delcert + if ($a_user[$id] && !empty($_POST['certid'])) { + foreach ($_POST['certid'] as $i) + unset($a_user[$id]['cert'][$i]); + + write_config(); + } + + conf_mount_rw(); + $userent = array(); + if (isset($id) && $a_user[$id]) { + $userent = $a_user[$id]; + } + + isset($_POST['utype']) ? $userent['scope'] = $_POST['utype'] : $userent['scope'] = "system"; + + /* the user name was modified */ + if (!empty($_POST['oldusername']) && ($_POST['usernamefld'] <> $_POST['oldusername'])) { + $_SERVER['REMOTE_USER'] = $_POST['usernamefld']; + local_user_del($userent); + } + + /* the user password was modified */ + if ($_POST['passwordfld1']) { + local_user_set_password($userent, $_POST['passwordfld1']); + } + + $userent['name'] = $_POST['usernamefld']; + $userent['descr'] = $_POST['descr']; + $userent['expires'] = $_POST['expires']; + $userent['authorizedkeys'] = base64_encode($_POST['authorizedkeys']); + $userent['ipsecpsk'] = $_POST['ipsecpsk']; + + if ($_POST['disabled']) { + $userent['disabled'] = true; + } else { + unset($userent['disabled']); + } + + if (isset($id) && $a_user[$id]) { + $a_user[$id] = $userent; + } else { + if (!empty($_POST['name'])) { + $cert = array(); + $cert['refid'] = uniqid(); + $userent['cert'] = array(); + + $cert['descr'] = $_POST['name']; + + $subject = cert_get_subject_array($ca['crt']); + + $dn = array( + 'countryName' => $subject[0]['v'], + 'stateOrProvinceName' => $subject[1]['v'], + 'localityName' => $subject[2]['v'], + 'organizationName' => $subject[3]['v'], + 'emailAddress' => $subject[4]['v'], + 'commonName' => $userent['name']); + + cert_create($cert, $_POST['caref'], $_POST['keylen'], + (int)$_POST['lifetime'], $dn); + + if (!is_array($config['cert'])) { + $config['cert'] = array(); + } + $config['cert'][] = $cert; + $userent['cert'][] = $cert['refid']; + } + $userent['uid'] = $config['system']['nextuid']++; + /* Add the user to All Users group. */ + foreach ($config['system']['group'] as $gidx => $group) { + if ($group['name'] == "all") { + if (!is_array($config['system']['group'][$gidx]['member'])) { + $config['system']['group'][$gidx]['member'] = array(); + } + $config['system']['group'][$gidx]['member'][] = $userent['uid']; + break; + } + } + + $a_user[] = $userent; + } + + local_user_set($userent); + local_user_set_groups($userent, $_POST['groups']); + write_config(); + + if (is_dir("/etc/inc/privhooks")) { + run_plugins("/etc/inc/privhooks"); + } + + conf_mount_ro(); + + pfSenseHeader("system_usermanager.php"); + } +} + +$closehead = false; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); + +$tab_array = array(); +$tab_array[] = array(gettext("Users"), true, "system_usermanager.php"); +$tab_array[] = array(gettext("Groups"), false, "system_groupmanager.php"); +$tab_array[] = array(gettext("Settings"), false, "system_usermanager_settings.php"); +$tab_array[] = array(gettext("Servers"), false, "system_authservers.php"); +display_top_tabs($tab_array); + +if (!($_GET['act'] == "new" || $_GET['act'] == "edit" || $input_errors)) +{ +?> + +<div class="table-responsive"> +<table class="table table-striped table-hover"> + <thead> + <tr> + <th> </th> + <th><?=gettext("Username")?></th> + <th><?=gettext("Full name")?></th> + <th><?=gettext("Disabled")?></th> + <th><?=gettext("Groups")?></th> + </tr> + </thead> + <tbody> + </tbody> + <tbody> +<?php +foreach($a_user as $i => $userent): + ?> + <tr> + <td> + <input type="checkbox" id="frc<?=$i?>" name="delete_check[]" value="<?=$i?>" <?=($userent['scope'] == "system" ? 'disabled="disabled"' : '')?>/> + </td> + <td> +<?php + if($userent['scope'] != "user") + $usrimg = 'eye-open'; + else + $usrimg = 'user'; +?> + <i class="icon icon-<?=$usrimg?>"></i> + <?=htmlspecialchars($userent['name'])?> + </td> + <td><?=htmlspecialchars($userent['descr'])?></td> + <td><?php if(isset($userent['disabled'])) echo "*"?></td> + <td><?=implode(",",local_user_get_groups($userent))?></td> + <td> + <a href="?act=edit&userid=<?=$i?>" class="btn btn-xs btn-primary">edit</a> +<?php if($userent['scope'] != "system"): ?> + <a href="?act=del&userid=<?=$i?>" class="btn btn-xs btn-danger">delete</a> +<?php endif; ?> + </td> + </tr> +<?php endforeach; ?> + </tbody> +</table> +</div> +<nav class="action-buttons"> + <a href="?act=new" class="btn btn-success">add new</a> +</nav> +<p> + <?=gettext("Additional users can be added here. User permissions for accessing " . + "the webConfigurator can be assigned directly or inherited from group memberships. " . + "An icon that appears grey indicates that it is a system defined object. " . + "Some system object properties can be modified but they cannot be deleted.")?> + <br /><br /> + <?=gettext("Accounts created here are also used for other parts of the system " . + "such as OpenVPN, IPsec, and Captive Portal.")?> +</p> +<?php + include("foot.inc"); + exit; +} + +require('classes/Form.class.php'); +$form = new Form; +$form->setAction('system_usermanager.php?act=edit'); +$form->addGlobal(new Form_Input( + 'userid', + null, + 'hidden', + $id +)); +$form->addGlobal(new Form_Input( + 'utype', + null, + 'hidden', + $pconfig['utype'] +)); +$form->addGlobal(new Form_Input( + 'oldusername', + null, + 'hidden', + $pconfig['usernamefld'] +)); + +$section = new Form_Section('User Properties'); + +$section->addInput(new Form_StaticText( + 'Defined by', + strtoupper($pconfig['utype']) +)); + +?> +<?php +$ro = false; +if ($pconfig['utype'] == "system") + $ro = true; + +$section->addInput(new Form_Checkbox( + 'disabled', + 'Disabled', + 'This user cannot login', + $pconfig['disabled'] +)); + +$section->addInput($input = new Form_Input( + 'usernamefld', + 'Username', + 'text', + $pconfig['usernamefld'] +)); + +if ($ro) + $input->setDisabled(); + +$group = new Form_Group('Password'); +$group->add(new Form_Input( + 'passwordfld1', + 'Password', + 'password' +)); +$group->add(new Form_Input( + 'passwordfld2', + 'Confirm Password', + 'password' +)); + +$section->add($group); + +$section->addInput($input = new Form_Input( + 'descr', + 'Full name', + 'text', + htmlspecialchars($pconfig['descr']) +))->setHelp('User\'s full name, for your own information only'); + +if ($ro) + $input->setDisabled(); + +$section->addInput(new Form_Input( + 'expires', + 'Expiration date', + 'date', + $pconfig['expires'] +))->setHelp('Leave blank if the account shouldn\'t expire, otherwise enter '. + 'the expiration date'); + +$systemGroups = array(); +foreach ($config['system']['group'] as $group) + $systemGroups[ $group['name'] ] = $group['name']; + +$section->addInput(new Form_Select( + 'groups', + 'Group Memberships', + array_combine((array)$pconfig['groups'], (array)$pconfig['groups']), + $systemGroups, + true +))->setHelp('Hold down CTRL (pc)/COMMAND (mac) key to select multiple items'); + +$form->add($section); + +if (isset($pconfig['uid'])) +{ + $section = new Form_Section('Effective Privileges'); + + foreach (get_user_privdesc($a_user[$id]) as $i => $priv) + { + // We reverse name and action for readability of longer names + $input = new Form_Checkbox( + 'privid[]', + null, + $priv['name'], + false, + $i + ); + + if ($priv['group']) + { + $group = new Form_Group('Inherited from '. $priv['group']); + $input->setDisabled(); + } + else + $group = new Form_Group('Revoke privilege'); + + $group->add($input); + $section->add($group); + } + + $section->addInput(new Form_StaticText( + null, + new Form_Button(null, 'grant more privileges', 'system_usermanager_addprivs.php?userid='. $id) + )); + + $form->add($section); + + $section = new Form_Section('User Certificates'); + + foreach ((array)$a_user[$id]['cert'] as $i => $certref) + { + $cert = lookup_cert($certref); + $ca = lookup_ca($cert['caref']); + + // We reverse name and action for readability of longer names + $section->addInput(new Form_Checkbox( + 'certid[]', + 'Delete certificate', + $cert['descr']. (is_cert_revoked($cert) ? ' <b>revoked</b>' : ''), + false, + $i + )); + } + + #FIXME; old ui supplied direct export links to each certificate + + $section->addInput(new Form_StaticText( + null, + new Form_Button(null, 'add certificate', 'system_certmanager.php?act=new&userid='. $id). + new Form_Button(null, 'export certificates', 'system_certmanager.php') + )); +} +else +{ + if (is_array($config['ca']) && count($config['ca']) > 0) + { + $section = new Form_Section('Create certificate for user'); + + $nonPrvCas = array(); + foreach( $config['ca'] as $ca) + { + if (!$ca['prv']) + continue; + + $nonPrvCas[ $ca['refid'] ] = $ca['descr']; + } + + if (!empty($nonPrvCas)) + { + $section->addInput(new Form_Input( + 'name', + 'Descriptive name', + 'text', + $pconfig['name'] + )); + + $section->addInput(new Form_Select( + 'caref', + 'Certificate authority', + null, + $nonPrvCas + )); + + $section->addInput(new Form_Select( + 'keylen', + 'Key length', + 2048, + array( + 512 => '512 bits', + 1024 => '1024 bits', + 2048 => '2049 bits', + 4096 => '4096 bits', + ) + )); + + $section->addInput(new Form_Input( + 'lifetime', + 'Lifetime', + 'number', + $pconfig['lifetime'] + )); + } + + $form->add($section); + } +} + +$section = new Form_Section('Keys'); + +$section->addInput(new Form_Textarea( + 'authorizedkeys', + 'Authorized keys', + 'text', + $pconfig['authorizedkeys'] +))->setHelp('Paste an authorized keys file here.'); + +$section->addInput(new Form_Input( + 'ipsecpsk', + 'IPsec Pre-Shared Key', + 'text', + $pconfig['ipsecpsk'] +)); + +$form->add($section); +print $form; + +include('foot.inc');
\ No newline at end of file diff --git a/src/usr/local/www/system_usermanager_addprivs.php b/src/usr/local/www/system_usermanager_addprivs.php new file mode 100644 index 0000000..2e05769 --- /dev/null +++ b/src/usr/local/www/system_usermanager_addprivs.php @@ -0,0 +1,214 @@ +<?php +/* $Id$ */ +/* + system_usermanager_addprivs.php + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Copyright (C) 2006 Daniel S. Haischt. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: auth +*/ + +##|+PRIV +##|*IDENT=page-system-usermanager-addprivs +##|*NAME=System: User Manager: Add Privileges page +##|*DESCR=Allow access to the 'System: User Manager: Add Privileges' page. +##|*MATCH=system_usermanager_addprivs.php* +##|-PRIV + +function admusercmp($a, $b) { + return strcasecmp($a['name'], $b['name']); +} + +require("guiconfig.inc"); + +$pgtitle = array("System", "User manager", "Add privileges"); + +if (is_numericint($_GET['userid'])) { + $userid = $_GET['userid']; +} + +if (isset($_POST['userid']) && is_numericint($_POST['userid'])) { + $userid = $_POST['userid']; +} + +if (!isset($config['system']['user'][$userid]) && !is_array($config['system']['user'][$userid])) { + pfSenseHeader("system_usermanager.php"); + exit; +} + +$a_user = & $config['system']['user'][$userid]; + +if (!is_array($a_user['priv'])) { + $a_user['priv'] = array(); +} + +if ($_POST) { + conf_mount_rw(); + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "sysprivs"); + $reqdfieldsn = array(gettext("Selected privileges")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + + if (!$input_errors) { + + if (!is_array($pconfig['sysprivs'])) { + $pconfig['sysprivs'] = array(); + } + + if (!count($a_user['priv'])) { + $a_user['priv'] = $pconfig['sysprivs']; + } else { + $a_user['priv'] = array_merge($a_user['priv'], $pconfig['sysprivs']); + } + + $a_user['priv'] = sort_user_privs($a_user['priv']); + local_user_set($a_user); + $retval = write_config(); + $savemsg = get_std_save_message($retval); + conf_mount_ro(); + + post_redirect("system_usermanager.php", array('act' => 'edit', 'userid' => $userid)); + + exit; + } + + conf_mount_ro(); +} + +function build_priv_list() { + global $priv_list, $a_user; + + $list = array(); + + foreach($priv_list as $pname => $pdata) { + if (in_array($pname, $a_user['priv'])) + continue; + + $list[$pname] = $pdata['name']; + } + + return($list); +} + +/* if ajax is calling, give them an update message */ +if (isAjax()) { + print_info_box_np($savemsg); +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +$tab_array = array(); +$tab_array[] = array(gettext("Users"), true, "system_usermanager.php"); +$tab_array[] = array(gettext("Groups"), false, "system_groupmanager.php"); +$tab_array[] = array(gettext("Settings"), false, "system_usermanager_settings.php"); +$tab_array[] = array(gettext("Servers"), false, "system_authservers.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('User privileges'); + +$section->addInput(new Form_Select( + 'sysprivs', + 'System', + null, + build_priv_list(), + true +))->addClass('multiselect')->setHelp('Hold down CTRL (PC)/COMMAND (Mac) key to select multiple items'); + +if (isset($userid)) { + $section->addInput(new Form_Input( + 'userid', + null, + 'hidden', + $userid + )); +} + +$form->add($section); + +print($form); +?> + +<div class="panel panel-body alert-info" id="pdesc">Select a privilege from the list above for a description"</div> + +<script> +//<![CDATA[ +events.push(function(){ + +<?php + + // Build a list of privilege descriptions + if (is_array($priv_list)) { + $id = 0; + + $jdescs = "var descs = new Array();\n"; + foreach ($priv_list as $pname => $pdata) { + if (in_array($pname, $a_user['priv'])) { + continue; + } + $desc = addslashes(preg_replace("/pfSense/i", $g['product_name'], $pdata['descr'])); + $jdescs .= "descs[{$id}] = '{$desc}';\n"; + $id++; + } + + echo $jdescs; + } +?> + // Set the number of options to display + $('.multiselect').attr("size","20"); + + // When the 'sysprivs" selector is clicked, we display a description + $('.multiselect').click(function() { + $('#pdesc').html(descs[$(this).children('option:selected').index()]); + }); +}); +//]]> +</script> + +<?php include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_usermanager_passwordmg.php b/src/usr/local/www/system_usermanager_passwordmg.php new file mode 100644 index 0000000..cb02942 --- /dev/null +++ b/src/usr/local/www/system_usermanager_passwordmg.php @@ -0,0 +1,128 @@ +<?php +/* $Id$ */ +/* + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2011 Ermal Luçi + system_usermanager_passwordmg.php + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: + pfSense_MODULE: auth +*/ + +##|+PRIV +##|*IDENT=page-system-usermanager-passwordmg +##|*NAME=System: User Password Manager page +##|*DESCR=Allow access to the 'System: User Password Manager' page. +##|*MATCH=system_usermanager_passwordmg.php* +##|-PRIV + +require_once("auth.inc"); +require_once("certs.inc"); +require_once("guiconfig.inc"); + +$pgtitle = array(gettext("System"), gettext("User Password")); + +if (isset($_POST['save'])) { + unset($input_errors); + /* input validation */ + + $reqdfields = explode(" ", "passwordfld1"); + $reqdfieldsn = array(gettext("Password")); + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ($_POST['passwordfld1'] != $_POST['passwordfld2']) { + $input_errors[] = gettext("The passwords do not match."); + } + + if (!$input_errors) { + if (!session_id()) { + session_start(); + } + // all values are okay --> saving changes + + $userent =& $config['system']['user'][$userindex[$_SESSION['Username']]]; + local_user_set_password($userent, $_POST['passwordfld1']); + local_user_set($userent); + unset($userent); + session_commit(); + + write_config(); + + $savemsg = gettext("Password successfully changed") . "<br />"; + } +} + +if (!session_id()) { + session_start(); +} + +/* determine if user is not local to system */ +$islocal = false; +foreach ($config['system']['user'] as $user) { + if ($user['name'] == $_SESSION['Username']) { + $islocal = true; + } +} + +session_commit(); + +include("head.inc"); + +if ($input_errors) { + print_input_errors($input_errors); +} + +if ($savemsg) { + print_info_box($savemsg); +} + +if ($islocal == false) { + echo gettext("Sorry, you cannot change the password for a non-local user."); + include("foot.inc"); + exit; +} + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('Update Password'); + +$section->addInput(new Form_Input( + 'passwordfld1', + 'Password', + 'password' +)); + +$section->addInput(new Form_Input( + 'passwordfld2', + 'Confirmation', + 'password' +))->setHelp('Select a new password'); + +$form->add($section); +print($form); + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_usermanager_settings.php b/src/usr/local/www/system_usermanager_settings.php new file mode 100644 index 0000000..a89970e --- /dev/null +++ b/src/usr/local/www/system_usermanager_settings.php @@ -0,0 +1,154 @@ +<?php +/* $Id$ */ +/* + system_usermanager_settings.php + + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2007 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2007 Bill Marquette <bill.marquette@gmail.com> + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: auth +*/ + +##|+PRIV +##|*IDENT=page-system-usermanager-settings +##|*NAME=System: User Manager: settings page +##|*DESCR=Allow access to the 'System: User Manager: settings' page. +##|*MATCH=system_usermanager_settings.php* +##|-PRIV + +require("guiconfig.inc"); + +$pconfig['session_timeout'] = &$config['system']['webgui']['session_timeout']; +$pconfig['authmode'] = &$config['system']['webgui']['authmode']; +$pconfig['backend'] = &$config['system']['webgui']['backend']; + +// Page title for main admin +$pgtitle = array(gettext("System"), gettext("User manager settings")); + +$save_and_test = false; +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + if (isset($_POST['session_timeout'])) { + $timeout = intval($_POST['session_timeout']); + if ($timeout != "" && (!is_numeric($timeout) || $timeout <= 0)) { + $input_errors[] = gettext("Session timeout must be an integer value."); + } + } + + if (!$input_errors) { + if ($_POST['authmode'] != "local") { + $authsrv = auth_get_authserver($_POST['authmode']); + if ($_POST['savetest']) { + if ($authsrv['type'] == "ldap") { + $save_and_test = true; + } else { + $savemsg = gettext("The test was not performed because it is supported only for ldap based backends."); + } + } + } + + + if (isset($_POST['session_timeout']) && $_POST['session_timeout'] != "") { + $config['system']['webgui']['session_timeout'] = intval($_POST['session_timeout']); + } else { + unset($config['system']['webgui']['session_timeout']); + } + + if ($_POST['authmode']) { + $config['system']['webgui']['authmode'] = $_POST['authmode']; + } else { + unset($config['system']['webgui']['authmode']); + } + + write_config(); + + } +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); +if ($savemsg) + print_info_box($savemsg); + +if($save_and_test) { + echo "<script>\n"; + echo "myRef = window.open('system_usermanager_settings_test.php?authserver=".$pconfig['authmode']."','mywin','left=20,top=20,width=700,height=550,toolbar=1,resizable=0');\n"; + echo "if (myRef==null || typeof(myRef)=='undefined') alert('" . gettext("Popup blocker detected. Action aborted.") ."');\n"; + echo "</script>\n"; +} + +$tab_array = array(); +$tab_array[] = array(gettext("Users"), false, "system_usermanager.php"); +$tab_array[] = array(gettext("Groups"), false, "system_groupmanager.php"); +$tab_array[] = array(gettext("Settings"), true, "system_usermanager_settings.php"); +$tab_array[] = array(gettext("Servers"), false, "system_authservers.php"); +display_top_tabs($tab_array); + +/* Default to pfsense backend type if none is defined */ +if(!$pconfig['backend']) + $pconfig['backend'] = "pfsense"; + +require('classes/Form.class.php'); +$form = new Form; + +$section = new Form_Section('Settings'); + +$section->addInput(new Form_Input( + 'session_timeout', + 'Session timeout', + 'number', + $pconfig['session_timeout'] +))->setHelp('Time in minutes to expire idle management sessions. The default is 4 '. + 'hours (240 minutes).Enter 0 to never expire sessions. NOTE: This is a security '. + 'risk!'); + +$auth_servers = array(); +foreach (auth_get_authserver_list() as $auth_server) + $auth_servers[ $auth_server['name'] ] = $auth_server['name']; + +$section->addInput(new Form_Select( + 'authmode', + 'Authentication Server', + $pconfig['authmode'], + $auth_servers +)); + +$form->addGlobal(new Form_Button( + 'savetest', + 'Save & Test' +))->removeClass('btn-primary')->addClass('btn-default'); + +$form->add($section); +print $form; + +include("fend.inc");
\ No newline at end of file diff --git a/src/usr/local/www/system_usermanager_settings_ldapacpicker.php b/src/usr/local/www/system_usermanager_settings_ldapacpicker.php new file mode 100644 index 0000000..068142e --- /dev/null +++ b/src/usr/local/www/system_usermanager_settings_ldapacpicker.php @@ -0,0 +1,131 @@ +<?php +/* $Id$ */ +/* + system_usermanager_settings_ldapacpicker.php + part of pfSense (https://www.pfsense.org/) + Copyright (C) 2007 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: auth +*/ + +require("guiconfig.inc"); +require_once("auth.inc"); + +$ous = array(); + +if ($_GET) { + $authcfg = array(); + $authcfg['ldap_port'] = $_GET['port']; + $authcfg['ldap_basedn'] = $_GET['basedn']; + $authcfg['host'] = $_GET['host']; + $authcfg['ldap_scope'] = $_GET['scope']; + $authcfg['ldap_binddn'] = $_GET['binddn']; + $authcfg['ldap_bindpw'] = $_GET['bindpw']; + $authcfg['ldap_urltype'] = $_GET['urltype']; + $authcfg['ldap_protver'] = $_GET['proto']; + $authcfg['ldap_authcn'] = explode(";", $_GET['authcn']); + $authcfg['ldap_caref'] = $_GET['cert']; + $ous = ldap_get_user_ous(true, $authcfg); +} + +?> +<html> + <head> + <STYLE type="text/css"> + TABLE { + border-width: 1px 1px 1px 1px; + border-spacing: 0px; + border-style: solid solid solid solid; + border-color: gray gray gray gray; + border-collapse: separate; + background-color: collapse; + } + TD { + border-width: 0px 0px 0px 0px; + border-spacing: 0px; + border-style: solid solid solid solid; + border-color: gray gray gray gray; + border-collapse: collapse; + background-color: white; + } + </STYLE> + </head> +<script type="text/javascript"> +function post_choices() { + + var ous = <?php echo count($ous); ?>; + var i; + opener.document.forms[0].ldapauthcontainers.value=""; + for (i = 0; i < ous; i++) { + if (document.forms[0].ou[i].checked) { + if (opener.document.forms[0].ldapauthcontainers.value != "") { + opener.document.forms[0].ldapauthcontainers.value+=";"; + } + opener.document.forms[0].ldapauthcontainers.value+=document.forms[0].ou[i].value; + } + } + window.close(); +--> +} +</script> + +<body link="#0000CC" vlink="#0000CC" alink="#0000CC" > +<form method="post" action="system_usermanager_settings_ldapacpicker.php"> +<?php if (empty($ous)): ?> + <p><?=gettext("Could not connect to the LDAP server. Please check your LDAP configuration.");?></p> + <input type='button' value='<?=gettext("Close"); ?>' onClick="window.close();"> +<?php else: ?> + <b><?=gettext("Please select which containers to Authenticate against:");?></b> + <p/> + <table width="100%" border="0" cellpadding="0" cellspacing="0"> + <tr> + <td class="tabnavtbl"> + <table width="100%"> +<?php + if (is_array($ous)) { + foreach ($ous as $ou) { + if (in_array($ou, $authcfg['ldap_authcn'])) { + $CHECKED=" CHECKED"; + } else { + $CHECKED=""; + } + echo " <tr><td><input type='checkbox' value='{$ou}' id='ou' name='ou[]'{$CHECKED}> {$ou}<br /></td></tr>\n"; + } + } +?> + </table> + </td> + </tr> + </table> + + <p/> + + <input type='button' value='<?=gettext("Save");?>' onClick="post_choices();"> +<?php endif; ?> +</form> +</body> +</html> diff --git a/src/usr/local/www/system_usermanager_settings_test.php b/src/usr/local/www/system_usermanager_settings_test.php new file mode 100755 index 0000000..f2cdef8 --- /dev/null +++ b/src/usr/local/www/system_usermanager_settings_test.php @@ -0,0 +1,117 @@ +<?php +/* $Id$ */ +/* + system_usermanager_settings_test.php + part of pfSense (https://www.pfsense.org/) + Copyright (C) 2007 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2014 Silvio Giunge <desenvolvimento@bluepex.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: auth +*/ + +##|+PRIV +##|*IDENT=page-system-usermanager-settings-testldap +##|*NAME=System: User Manager: Settings: Test LDAP page +##|*DESCR=Allow access to the 'System: User Manager: Settings: Test LDAP' page. +##|*MATCH=system_usermanager_settings_test.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("auth.inc"); + +if (isset($config['system']['authserver'][0]['host'])) { + $auth_server = $config['system']['authserver'][0]['host']; + $authserver = $_GET['authserver']; + $authcfg = auth_get_authserver($authserver); +} + +?><!DOCTYPE html> +<html lang="en"> +<head> + <link rel="stylesheet" href="/bootstrap/css/pfSense.css" /> + <title><?=gettext("Test Authentication server"); ?></title> +</head> +<body id="system_usermanager_settings_test" class="no-menu"> + <div id="jumbotron"> + <div class="container"> + <div class="col-sm-offset-3 col-sm-6 col-xs-12"> + <pre> +<?php + +if (!$authcfg) { + printf(gettext("Could not find settings for %s%s"), htmlspecialchars($authserver), "<p/>"); +} else { + echo "<b>" . sprintf(gettext("Testing %s LDAP settings... One moment please..."), $g['product_name']) . "</b>"; + + echo "<table>"; + + echo "<tr><td>" . gettext("Attempting connection to") . " " . "<td><center>$auth_server</b></center></td>"; + if (ldap_test_connection($authcfg)) { + echo "<td><center><font color=green>OK</center></td></tr>"; + + echo "<tr><td>" . gettext("Attempting bind to") . " " . "<td><center>$auth_server</b></center></td>"; + if (ldap_test_bind($authcfg)) { + echo "<td><center><font color=green>OK</center></td></tr>"; + + echo "<tr><td>" . gettext("Attempting to fetch Organizational Units from") . " " . "<td><center>$auth_server</b></center></td>"; + $ous = ldap_get_user_ous(true, $authcfg); + if (count($ous)>1) { + echo "<td><center><font color=green>OK</center></td></tr>"; + echo "</table>"; + if (is_array($ous)) { + echo "<br/>"; + echo "<b>" . gettext("Organization units found") . "</b>"; + echo "<table width='100%'>"; + foreach ($ous as $ou) { + echo "<tr><td onmouseover=\"this.style.backgroundColor='#ffffff';\" onmouseout=\"this.style.backgroundColor='#dddddd';\">" . $ou . "</td></tr>"; + } + } + } else { + echo "<td><font color=red>" . gettext("failed") . "</td></tr>"; + } + + echo "</table><p/>"; + + } else { + echo "<td><font color=red>" . gettext("failed") . "</td></tr>"; + echo "</table><p/>"; + } + } else { + echo "<td><font color=red>" . gettext("failed") . "</td></tr>"; + echo "</table><p/>"; + } +} + +?> + </pre> + + <a href="javascript:window.close();" class="btn btn-primary">Return</a> + </div> + </div> + </div> +</body> +</html>
\ No newline at end of file diff --git a/src/usr/local/www/tree/i-bottom.gif b/src/usr/local/www/tree/i-bottom.gif Binary files differnew file mode 100755 index 0000000..f07fa99 --- /dev/null +++ b/src/usr/local/www/tree/i-bottom.gif diff --git a/src/usr/local/www/tree/i-repeater.gif b/src/usr/local/www/tree/i-repeater.gif Binary files differnew file mode 100755 index 0000000..d5ab089 --- /dev/null +++ b/src/usr/local/www/tree/i-repeater.gif diff --git a/src/usr/local/www/tree/index.html b/src/usr/local/www/tree/index.html new file mode 100755 index 0000000..f2c45d4 --- /dev/null +++ b/src/usr/local/www/tree/index.html @@ -0,0 +1,228 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" > + + <head> + <title>SilverStripe Tree Control</title> + <link rel="stylesheet" type="text/css" media="all" href="tree.css" /> + <script type="text/javascript" src="tree.js"></script> + <style> + html { + background-color: #DDD; + } + body { + font-size: 80%; + font-family: Arial, Helvetica, sans-serif; + width: 50em; + margin: 0 auto 3.5em auto; + padding: 1em; + background-color: white; + border-left: 1px #CCC solid; + border-right: 1px #CCC solid; + } + ul.tree a { + font-size: 0.8em; + + } + code { + display: block; + font-size: 1.2em; + margin: 2em 5em; + padding: 0.5em; + border: 1px #CCC solid; + background-color: #EEE; + } + #version { + float: right; + font-style: italic; + margin-top: -4em; + } + + dt { + margin: 1.2em 0 0.2em 0; + font-weight: bold; + font-size: 1.1em + } + dd { + margin: 0; + } + h2 { + margin-top: 4em; + border-bottom: 1px #CCC dotted; + } + + #footer { + position: fixed; + bottom: 0; + left: 0; + width: 100%; + height: 3em; + } + #footer div { + margin: auto; + width: 52em; + height: 3em; + background-color: #777; + color: white; + } + #footer a { + color: white; + } + #footer p.left { + float: left; + margin: 0.75em 1em; + } + #footer p.right { + float: right; + margin: 0.75em 1em; + } + + </style> + </head> + +<body> + + <h1>SilverStripe Tree Control</h1> + <p>This tree control was put together by <a href="sam@silverstripe.com">Sam Minnée</a> at + <a href="http://www.silverstripe.com/blog">SilverStripe</a> in New Zealand. We've put it out there + for everyone to enjoy. Check out <a href="http://www.silverstripe.com/blog">our blog</a> if you're + wondering what we're up to.</p> + <p>This file came from <a href="http://www.silverstripe.com/downloads/tree/">http://www.silverstripe.com/downloads/tree/</a>. + If you found this file elsewhere, check out that page: we might have posted an updated version.</p> + + <p> + <b>Quick-links:</b> + <a href="#Demo">Demo</a> | <a href="#Usage">Usage</a> | <a href="#Download">Download</a> | <a href="#HowItWorks">How it Works</a> + </p> + + <h2 id="Demo">Demo</h2> + <p>Here's a basic demo of the tree control. Our styling is fairly basic, but with updated CSS and + images you can do whatever you like. Just for fun, try changing the text size.</p> + + <ul class="tree"> + <li><a href="#">item 1</a> + <ul> + <li><a href="#">item 1.1</a></li> + <li class="closed"><a href="#">item 1.2</a> + <ul> + <li><a href="#">item 1.2.1</a></li> + <li><a href="#">item 1.2.2</a></li> + <li><a href="#">item 1.2.3</a></li> + </ul> + </li> + <li><a href="#">item 1.3</a></li> + </ul> + </li> + <li><a href="#">item 2</a> + <ul> + <li><a href="#">item 2.1</a></li> + <li><a href="#">item 2.2</a></li> + <li><a href="#">item 2.3</a></li> + </ul> + </li> + </ul> + + <h2 id="Download">Download</h2> + + <p><a href="../tree.zip">Download everything you need here</a> - tree.zip, 11kb</p> + + <h2 id="Usage">Usage</h2> + + <p>The first thing to do is include the appropriate JavaScript and CSS files:</p> + + <code> + <link rel="stylesheet" type="text/css" media="all" href="tree.css" /><br /> + <script type="text/javascript" src="tree.js"></script> + </code> + + <p>Then, create the HTML for you tree. This is basically a nested set of bullet pointed links. The + "tree" class at the top is what the script will look for. Note that you can make a tree ndoe closed + to begin with by adding class="closed".</p> + + <p>Here's the HTML code that I inserted to create the demo tree above.</p> + + <code> + <ul class="tree"><br /> + <li><a href="#">item 1</a><br /> + <ul><br /> + <li><a href="#">item 1.1</a></li><br /> + <li class="closed"><a href="#">item 1.2</a><br /> + <ul><br /> + <li><a href="#">item 1.2.1</a></li><br /> + <li><a href="#">item 1.2.2</a></li><br /> + <li><a href="#">item 1.2.3</a></li><br /> + </ul> <br /> + </li><br /> + <li><a href="#">item 1.3</a></li><br /> + </ul> <br /> + </li><br /> + <li><a href="#">item 2</a><br /> + <ul><br /> + <li><a href="#">item 2.1</a></li><br /> + <li><a href="#">item 2.2</a></li><br /> + <li><a href="#">item 2.3</a></li><br /> + </ul> <br /> + </li><br /> + </ul> + </code> + + <p>Your tree is now complete!</p> + + <h2 id="HowItWorks">How it works</h2> + <dl> + <dt>Starting the script</dt> + <dd>In simple situations, creating an auto-loading script is a simple matter of setting window.onload + to a function. But what if there's more than one script? To this end, we created an appendLoader() + function that will execute multiple loader functions, including a previously defined loader function</dd> + + <dt>Finding the tree content</dt> + <dd>Rather than write a piece of script to define we're your tree is, we've tried to make the script + as automatic as possible - it finds all ULs with a class name containing "tree".</dd> + + <dt>Augmenting the HTML</dt> + <dd>Unfortunately, an LI containing an A isn't sufficient for doing all of the necessary tree styling. + Rather than force people to put non-semantic HTML into their file, the script generates extra <span> tags. + So, the following HTML: + + <code> + <li><a href="#">My item</a></li> + </code> + + Is turned into the more ungainly, and yet more easily styled: + + <code> + <li><span class="a"><span class="b"><span class="c"><a href="#">My item</a></span></span></span></li> + </code> + + Additionally, some helper classes are applied to the <li> and <span class="a"> elements: + <ul> + <li>"last" is applied to the last node of any subtree.</li> + <li>"children" is applied to any node that has children.</li> + </ul> + </dd> + + <dt>Styling it up</dt> + <dd>Why the heck do we need 5 styling elements? Basically, because there are 5 background-images to apply: + <ul> + <li><b>li:</b> A repeating vertical line is shown. Nested <li> tags + give us the multiple vertical lines that we need.</li> + <li><b>span.a:</b> We overlay the vertical line with 'L' and 'T' elements as needed.</li> + <li><b>span.b:</b> We overlay '+' or '-' signs on nodes with children.</li> + <li><b>span.c:</b> This is needed to fix up the vertical line.</li> + <li><b>a:</b> Finally, we apply the page icon.</li> + </ul> + </dd> + + <dt>Opening / closing nodes</dt> + <dd>Having come this far, the "dynamic" aspect of the tree control is very trivial. We set a "closed" + class on the <li> and <span class="a"> elements, and our CSS takes care of hiding the + children, changing the - to a + and changing the folder icon.</dd> + </dl> + + <div id="footer"> + <div> + <p class="left"><a href="http://www.silverstripe.com/downloads/tree">SilverStripe Tree Control</a>: v0.1, 30 Oct 2005</p> + <p class="right">Copyright © 2005 <a href="http://www.silverstripe.com/blog">SilverStripe Limited</a></p> + </div> + </div> + </body> +</html> diff --git a/src/usr/local/www/tree/l.gif b/src/usr/local/www/tree/l.gif Binary files differnew file mode 100755 index 0000000..1e8c707 --- /dev/null +++ b/src/usr/local/www/tree/l.gif diff --git a/src/usr/local/www/tree/minus.gif b/src/usr/local/www/tree/minus.gif Binary files differnew file mode 100755 index 0000000..7a7fd3b --- /dev/null +++ b/src/usr/local/www/tree/minus.gif diff --git a/src/usr/local/www/tree/page-file.png b/src/usr/local/www/tree/page-file.png Binary files differnew file mode 100755 index 0000000..d3bb119 --- /dev/null +++ b/src/usr/local/www/tree/page-file.png diff --git a/src/usr/local/www/tree/page-file_play.gif b/src/usr/local/www/tree/page-file_play.gif Binary files differnew file mode 100755 index 0000000..0c8e9ff --- /dev/null +++ b/src/usr/local/www/tree/page-file_play.gif diff --git a/src/usr/local/www/tree/page-file_x.gif b/src/usr/local/www/tree/page-file_x.gif Binary files differnew file mode 100755 index 0000000..504f06e --- /dev/null +++ b/src/usr/local/www/tree/page-file_x.gif diff --git a/src/usr/local/www/tree/page-foldericon.png b/src/usr/local/www/tree/page-foldericon.png Binary files differnew file mode 100755 index 0000000..d26f2dc --- /dev/null +++ b/src/usr/local/www/tree/page-foldericon.png diff --git a/src/usr/local/www/tree/page-openfoldericon.png b/src/usr/local/www/tree/page-openfoldericon.png Binary files differnew file mode 100755 index 0000000..8d00c39 --- /dev/null +++ b/src/usr/local/www/tree/page-openfoldericon.png diff --git a/src/usr/local/www/tree/plus.gif b/src/usr/local/www/tree/plus.gif Binary files differnew file mode 100755 index 0000000..3530f59 --- /dev/null +++ b/src/usr/local/www/tree/plus.gif diff --git a/src/usr/local/www/tree/t.gif b/src/usr/local/www/tree/t.gif Binary files differnew file mode 100755 index 0000000..a92da2a --- /dev/null +++ b/src/usr/local/www/tree/t.gif diff --git a/src/usr/local/www/tree/tree.css b/src/usr/local/www/tree/tree.css new file mode 100755 index 0000000..31b7979 --- /dev/null +++ b/src/usr/local/www/tree/tree.css @@ -0,0 +1,136 @@ +/* + * CSS for Standard tree layout + * Copyright (C) 2005 SilverStripe Limited + * Feel free to use this on your websites, but please leave this message in the fies + * http://www.silverstripe.com/blog + */ + +ul.tree{ + width: auto; + padding-left : 0px; + margin-left : 0px; +} + +ul.tree img{ + border : none; +} + + +ul.tree, ul.tree ul { + padding-left: 0; +} + +ul.tree ul { + margin-left: 16px; + +} +ul.tree li.closed ul { + display: none; +} + + +ul.tree li { + list-style: none; + background: url(i-repeater.gif) 0 0 repeat-y; + display: block; + width: auto; + /* background-color:#FFFFFF; */ +} + + + +ul.tree li.last { + list-style: none; + background-image: none; +} + + + +/* Span-A: I/L/I glpyhs */ +ul.tree span.a { + background: url(t.gif) 0 50% no-repeat; + display: block; +} +ul.tree span.a.last { + background: url(l.gif) 0 50% no-repeat; +} + +/* Span-B: Plus/Minus icon */ +ul.tree span.b { +} +ul.tree span.a.children span.b { + background: url(minus.gif) 0 50% no-repeat; + cursor: pointer; +} +ul.tree li.closed span.a.children span.b { + background: url(plus.gif) 0 50% no-repeat; + cursor: pointer; +} + +/* Span-C: Spacing and extending tree line below the icon */ +ul.tree span.c { + margin-left: 16px; +} +ul.tree span.a.children span.c { + background: url(i-bottom.gif) 0 50% no-repeat; +} +ul.tree span.a.spanClosed span.c { + background-image: none; +} + + +/* Anchor tag: Page icon */ +ul.tree a { + white-space: nowrap; + overflow: hidden; + +/* padding: 10px 0px 10px 18px; */ + padding: 3px 0px 3px 18px; + line-height: 16px; + +/* background: url(page-file.png) 0 50% no-repeat; */ + background: url(page-file.png) 0 0 no-repeat; +} +ul.tree span.a.children a { + background-image: url(page-openfoldericon.png); +} +ul.tree span.a.children.spanClosed a { + background-image: url(page-foldericon.png); +} + +/* Unformatted tree */ +ul.tree.unformatted li { + background-image: none; + padding-left: 16px; +} +ul.tree.unformatted li li { + background-image: none; + padding-left: 0px; +} + +/* + * Divs, by default store vertically aligned data + */ + +ul.tree li div { + float: right; + clear: right; + height: 1em; + margin-top: -26px; +} +/* As inside DIVs should be treated normally */ +ul.tree div a { + padding: 0; + background-image: none; + min-height: auto; + height: auto; +} + +ul.tree li A:link, ul.tree li A:hover, ul.tree li A:visited { + color : #111111; +} + + +ul.tree li .over{ + background-color : pink; +} diff --git a/src/usr/local/www/tree/tree.js b/src/usr/local/www/tree/tree.js new file mode 100755 index 0000000..8e9651e --- /dev/null +++ b/src/usr/local/www/tree/tree.js @@ -0,0 +1,195 @@ +/* + * Content-separated javascript tree widget + * Copyright (C) 2005 SilverStripe Limited + * Feel free to use this on your websites, but please leave this message in the fies + * http://www.silverstripe.com/blog +*/ + +/* + * Initialise all trees identified by <ul class="tree"> + */ +function autoInit_trees() { + var candidates = document.getElementsByTagName('ul'); + for(var i=0;i<candidates.length;i++) { + if(candidates[i].className && candidates[i].className.indexOf('tree') != -1) { + initTree(candidates[i]); + candidates[i].className = candidates[i].className.replace(/ ?unformatted ?/, ' '); + } + } +} + +/* + * Initialise a tree node, converting all its LIs appropriately + */ +function initTree(el) { + var i,j; + var spanA, spanB, spanC; + var startingPoint, stoppingPoint, childUL; + + // Find all LIs to process + for(i=0;i<el.childNodes.length;i++) { + if(el.childNodes[i].tagName && el.childNodes[i].tagName.toLowerCase() == 'li') { + var li = el.childNodes[i]; + + // Create our extra spans + spanA = document.createElement('span'); + spanB = document.createElement('span'); + spanC = document.createElement('span'); + spanA.appendChild(spanB); + spanB.appendChild(spanC); + spanA.className = 'a ' + li.className.replace('closed','spanClosed'); + spanA.onMouseOver = function() {}; + spanB.className = 'b'; + spanB.onclick = treeToggle; + spanC.className = 'c'; + + + // Find the UL within the LI, if it exists + stoppingPoint = li.childNodes.length; + startingPoint = 0; + childUL = null; + for(j=0;j<li.childNodes.length;j++) { + if(li.childNodes[j].tagName && li.childNodes[j].tagName.toLowerCase() == 'div') { + startingPoint = j + 1; + continue; + } + + if(li.childNodes[j].tagName && li.childNodes[j].tagName.toLowerCase() == 'ul') { + childUL = li.childNodes[j]; + stoppingPoint = j; + break; + } + } + + // Move all the nodes up until that point into spanC + for(j=startingPoint;j<stoppingPoint;j++) { + spanC.appendChild(li.childNodes[startingPoint]); + } + + // Insert the outermost extra span into the tree + if(li.childNodes.length > startingPoint) li.insertBefore(spanA, li.childNodes[startingPoint]); + else li.appendChild(spanA); + + // Process the children + if(childUL != null) { + if(initTree(childUL)) { + addClass(li, 'children', 'closed'); + addClass(spanA, 'children', 'spanClosed'); + } + } + } + } + + if(li) { + // li and spanA will still be set to the last item + + addClass(li, 'last', 'closed'); + addClass(spanA, 'last', 'spanClosed'); + return true; + } else { + return false; + } + +} + + +/* + * +/- toggle the tree, where el is the <span class="b"> node + * force, will force it to "open" or "close" + */ +function treeToggle(el, force) { + el = this; + + while(el != null && (!el.tagName || el.tagName.toLowerCase() != "li")) el = el.parentNode; + + // Get UL within the LI + var childSet = findChildWithTag(el, 'ul'); + var topSpan = findChildWithTag(el, 'span'); + + if( force != null ){ + + if( force == "open"){ + treeOpen( topSpan, el ); + } + else if( force == "close" ){ + treeClose( topSpan, el ); + } + + } + + else if( childSet != null) { + // Is open, close it + if(!el.className.match(/(^| )closed($| )/)) { + treeClose( topSpan, el ); + // Is closed, open it + } else { + treeOpen( topSpan, el ); + } + } +} + + +function treeOpen( a, b ){ + removeClass(a,'spanClosed'); + removeClass(b,'closed'); +} + + +function treeClose( a, b ){ + addClass(a,'spanClosed'); + addClass(b,'closed'); +} + +/* + * Find the a child of el of type tag + */ +function findChildWithTag(el, tag) { + for(var i=0;i<el.childNodes.length;i++) { + if(el.childNodes[i].tagName != null && el.childNodes[i].tagName.toLowerCase() == tag) return el.childNodes[i]; + } + return null; +} + +/* + * Functions to add and remove class names + * Mac IE hates unnecessary spaces + */ +function addClass(el, cls, forceBefore) { + if(forceBefore != null && el.className.match(new RegExp('(^| )' + forceBefore))) { + el.className = el.className.replace(new RegExp("( |^)" + forceBefore), '$1' + cls + ' ' + forceBefore); + + } else if(!el.className.match(new RegExp('(^| )' + cls + '($| )'))) { + el.className += ' ' + cls; + el.className = el.className.replace(/(^ +)|( +$)/g, ''); + } +} +function removeClass(el, cls) { + var old = el.className; + var newCls = ' ' + el.className + ' '; + newCls = newCls.replace(new RegExp(' (' + cls + ' +)+','g'), ' '); + el.className = newCls.replace(/(^ +)|( +$)/g, ''); +} + +/* + * Handlers for automated loading + */ + _LOADERS = Array(); + +function callAllLoaders() { + var i, loaderFunc; + for(i=0;i<_LOADERS.length;i++) { + loaderFunc = _LOADERS[i]; + if(loaderFunc != callAllLoaders) loaderFunc(); + } +} + +function appendLoader(loaderFunc) { + if(window.onload && window.onload != callAllLoaders) + _LOADERS[_LOADERS.length] = window.onload; + + window.onload = callAllLoaders; + + _LOADERS[_LOADERS.length] = loaderFunc; +} + +appendLoader(autoInit_trees); diff --git a/src/usr/local/www/uploadconfig.php b/src/usr/local/www/uploadconfig.php new file mode 100644 index 0000000..35e7c55 --- /dev/null +++ b/src/usr/local/www/uploadconfig.php @@ -0,0 +1,65 @@ +#!/usr/local/bin/php +<?php +/* + uploadconfig.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-hidden-uploadconfiguration +##|*NAME=Hidden: Upload Configuration page +##|*DESCR=Allow access to the 'Hidden: Upload Configuration' page. +##|*MATCH=uploadconfig.php* +##|-PRIV + + +require("guiconfig.inc"); + +header("Content-Type: text/plain"); + +/* get config.xml in POST variable "config" */ +if ($_POST['config']) { + $fd = @fopen("{$g['tmp_path']}/config.xml", "w"); + if (!$fd) { + echo gettext("ERR Could not save configuration.")."\n"; + exit(0); + } + fwrite($fd, $_POST['config']); + fclose($fd); + if (config_install("{$g['tmp_path']}/config.xml") == 0) { + echo gettext("OK")."\n"; + system_reboot(); + } else { + echo gettext("ERR Could not install configuration.")."\n"; + } +} else { + echo gettext("ERR Invalid configuration received.")."\n"; +} + +exit(0); +?> diff --git a/src/usr/local/www/vpn_ipsec.php b/src/usr/local/www/vpn_ipsec.php new file mode 100644 index 0000000..cfe6bf9 --- /dev/null +++ b/src/usr/local/www/vpn_ipsec.php @@ -0,0 +1,558 @@ +<?php +/* + vpn_ipsec.php + part of m0n0wall (http://m0n0.ch/wall) + part of pfSense + + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-ipsec +##|*NAME=VPN: IPsec page +##|*DESCR=Allow access to the 'VPN: IPsec' page. +##|*MATCH=vpn_ipsec.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("ipsec.inc"); +require_once("vpn.inc"); + +if (!is_array($config['ipsec']['phase1'])) { + $config['ipsec']['phase1'] = array(); +} + +if (!is_array($config['ipsec']['phase2'])) { + $config['ipsec']['phase2'] = array(); +} + +$a_phase1 = &$config['ipsec']['phase1']; +$a_phase2 = &$config['ipsec']['phase2']; + +$pconfig['enable'] = isset($config['ipsec']['enable']); + +if ($_POST) { + if ($_POST['apply']) { + $retval = 0; + $retval = vpn_ipsec_configure(); + /* reload the filter in the background */ + filter_configure(); + $savemsg = get_std_save_message($retval); + if ($retval >= 0) { + if (is_subsystem_dirty('ipsec')) { + clear_subsystem_dirty('ipsec'); + } + } + } else if ($_POST['save']) { + $pconfig = $_POST; + + $config['ipsec']['enable'] = $_POST['enable'] ? true : false; + + write_config(); + + $retval = vpn_ipsec_configure(); + } else if (isset($_POST['del'])) { + /* delete selected p1 entries */ + if (is_array($_POST['p1entry']) && count($_POST['p1entry'])) { + foreach ($_POST['p1entry'] as $p1entrydel) { + unset($a_phase1[$p1entrydel]); + } + if (write_config()) { + mark_subsystem_dirty('ipsec'); + } + } + } else if (isset($_POST['delp2'])) { + /* delete selected p2 entries */ + if (is_array($_POST['p2entry']) && count($_POST['p2entry'])) { + foreach ($_POST['p2entry'] as $p2entrydel) { + unset($a_phase2[$p2entrydel]); + } + if (write_config()) { + mark_subsystem_dirty('ipsec'); + } + } + } else { + /* yuck - IE won't send value attributes for image buttons, while Mozilla does - so we use .x/.y to find move button clicks instead... */ + + // TODO: this. is. nasty. + unset($delbtn, $delbtnp2, $movebtn, $movebtnp2, $togglebtn, $togglebtnp2); + foreach ($_POST as $pn => $pd) { + if (preg_match("/del_(\d+)/", $pn, $matches)) { + $delbtn = $matches[1]; + } else if (preg_match("/delp2_(\d+)/", $pn, $matches)) { + $delbtnp2 = $matches[1]; + } else if (preg_match("/move_(\d+)/", $pn, $matches)) { + $movebtn = $matches[1]; + } else if (preg_match("/movep2_(\d+)/", $pn, $matches)) { + $movebtnp2 = $matches[1]; + } else if (preg_match("/toggle_(\d+)/", $pn, $matches)) { + $togglebtn = $matches[1]; + } else if (preg_match("/togglep2_(\d+)/", $pn, $matches)) { + $togglebtnp2 = $matches[1]; + } + } + + $save = 1; + + /* move selected p1 entries before this */ + if (isset($movebtn) && is_array($_POST['p1entry']) && count($_POST['p1entry'])) { + $a_phase1_new = array(); + + /* copy all p1 entries < $movebtn and not selected */ + for ($i = 0; $i < $movebtn; $i++) { + if (!in_array($i, $_POST['p1entry'])) { + $a_phase1_new[] = $a_phase1[$i]; + } + } + + /* copy all selected p1 entries */ + for ($i = 0; $i < count($a_phase1); $i++) { + if ($i == $movebtn) { + continue; + } + if (in_array($i, $_POST['p1entry'])) { + $a_phase1_new[] = $a_phase1[$i]; + } + } + + /* copy $movebtn p1 entry */ + if ($movebtn < count($a_phase1)) { + $a_phase1_new[] = $a_phase1[$movebtn]; + } + + /* copy all p1 entries > $movebtn and not selected */ + for ($i = $movebtn+1; $i < count($a_phase1); $i++) { + if (!in_array($i, $_POST['p1entry'])) { + $a_phase1_new[] = $a_phase1[$i]; + } + } + if (count($a_phase1_new) > 0) { + $a_phase1 = $a_phase1_new; + } + + } else if (isset($movebtnp2) && is_array($_POST['p2entry']) && count($_POST['p2entry'])) { + /* move selected p2 entries before this */ + $a_phase2_new = array(); + + /* copy all p2 entries < $movebtnp2 and not selected */ + for ($i = 0; $i < $movebtnp2; $i++) { + if (!in_array($i, $_POST['p2entry'])) { + $a_phase2_new[] = $a_phase2[$i]; + } + } + + /* copy all selected p2 entries */ + for ($i = 0; $i < count($a_phase2); $i++) { + if ($i == $movebtnp2) { + continue; + } + if (in_array($i, $_POST['p2entry'])) { + $a_phase2_new[] = $a_phase2[$i]; + } + } + + /* copy $movebtnp2 p2 entry */ + if ($movebtnp2 < count($a_phase2)) { + $a_phase2_new[] = $a_phase2[$movebtnp2]; + } + + /* copy all p2 entries > $movebtnp2 and not selected */ + for ($i = $movebtnp2+1; $i < count($a_phase2); $i++) { + if (!in_array($i, $_POST['p2entry'])) { + $a_phase2_new[] = $a_phase2[$i]; + } + } + if (count($a_phase2_new) > 0) { + $a_phase2 = $a_phase2_new; + } + + } else if (isset($togglebtn)) { + if (isset($a_phase1[$togglebtn]['disabled'])) { + unset($a_phase1[$togglebtn]['disabled']); + } else { + $a_phase1[$togglebtn]['disabled'] = true; + } + } else if (isset($togglebtnp2)) { + if (isset($a_phase2[$togglebtnp2]['disabled'])) { + unset($a_phase2[$togglebtnp2]['disabled']); + } else { + $a_phase2[$togglebtnp2]['disabled'] = true; + } + } else if (isset($delbtn)) { + /* remove static route if interface is not WAN */ + if ($a_phase1[$delbtn]['interface'] <> "wan") { + mwexec("/sbin/route delete -host {$a_phase1[$delbtn]['remote-gateway']}"); + } + + /* remove all phase2 entries that match the ikeid */ + $ikeid = $a_phase1[$delbtn]['ikeid']; + foreach ($a_phase2 as $p2index => $ph2tmp) { + if ($ph2tmp['ikeid'] == $ikeid) { + unset($a_phase2[$p2index]); + } + } + unset($a_phase1[$delbtn]); + + } else if (isset($delbtnp2)) { + unset($a_phase2[$delbtnp2]); + + } else { + $save = 0; + } + + if ($save === 1) { + if (write_config()) { + mark_subsystem_dirty('ipsec'); + } + } + } +} + +$pgtitle = array(gettext("VPN"), gettext("IPsec")); +$shortcut_section = "ipsec"; + +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("Tunnels"), true, "vpn_ipsec.php"); +$tab_array[] = array(gettext("Mobile clients"), false, "vpn_ipsec_mobile.php"); +$tab_array[] = array(gettext("Pre-Shared Keys"), false, "vpn_ipsec_keys.php"); +$tab_array[] = array(gettext("Advanced Settings"), false, "vpn_ipsec_settings.php"); +display_top_tabs($tab_array); +?> + +<script type="text/javascript" src="/javascript/row_toggle.js"></script> + +<?php + if ($savemsg) { + print_info_box($savemsg, 'success'); + } + + if ($pconfig['enable'] && is_subsystem_dirty('ipsec')) { + print_info_box_np(gettext("The IPsec tunnel configuration has been changed") . ".<br />" . gettext("You must apply the changes in order for them to take effect.")); + } +?> + +<h2>Rules</h2> + +<form method="post"> +<div class="table-responsive"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th> </th> + <th> </th> + <th><?=gettext("IKE"); ?></th> + <th><?=gettext("Remote Gateway"); ?></th> + <th><?=gettext("Mode"); ?></th> + <th><?=gettext("P1 Protocol"); ?></th> + <th><?=gettext("P1 Transforms"); ?></th> + <th><?=gettext("P1 Description"); ?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php $i = 0; foreach ($a_phase1 as $ph1ent): ?> +<?php + $iconfn = "pass"; + + $entryStatus = (isset($ph1ent['disabled']) ? 'disabled' : 'enabled'); + + if ($entryStatus == 'disabled') { + $iconfn .= "_d"; + } +?> + <tr id="fr<?=$i?>" ondblclick="document.location='vpn_ipsec_phase1.php?p1index=<?=$i?>'" class="<?= $entryStatus ?>"> + <td> + <input type="checkbox" id="frc<?=$i?>" name="p1entry[]" value="<?=$i?>" onclick="fr_bgcolor('<?=$i?>')" /> + </td> + <td> + <button value="toggle_<?=$i?>" name="toggle_<?=$i?>" title="<?=gettext("click to toggle enabled/disabled status")?>" class="btn btn-xs btn-default" type="submit"><?= ($entryStatus == 'disabled' ? 'enable' : 'disable') ?></button> + </td> + <td onclick="fr_toggle(<?=$i?>)" id="frd<?=$i?>"> +<?php + if (empty($ph1ent['iketype']) || $ph1ent['iketype'] == "ikev1") + echo "V1"; + else + echo "V2"; +?> + </td> + <td onclick="fr_toggle(<?=$i?>)" id="frd<?=$i?>"> +<?php + if ($ph1ent['interface']) { + $iflabels = get_configured_interface_with_descr(); + + $carplist = get_configured_carp_interface_list(); + foreach ($carplist as $cif => $carpip) + $iflabels[$cif] = $carpip." (".get_vip_descr($carpip).")"; + + $aliaslist = get_configured_ip_aliases_list(); + foreach ($aliaslist as $aliasip => $aliasif) + $iflabels[$aliasip] = $aliasip." (".get_vip_descr($aliasip).")"; + + $grouplist = return_gateway_groups_array(); + foreach ($grouplist as $name => $group) { + if($group[0]['vip'] != "") + $vipif = $group[0]['vip']; + else + $vipif = $group[0]['int']; + $iflabels[$name] = "GW Group {$name}"; + } + $if = htmlspecialchars($iflabels[$ph1ent['interface']]); + } + else + $if = "WAN"; + + if (!isset($ph1ent['mobile'])) + echo $if."<br />".$ph1ent['remote-gateway']; + else + echo $if."<br /><strong>" . gettext("Mobile Client") . "</strong>"; +?> + </td> + <td onclick="fr_toggle(<?=$i?>)" id="frd<?=$i?>"> + <?=$spans?> + <?php + if (empty($ph1ent['iketype']) || $ph1ent['iketype'] == "ikev1") + echo "{$ph1ent['mode']}"; + ?> + <?=$spane?> + </td> + <td onclick="fr_toggle(<?=$i?>)" id="frd<?=$i?>"> + <?=$p1_ealgos[$ph1ent['encryption-algorithm']['name']]['name']?> +<?php + if ($ph1ent['encryption-algorithm']['keylen']) { + if ($ph1ent['encryption-algorithm']['keylen']=="auto") + echo " (" . gettext("auto") . ")"; + else + echo " ({$ph1ent['encryption-algorithm']['keylen']} " . gettext("bits") . ")"; + } +?> + </td> + <td> + <?=$p1_halgos[$ph1ent['hash-algorithm']]?> + </td> + <td> + <?=htmlspecialchars($ph1ent['descr'])?> + </td> + <td> + <?php // TODO: add mouseover behaviour which indicates insert position when moving ?> + <button class="btn btn-xs btn-default" type="submit" name="move_<?=$i?>" value="move_<?=$i?>"><?=gettext("move selected entries before this")?></button> + <a class="btn btn-xs btn-primary" href="vpn_ipsec_phase1.php?p1index=<?=$i?>" title="<?=gettext("edit phase1 entry"); ?>">edit</a> + <button class="btn btn-xs btn-danger" type="submit" name="del_<?=$i?>" value="del_<?=$i?>" title="<?=gettext('delete phase1 entry'); ?>">delete</button> + <?php if (!isset($ph1ent['mobile'])): ?> + <a class="btn btn-xs btn-success" href="vpn_ipsec_phase1.php?dup=<?=$i?>" title="<?=gettext("copy phase1 entry"); ?>">copy</a> + <?php endif; ?> + </td> + </tr> + <tr class="<?= $entryStatus ?>"> + <td colspan="2"></td> + <td colspan="7" class="contains-table"> +<?php + if (isset($_POST["tdph2-{$i}-visible"])) + $tdph2_visible = htmlspecialchars($_POST["tdph2-{$i}-visible"]); + else + $tdph2_visible = 0; +?> + <input type="hidden" name="tdph2-<?=$i?>-visible" id="tdph2-<?=$i?>-visible" value="<?=$tdph2_visible?>" /> + <div id="shph2but-<?=$i?>" <?=($tdph2_visible == '1' ? 'style="display:none"' : '')?>> +<?php + $phase2count=0; + + foreach ($a_phase2 as $ph2ent) { + if ($ph2ent['ikeid'] != $ph1ent['ikeid']) + continue; + $phase2count++; + } + $fr_prefix = "frp2{$i}"; + $fr_header = $fr_prefix . "header"; +?> + <input type="button" onclick="show_phase2('tdph2-<?=$i?>','shph2but-<?=$i?>')" value="+" /> - <?php printf(gettext("Show %s Phase-2 entries"), $phase2count); ?> + </div> + <div id="tdph2-<?=$i?>" <?=($tdph2_visible != '1' ? 'style="display:none"' : '')?>> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th> </th> + <th> </th> + <th><?=gettext("Mode"); ?></th> + <th><?=gettext("Local Subnet"); ?></th> + <th><?=gettext("Remote Subnet"); ?></th> + <th><?=gettext("P2 Protocol"); ?></th> + <th><?=gettext("P2 Transforms"); ?></th> + <th><?=gettext("P2 Auth Methods"); ?></th> + <th> </th> + </tr> + </thead> + <tbody> +<?php $j = 0; foreach ($a_phase2 as $ph2index => $ph2ent): ?> +<?php + if ($ph2ent['ikeid'] != $ph1ent['ikeid']) + continue; + + $fr_c = $fr_prefix . "c" . $j; + $fr_d = $fr_prefix . "d" . $j; + + $iconfn = "pass"; + $entryStatus = (isset($ph2ent['disabled']) || isset($ph1ent['disabled']) ? 'disabled' : 'enabled'); + + if ($entryStatus == 'disabled') + $iconfn .= "_d"; + +?> + <tr id="<?=$fr_prefix . $j?>" ondblclick="document.location='vpn_ipsec_phase2.php?p2index=<?=$ph2ent['uniqid']?>'" class="<?= $entryStatus ?>"> + <td> + <input type="checkbox" id="<?=$fr_c?>" name="p2entry[]" value="<?=$ph2index?>" onclick="fr_bgcolor('<?=$j?>', '<?=$fr_prefix?>')" /> + </td> + <td> + <button value="togglep2_<?=$ph2index?>" name="togglep2_<?=$ph2index?>" title="<?=gettext("click to toggle enabled/disabled status")?>" class="btn btn-xs btn-default" type="submit"><?= ($entryStatus == 'disabled'? 'enable' : 'disable') ?></button> + </td> + <td id="<?=$fr_d?>" onclick="fr_toggle('<?=$j?>', '<?=$fr_prefix?>')"> + <?=$ph2ent['mode']?> + </td> +<?php if(($ph2ent['mode'] == "tunnel") or ($ph2ent['mode'] == "tunnel6")): ?> + <td id="<?=$fr_d?>" onclick="fr_toggle('<?=$j?>', '<?=$fr_prefix?>')"> + <?=ipsec_idinfo_to_text($ph2ent['localid']); ?> + </td> + <td id="<?=$fr_d?>" onclick="fr_toggle('<?=$j?>', '<?=$fr_prefix?>')"> + <?=ipsec_idinfo_to_text($ph2ent['remoteid']); ?> + </td> +<?php else: ?> + <td colspan="2"></td> +<?php endif; ?> + <td id="<?=$fr_d?>" onclick="fr_toggle('<?=$j?>', '<?=$fr_prefix?>')"> + <?=$p2_protos[$ph2ent['protocol']]; ?> + </td> + <td id="<?=$fr_d?>" onclick="fr_toggle('<?=$j?>', '<?=$fr_prefix?>')"> +<?php + foreach ($ph2ent['encryption-algorithm-option'] as $k => $ph2ea) { + if ($k) + echo ", "; + echo $p2_ealgos[$ph2ea['name']]['name']; + if ($ph2ea['keylen']) { + if ($ph2ea['keylen']=="auto") + echo " (" . gettext("auto") . ")"; + else + echo " ({$ph2ea['keylen']} " . gettext("bits") . ")"; + } + } +?> + </td> + <td id="<?=$fr_d?>" onclick="fr_toggle('<?=$j?>', '<?=$fr_prefix?>')"> +<?php + if (!empty($ph2ent['hash-algorithm-option']) && is_array($ph2ent['hash-algorithm-option'])) { + foreach ($ph2ent['hash-algorithm-option'] as $k => $ph2ha) { + if ($k) + echo ", "; + echo $p2_halgos[$ph2ha]; + } + } +?> + </td> + <td> + <?php // TODO: add mouseover behaviour which indicates insert position when moving ?> + <button class="btn btn-xs btn-default" type="submit" name="movep2_<?=$j?>" value="movep2_<?=$j?>"><?=gettext("move selected entries before this")?></button> + <a class="btn btn-xs btn-primary" href="vpn_ipsec_phase2.php?p2index=<?=$ph2ent['uniqid']?>" title="<?=gettext("edit phase2 entry"); ?>">edit</a> + <button class="btn btn-xs btn-danger" type="submit" name="delp2_<?=$ph2index?>" value="delp2_<?=$ph2index?>" title="<?=gettext('delete phase2 entry'); ?>">delete</button> + <a class="btn btn-xs btn-success" href="vpn_ipsec_phase2.php?dup=<?=$ph2ent['uniqid']?>" title="<?=gettext("add a new Phase 2 based on this one"); ?>">copy</a> + </td> + </tr> +<?php $j++; endforeach; ?> + <tr> + <td colspan="8"></td> + <td> +<?php + if ($j == 0): +?> + <img src="/themes/<?= $g['theme']; ?>/images/icons/icon_left_d.gif" width="17" height="17" title="<?=gettext("move selected phase2 entries to end")?>" border="0" alt="move" /> +<?php + else: +?> + <input onmouseover="fr_insline(<?=$j?>, true, '<?=$fr_prefix?>')" onmouseout="fr_insline(<?=$j?>, false, '<?=$fr_prefix?>')" name="movep2_<?=$j?>" type="image" src="/themes/<?= $g['theme']; ?>/images/icons/icon_left.gif" style="width:17;height:17;border:0" title="<?=gettext("move selected phase2 entries to end")?>" /> +<?php + endif; +?> + <a href="vpn_ipsec_phase2.php?ikeid=<?=$ph1ent['ikeid']?><?php if (isset($ph1ent['mobile'])) echo "&mobile=true"?>"> + <img src="./themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" title="<?=gettext("add phase2 entry"); ?>" alt="add" /> + </a> +<?php + if ($j == 0): +?> + <img src="/themes/<?= $g['theme']; ?>/images/icons/icon_x_d.gif" width="17" height="17" title="<?=gettext("delete selected phase2 entries")?>" border="0" alt="delete" /> +<?php + else: +?> + <input name="delp2" type="image" src="/themes/<?= $g['theme']; ?>/images/icons/icon_x.gif" style="width:17;height:17" title="<?=gettext("delete selected phase2 entries")?>" onclick="return confirm('<?=gettext("Do you really want to delete the selected phase2 entries?")?>')" /> +<?php + endif; +?> + </td> + </tr> + </tbody> + </table> + </div> + </td> + </tr> +<?php + $i++; + endforeach; // $a_phase1 as $ph1ent +?> + </tbody> + </table> +</div> + +<nav class="action-buttons"> +<?php if ($i !== 0): ?> + <input type="submit" name="move_<?=$i?>" class="btn btn-default" value="<?=gettext("move selected phase1 entries to end")?>" /> +<?php endif; ?> + <a href="vpn_ipsec_phase1.php" class="btn btn-success"><?=gettext("add new phase1")?></a> +<?php if ($i !== 0): ?> + <input type="submit" name="del" class="btn btn-danger" value="<?=gettext("delete selected phase1 entries")?>" onclick="return confirm('<?=gettext("Do you really want to delete the selected phase1 entries?")?>')" /> +<?php endif; ?> +</nav> +</form> + +<div class="alert alert-info"> + <strong><?=gettext("Note:")?></strong><br /> + <?=gettext("You can check your IPsec status at"); ?> <a href="diag_ipsec.php"><?=gettext("Status:IPsec"); ?></a>.<br /> + <?=gettext("IPsec Debug Mode can be enabled at"); ?> <a href="vpn_ipsec_settings.php"><?=gettext("VPN:IPsec:Advanced Settings"); ?></a>.<br /> + <?=gettext("IPsec can be set to prefer older SAs at"); ?> <a href="vpn_ipsec_settings.php"><?=gettext("VPN:IPsec:Advanced Settings"); ?></a>. +</div> + +<?php include("foot.inc"); ?> +<script type="text/javascript"> +//<![CDATA[ +function show_phase2(id, buttonid) { + document.getElementById(buttonid).innerHTML=''; + document.getElementById(id).style.display = "block"; + var visible = id + '-visible'; + document.getElementById(visible).value = "1"; +} +//]]> +</script>
\ No newline at end of file diff --git a/src/usr/local/www/vpn_ipsec_keys.php b/src/usr/local/www/vpn_ipsec_keys.php new file mode 100644 index 0000000..d5ae312 --- /dev/null +++ b/src/usr/local/www/vpn_ipsec_keys.php @@ -0,0 +1,177 @@ +<?php +/* + vpn_ipsec_keys.php + part of m0n0wall (http://m0n0.ch/wall) + part of pfSense + + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-ipsec-listkeys +##|*NAME=VPN: IPsec: Pre-Shared Keys List +##|*DESCR=Allow access to the 'VPN: IPsec: Pre-Shared Keys List' page. +##|*MATCH=vpn_ipsec_keys.php* +##|-PRIV + +require("functions.inc"); +require("guiconfig.inc"); +require_once("ipsec.inc"); +require_once("vpn.inc"); +require_once("filter.inc"); + +if (!is_array($config['ipsec']['mobilekey'])) { + $config['ipsec']['mobilekey'] = array(); +} +ipsec_mobilekey_sort(); +$a_secret = &$config['ipsec']['mobilekey']; + +$userkeys = array(); +foreach ($config['system']['user'] as $id => $user) { + if (!empty($user['ipsecpsk'])) { + $userkeys[] = array('ident' => $user['name'], 'type' => 'PSK', 'pre-shared-key' => $user['ipsecpsk'], 'id' => $id);; + } +} + +if (isset($_POST['apply'])) { + $retval = vpn_ipsec_configure(); + /* reload the filter in the background */ + filter_configure(); + $savemsg = get_std_save_message($retval); + if (is_subsystem_dirty('ipsec')) { + clear_subsystem_dirty('ipsec'); + } +} + +if ($_GET['act'] == "del") { + if ($a_secret[$_GET['id']]) { + unset($a_secret[$_GET['id']]); + write_config(gettext("Deleted IPsec Pre-Shared Key")); + mark_subsystem_dirty('ipsec'); + header("Location: vpn_ipsec_keys.php"); + exit; + } +} + +$pgtitle = gettext("VPN: IPsec: Keys"); +$shortcut_section = "ipsec"; + +include("head.inc"); + +?> + +<?php +if ($savemsg) + print_info_box($savemsg); +if (is_subsystem_dirty('ipsec')) + print_info_box_np(gettext("The IPsec tunnel configuration has been changed") . ".<br />" . gettext("You must apply the changes in order for them to take effect.")); + +?> + +<?php + $tab_array = array(); + $tab_array[0] = array(gettext("Tunnels"), false, "vpn_ipsec.php"); + $tab_array[1] = array(gettext("Mobile clients"), false, "vpn_ipsec_mobile.php"); + $tab_array[2] = array(gettext("Pre-Shared Keys"), true, "vpn_ipsec_keys.php"); + $tab_array[3] = array(gettext("Advanced Settings"), false, "vpn_ipsec_settings.php"); + display_top_tabs($tab_array); +?> + +<div class="table-responsive"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th><?=gettext("Identifier"); ?></th> + <th><?=gettext("Type"); ?></th> + <th><?=gettext("Pre-Shared Key"); ?></th> + <th></th> + </tr> + </thead> + + <tbody> +<?php $i = 0; foreach ($userkeys as $secretent): ?> + <tr> + <td> + <?php + if ($secretent['ident'] == 'allusers') + echo gettext("ANY USER"); + else + echo htmlspecialchars($secretent['ident']); + ?> + </td> + <td> + <?php + if (empty($secretent['type'])) + echo 'PSK'; + else + echo htmlspecialchars($secretent['type']); + ?> + </td> + <td> + <?=htmlspecialchars($secretent['pre-shared-key'])?> + </td> + <td> + <a class="btn btn-primary btn-xs" href="system_usermanager.php?act=edit&userid=<?=$secretent['id']?>">edit user</a> + </td> + </tr> +<?php $i++; endforeach; ?> + +<?php $i = 0; foreach ($a_secret as $secretent): ?> + <tr> + <td> + <?=htmlspecialchars($secretent['ident'])?> + </td> + <td> + <?php + if (empty($secretent['type'])) + echo 'PSK'; + else + echo htmlspecialchars($secretent['type']); + ?> + </td> + <td> + <?=htmlspecialchars($secretent['pre-shared-key'])?> + </td> + <td> + <a class="btn btn-primary btn-xs" href="vpn_ipsec_keys_edit.php?id=<?=$i?>">edit key</a> + <a class="btn btn-danger btn-xs" href="vpn_ipsec_keys.php?act=del&id=<?=$i?>">delete key</a> + </td> + </tr> +<?php $i++; endforeach; ?> + </tbody> + </table> +</div> + +<nav class="action-buttons"> + <a class="btn btn-success" href="vpn_ipsec_keys_edit.php"><?=gettext("add key")?></a> +</nav> + +<div class="alert alert-info"> + <strong><?=gettext("Note"); ?>:</strong><br /> + <?=gettext("PSK for any user can be set by using an identifier of any/ANY")?> +</div> + +<?php include("foot.inc"); ?>
\ No newline at end of file diff --git a/src/usr/local/www/vpn_ipsec_keys_edit.php b/src/usr/local/www/vpn_ipsec_keys_edit.php new file mode 100644 index 0000000..d4037c4 --- /dev/null +++ b/src/usr/local/www/vpn_ipsec_keys_edit.php @@ -0,0 +1,187 @@ +<?php +/* + vpn_ipsec_keys_edit.php + part of m0n0wall (http://m0n0.ch/wall) + part of pfSense + + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-ipsec-editkeys +##|*NAME=VPN: IPsec: Edit Pre-Shared Keys +##|*DESCR=Allow access to the 'VPN: IPsec: Edit Pre-Shared Keys' page. +##|*MATCH=vpn_ipsec_keys_edit.php* +##|-PRIV + +require("functions.inc"); +require("guiconfig.inc"); +require_once("ipsec.inc"); +require_once("vpn.inc"); + +if (!is_array($config['ipsec']['mobilekey'])) { + $config['ipsec']['mobilekey'] = array(); +} +ipsec_mobilekey_sort(); +$a_secret = &$config['ipsec']['mobilekey']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_secret[$id]) { + $pconfig['ident'] = $a_secret[$id]['ident']; + $pconfig['type'] = $a_secret[$id]['type']; + $pconfig['psk'] = $a_secret[$id]['pre-shared-key']; +} + +if ($_POST) { + $userids = array(); + foreach ($config['system']['user'] as $uid => $user) { + $userids[$user['name']] = $uid; + } + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + $reqdfields = explode(" ", "ident psk"); + $reqdfieldsn = array(gettext("Identifier"), gettext("Pre-Shared Key")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (preg_match("/[^a-zA-Z0-9@\.\-]/", $_POST['ident'])) { + $input_errors[] = gettext("The identifier contains invalid characters."); + } + + if (array_key_exists($_POST['ident'], $userids)) { + $input_errors[] = gettext("A user with this name already exists. Add the key to the user instead."); + } + unset($userids); + + if (isset($_POST['psk']) && !preg_match('/^[[:ascii:]]*$/', $_POST['psk'])) { + $input_errors[] = gettext("Pre-Shared Key contains invalid characters."); + } + + if (!$input_errors && !(isset($id) && $a_secret[$id])) { + /* make sure there are no dupes */ + foreach ($a_secret as $secretent) { + if ($secretent['ident'] == $_POST['ident']) { + $input_errors[] = gettext("Another entry with the same identifier already exists."); + break; + } + } + } + + if (!$input_errors) { + + if (isset($id) && $a_secret[$id]) { + $secretent = $a_secret[$id]; + } + + $secretent['ident'] = $_POST['ident']; + $secretent['type'] = $_POST['type']; + $secretent['pre-shared-key'] = $_POST['psk']; + $text = ""; + + if (isset($id) && $a_secret[$id]) { + $a_secret[$id] = $secretent; + $text = gettext("Edited"); + } else { + $a_secret[] = $secretent; + $text = gettext("Added"); + } + + write_config("{$text} IPsec Pre-Shared Keys"); + mark_subsystem_dirty('ipsec'); + + header("Location: vpn_ipsec_keys.php"); + exit; + } +} + +$pgtitle = gettext("VPN: IPsec: Edit Pre-Shared Key"); +$shortcut_section = "ipsec"; + +include("head.inc"); + +?> + +<?php if ($input_errors) print_input_errors($input_errors); ?> + +<?php + +require('classes/Form.class.php'); +$form = new Form; + +$section = new Form_Section('Edit pre-shared-secret'); + +$section->addInput(new Form_Input( + 'ident', + 'Identifier', + 'text', + $pconfig['ident'] +))->setHelp('This can be either an IP address, fully qualified domain name or an e-mail address'); + +$section->addInput(new Form_Select( + 'type', + 'Secret type', + $pconfig['type'], + $ipsec_preshared_key_type +))->setWidth(2); + +$section->addInput(new Form_Input( + 'psk', + 'Pre-Shared Key', + 'text', + $pconfig['psk'] +)); + +if (isset($id) && $a_secret[$id]) +{ + $form->addGlobal(new Form_Input( + 'id', + false, + 'hidden', + $id + )); +} + +$form->add($section); + +print $form; + +?> + +<div class="alert alert-info"> + <strong><?=gettext("Note"); ?>:</strong><br /> + <?=gettext("PSK for any user can be set by using an identifier of any/ANY")?> +</div> + +<?php include("foot.inc"); ?>
\ No newline at end of file diff --git a/src/usr/local/www/vpn_ipsec_mobile.php b/src/usr/local/www/vpn_ipsec_mobile.php new file mode 100644 index 0000000..503b2b2 --- /dev/null +++ b/src/usr/local/www/vpn_ipsec_mobile.php @@ -0,0 +1,611 @@ +<?php +/* + vpn_ipsec_mobile.php + + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-ipsec-mobile +##|*NAME=VPN: IPsec: Mobile page +##|*DESCR=Allow access to the 'VPN: IPsec: Mobile' page. +##|*MATCH=vpn_ipsec_mobile.php* +##|-PRIV + +require("functions.inc"); +require("guiconfig.inc"); +require_once("ipsec.inc"); +require_once("vpn.inc"); +require_once("filter.inc"); + +if (!is_array($config['ipsec']['phase1'])) { + $config['ipsec']['phase1'] = array(); +} + +$a_phase1 = &$config['ipsec']['phase1']; + +if (!is_array($config['ipsec']['client'])) { + $config['ipsec']['client'] = array(); +} + +$a_client = &$config['ipsec']['client']; + +if (count($a_client)) { + + $pconfig['enable'] = $a_client['enable']; + + $pconfig['user_source'] = $a_client['user_source']; + $pconfig['group_source'] = $a_client['group_source']; + + $pconfig['pool_address'] = $a_client['pool_address']; + $pconfig['pool_netbits'] = $a_client['pool_netbits']; + $pconfig['net_list'] = $a_client['net_list']; + $pconfig['save_passwd'] = $a_client['save_passwd']; + $pconfig['dns_domain'] = $a_client['dns_domain']; + $pconfig['dns_split'] = $a_client['dns_split']; + $pconfig['dns_server1'] = $a_client['dns_server1']; + $pconfig['dns_server2'] = $a_client['dns_server2']; + $pconfig['dns_server3'] = $a_client['dns_server3']; + $pconfig['dns_server4'] = $a_client['dns_server4']; + $pconfig['wins_server1'] = $a_client['wins_server1']; + $pconfig['wins_server2'] = $a_client['wins_server2']; + $pconfig['pfs_group'] = $a_client['pfs_group']; + $pconfig['login_banner'] = $a_client['login_banner']; + + if (isset($pconfig['enable'])) { + $pconfig['enable'] = true; + } + + if ($pconfig['pool_address']&&$pconfig['pool_netbits']) { + $pconfig['pool_enable'] = true; + } else { + $pconfig['pool_netbits'] = 24; + } + + if (isset($pconfig['net_list'])) { + $pconfig['net_list_enable'] = true; + } + + if (isset($pconfig['save_passwd'])) { + $pconfig['save_passwd_enable'] = true; + } + + if ($pconfig['dns_domain']) { + $pconfig['dns_domain_enable'] = true; + } + + if ($pconfig['dns_split']) { + $pconfig['dns_split_enable'] = true; + } + + if ($pconfig['dns_server1']||$pconfig['dns_server2']||$pconfig['dns_server3']||$pconfig['dns_server4']) { + $pconfig['dns_server_enable'] = true; + } + + if ($pconfig['wins_server1']||$pconfig['wins_server2']) { + $pconfig['wins_server_enable'] = true; + } + + if (isset($pconfig['pfs_group'])) { + $pconfig['pfs_group_enable'] = true; + } + + if ($pconfig['login_banner']) { + $pconfig['login_banner_enable'] = true; + } +} + +if ($_POST['create']) { + header("Location: vpn_ipsec_phase1.php?mobile=true"); +} + +if ($_POST['apply']) { + $retval = 0; + /* NOTE: #4353 Always restart ipsec when mobile clients settings change */ + $retval = vpn_ipsec_configure(true); + $savemsg = get_std_save_message($retval); + if ($retval >= 0) { + if (is_subsystem_dirty('ipsec')) { + clear_subsystem_dirty('ipsec'); + } + } +} + +if ($_POST['save']) { + + unset($input_errors); + $pconfig = $_POST; + + /* input consolidation */ + + /* input validation */ + + $reqdfields = explode(" ", "user_source group_source"); + $reqdfieldsn = array(gettext("User Authentication Source"), gettext("Group Authentication Source")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if ($pconfig['pool_enable']) { + if (!is_ipaddr($pconfig['pool_address'])) { + $input_errors[] = gettext("A valid IP address for 'Virtual Address Pool Network' must be specified."); + } + } + if ($pconfig['dns_domain_enable']) { + if (!is_domain($pconfig['dns_domain'])) { + $input_errors[] = gettext("A valid value for 'DNS Default Domain' must be specified."); + } + } + if ($pconfig['dns_split_enable']) { + if (!empty($pconfig['dns_split'])) { + /* Replace multiple spaces by single */ + $pconfig['dns_split'] = preg_replace('/\s+/', ' ', trim($pconfig['dns_split'])); + $domain_array = explode(' ', $pconfig['dns_split']); + foreach ($domain_array as $curdomain) { + if (!is_domain($curdomain)) { + $input_errors[] = gettext("A valid split DNS domain list must be specified."); + break; + } + } + } + } + + if ($pconfig['dns_server_enable']) { + if (!$pconfig['dns_server1'] && !$pconfig['dns_server2'] && + !$pconfig['dns_server3'] && !$pconfig['dns_server4']) { + $input_errors[] = gettext("At least one DNS server must be specified to enable the DNS Server option."); + } + if ($pconfig['dns_server1'] && !is_ipaddr($pconfig['dns_server1'])) { + $input_errors[] = gettext("A valid IP address for 'DNS Server #1' must be specified."); + } + if ($pconfig['dns_server2'] && !is_ipaddr($pconfig['dns_server2'])) { + $input_errors[] = gettext("A valid IP address for 'DNS Server #2' must be specified."); + } + if ($pconfig['dns_server3'] && !is_ipaddr($pconfig['dns_server3'])) { + $input_errors[] = gettext("A valid IP address for 'DNS Server #3' must be specified."); + } + if ($pconfig['dns_server4'] && !is_ipaddr($pconfig['dns_server4'])) { + $input_errors[] = gettext("A valid IP address for 'DNS Server #4' must be specified."); + } + } + + if ($pconfig['wins_server_enable']) { + if (!$pconfig['wins_server1'] && !$pconfig['wins_server2']) { + $input_errors[] = gettext("At least one WINS server must be specified to enable the DNS Server option."); + } + if ($pconfig['wins_server1'] && !is_ipaddr($pconfig['wins_server1'])) { + $input_errors[] = gettext("A valid IP address for 'WINS Server #1' must be specified."); + } + if ($pconfig['wins_server2'] && !is_ipaddr($pconfig['wins_server2'])) { + $input_errors[] = gettext("A valid IP address for 'WINS Server #2' must be specified."); + } + } + + if ($pconfig['login_banner_enable']) { + if (!strlen($pconfig['login_banner'])) { + $input_errors[] = gettext("A valid value for 'Login Banner' must be specified."); + } + } + + if (!$input_errors) { + $client = array(); + + if ($pconfig['enable']) { + $client['enable'] = true; + } + + if (!empty($pconfig['user_source'])) { + $client['user_source'] = implode(",", $pconfig['user_source']); + } + $client['group_source'] = $pconfig['group_source']; + + if ($pconfig['pool_enable']) { + $client['pool_address'] = $pconfig['pool_address']; + $client['pool_netbits'] = $pconfig['pool_netbits']; + } + + if ($pconfig['net_list_enable']) { + $client['net_list'] = true; + } + + if ($pconfig['save_passwd_enable']) { + $client['save_passwd'] = true; + } + + if ($pconfig['dns_domain_enable']) { + $client['dns_domain'] = $pconfig['dns_domain']; + } + + if ($pconfig['dns_split_enable']) { + $client['dns_split'] = $pconfig['dns_split']; + } + + if ($pconfig['dns_server_enable']) { + $client['dns_server1'] = $pconfig['dns_server1']; + $client['dns_server2'] = $pconfig['dns_server2']; + $client['dns_server3'] = $pconfig['dns_server3']; + $client['dns_server4'] = $pconfig['dns_server4']; + } + + if ($pconfig['wins_server_enable']) { + $client['wins_server1'] = $pconfig['wins_server1']; + $client['wins_server2'] = $pconfig['wins_server2']; + } + + if ($pconfig['pfs_group_enable']) { + $client['pfs_group'] = $pconfig['pfs_group']; + } + + if ($pconfig['login_banner_enable']) { + $client['login_banner'] = $pconfig['login_banner']; + } + + $a_client = $client; + + write_config(); + mark_subsystem_dirty('ipsec'); + + header("Location: vpn_ipsec_mobile.php"); + exit; + } +} + +$pgtitle = array(gettext("VPN"), gettext("IPsec"), gettext("Mobile")); +$shortcut_section = "ipsec"; + +include("head.inc"); +?> + + <script type="text/javascript"> + //<![CDATA[ + + function pool_change() { + + if (document.iform.pool_enable.checked) { + document.iform.pool_address.disabled = 0; + document.iform.pool_netbits.disabled = 0; + } else { + document.iform.pool_address.disabled = 1; + document.iform.pool_netbits.disabled = 1; + } + } + + function dns_domain_change() { + + if (document.iform.dns_domain_enable.checked) + document.iform.dns_domain.disabled = 0; + else + document.iform.dns_domain.disabled = 1; + } + + function dns_split_change() { + + if (document.iform.dns_split_enable.checked) + document.iform.dns_split.disabled = 0; + else + document.iform.dns_split.disabled = 1; + } + + function dns_server_change() { + + if (document.iform.dns_server_enable.checked) { + document.iform.dns_server1.disabled = 0; + document.iform.dns_server2.disabled = 0; + document.iform.dns_server3.disabled = 0; + document.iform.dns_server4.disabled = 0; + } else { + document.iform.dns_server1.disabled = 1; + document.iform.dns_server2.disabled = 1; + document.iform.dns_server3.disabled = 1; + document.iform.dns_server4.disabled = 1; + } + } + + function wins_server_change() { + + if (document.iform.wins_server_enable.checked) { + document.iform.wins_server1.disabled = 0; + document.iform.wins_server2.disabled = 0; + } else { + document.iform.wins_server1.disabled = 1; + document.iform.wins_server2.disabled = 1; + } + } + + function pfs_group_change() { + + if (document.iform.pfs_group_enable.checked) + document.iform.pfs_group.disabled = 0; + else + document.iform.pfs_group.disabled = 1; + } + + function login_banner_change() { + + if (document.iform.login_banner_enable.checked) + document.iform.login_banner.disabled = 0; + else + document.iform.login_banner.disabled = 1; + } + + //]]> + </script> + +<?php +if ($savemsg) + print_info_box($savemsg); +if (isset($config['ipsec']['enable']) && is_subsystem_dirty('ipsec')) + print_info_box_np(gettext("The IPsec tunnel configuration has been changed") . ".<br />" . gettext("You must apply the changes in order for them to take effect.")); +foreach ($a_phase1 as $ph1ent) + if (isset($ph1ent['mobile'])) + $ph1found = true; +if ($pconfig['enable'] && !$ph1found) + print_info_box_np(gettext("Support for IPsec Mobile clients is enabled but a Phase1 definition was not found") . ".<br />" . gettext("Please click Create to define one."),gettext("create"),gettext("Create Phase1")); +if ($input_errors) + print_input_errors($input_errors); + +$tab_array = array(); +$tab_array[0] = array(gettext("Tunnels"), false, "vpn_ipsec.php"); +$tab_array[1] = array(gettext("Mobile clients"), true, "vpn_ipsec_mobile.php"); +$tab_array[2] = array(gettext("Pre-Shared Key"), false, "vpn_ipsec_keys.php"); +$tab_array[3] = array(gettext("Advanced Settings"), false, "vpn_ipsec_settings.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); +$form = new Form; + +$section = new Form_Section('Enable IPsec Mobile Client Support'); +$section->addInput(new Form_Checkbox( + 'enable', + 'IKE Extensions', + 'Enable IPsec Mobile Client Support', + $pconfig['enable'] +)); + +$form->add($section); + +$section = new Form_Section('Extended Authentication (Xauth)'); + +$authServers = array(); + +foreach (auth_get_authserver_list() as $authServer) + $authServers[$authServer['name']] = $authServer['name']; // Value == name + +$section->addInput(new Form_Select( + 'user_source', + 'User Authentication', + explode(",", $pconfig['user_source']), + $authServers, + true +))->setHelp('Source'); + +$section->addInput(new Form_Select( + 'group_source', + 'Group Authentication', + $pconfig['group_source'], + array( + 'none' => 'none', + 'system' => 'system', + ) +))->setHelp('Source'); + +$form->add($section); + +$section = new Form_Section('Client Configuration (mode-cfg)'); + +$section->addInput(new Form_Checkbox( + 'pool_enable', + 'Virtual Address Pool', + 'Provide a virtual IP address to clients', + $pconfig['pool_enable'] +))->toggles('.toggle-pool_enable'); + +// TODO: Refactor this manual setup +$group = new Form_Group(''); +$group->addClass('toggle-pool_enable collapse'); + +if (!empty($pconfig['pool_enable'])) + $group->addClass('in'); + +$group->add(new Form_Input( + 'pool_address', + 'Network', + 'text', + htmlspecialchars($pconfig['pool_address']) +))->setWidth(4)->setHelp('Network configuration for Virtual Address Pool'); + +$netBits = array(); + +for ($i = 32; $i >= 0; $i--) + $netBits[$i] = $i; + +$group->add(new Form_Select( + 'pool_netbits', + '', + $pconfig['pool_netbits'], + $netBits +))->setWidth(2); + +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'net_list_enable', + 'Network List', + 'Provide a list of accessible networks to clients', + $pconfig['net_list_enable'] +)); + +$section->addInput(new Form_Checkbox( + 'save_passwd_enable', + 'Save Xauth Password', + 'Allow clients to save Xauth passwords (Cisco VPN client only).', + $pconfig['save_passwd_enable'] +))->setHelp('NOTE: With iPhone clients, this does not work when deployed via the iPhone configuration utility, only by manual entry.'); + +$section->addInput(new Form_Checkbox( + 'dns_domain_enable', + 'DNS Default Domain', + 'Provide a default domain name to clients', + $pconfig['dns_domain_enable'] +))->toggles('.toggle-dns_domain'); + +$group = new Form_Group(''); +$group->addClass('toggle-dns_domain collapse'); + +if (!empty($pconfig['dns_domain_enable'])) + $group->addClass('in'); + +$group->add(new Form_Input( + 'dns_domain', + '', + 'text', + htmlspecialchars($pconfig['dns_domain']) +))->setHelp('Specify domain as DNS Default Domain'); + +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'dns_split_enable', + 'Split DNS', + 'Provide a list of split DNS domain names to clients. Enter a space separated list.', + $pconfig['dns_split_enable'] +))->toggles('.toggle-dns_split'); + +$group = new Form_Group(''); +$group->addClass('toggle-dns_split collapse'); + +if (!empty($pconfig['dns_split_enable'])) + $group->addClass('in'); + +$group->add(new Form_Input( + 'dns_split', + '', + 'text', + htmlspecialchars($pconfig['dns_split']) +))->setHelp('NOTE: If left blank, and a default domain is set, it will be used for this value.'); + +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'dns_server_enable', + 'DNS Servers', + 'Provide a DNS server list to clients', + $pconfig['dns_server_enable'] +))->toggles('.toggle-dns_server_enable'); + +for ($i = 1; $i <= 4; $i++) +{ + $group = new Form_Group('Server #' . $i); + $group->addClass('toggle-dns_server_enable collapse'); + + if (!empty($pconfig['dns_server_enable'])) + $group->addClass('in'); + + $group->add(new Form_Input( + 'dns_server' . $i, + 'Server #' . $i, + 'text', + htmlspecialchars($pconfig['dns_server' . $i]) + )); + + $section->add($group); +} + +$section->addInput(new Form_Checkbox( + 'wins_server_enable', + 'WINS Servers', + 'Provide a WINS server list to clients', + $pconfig['wins_server_enable'] +))->toggles('.toggle-wins_server_enable'); + +for ($i = 1; $i <= 2; $i++) +{ + $group = new Form_Group('Server #' . $i); + $group->addClass('toggle-wins_server_enable collapse'); + + if (!empty($pconfig['wins_server_enable'])) + $group->addClass('in'); + + $group->add(new Form_Input( + 'wins_server' . $i, + 'Server #' . $i, + 'text', + htmlspecialchars($pconfig['wins_server' . $i]), + array('size' => 20) + )); + + $section->add($group); +} + +$section->addInput(new Form_Checkbox( + 'pfs_group_enable', + 'Phase2 PFS Group', + 'Provide the Phase2 PFS group to clients ( overrides all mobile phase2 settings )', + $pconfig['pfs_group_enable'] +))->toggles('.toggle-pfs_group'); + +$group = new Form_Group('Group'); +$group->addClass('toggle-pfs_group collapse'); + +if (!empty($pconfig['pfs_group_enable'])) + $group->addClass('in'); + +$group->add(new Form_Select( + 'pfs_group', + 'Group', + $pconfig['pfs_group'], + $p2_pfskeygroups +))->setWidth(2); + +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'login_banner_enable', + 'Login Banner', + 'Provide a login banner to clients', + $pconfig['login_banner_enable'] +))->toggles('.toggle-login_banner'); + +$group = new Form_Group(''); +$group->addClass('toggle-login_banner collapse'); + +if (!empty($pconfig['login_banner_enable'])) + $group->addClass('in'); + +// TODO: should be a textarea +$group->add(new Form_Input( + 'login_banner', + '', + 'text', + htmlspecialchars($pconfig['login_banner']) +)); + +$section->add($group); + +$form->add($section); + +print $form; + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/vpn_ipsec_phase1.php b/src/usr/local/www/vpn_ipsec_phase1.php new file mode 100644 index 0000000..588b4dd --- /dev/null +++ b/src/usr/local/www/vpn_ipsec_phase1.php @@ -0,0 +1,1085 @@ +<?php +/* + vpn_ipsec_phase1.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2014 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-ipsec-editphase1 +##|*NAME=VPN: IPsec: Edit Phase 1 page +##|*DESCR=Allow access to the 'VPN: IPsec: Edit Phase 1' page. +##|*MATCH=vpn_ipsec_phase1.php* +##|-PRIV + +require("functions.inc"); +require("guiconfig.inc"); +require_once("ipsec.inc"); +require_once("vpn.inc"); +require_once("filter.inc"); + +if (!is_array($config['ipsec']['phase1'])) { + $config['ipsec']['phase1'] = array(); +} + +if (!is_array($config['ipsec']['phase2'])) { + $config['ipsec']['phase2'] = array(); +} + +$a_phase1 = &$config['ipsec']['phase1']; +$a_phase2 = &$config['ipsec']['phase2']; + +if (is_numericint($_GET['p1index'])) { + $p1index = $_GET['p1index']; +} +if (isset($_POST['p1index']) && is_numericint($_POST['p1index'])) { + $p1index = $_POST['p1index']; +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + $p1index = $_GET['dup']; +} + +if (isset($p1index) && $a_phase1[$p1index]) { + // don't copy the ikeid on dup + if (!isset($_GET['dup']) || !is_numericint($_GET['dup'])) { + $pconfig['ikeid'] = $a_phase1[$p1index]['ikeid']; + } + + $old_ph1ent = $a_phase1[$p1index]; + + $pconfig['disabled'] = isset($a_phase1[$p1index]['disabled']); + + if ($a_phase1[$p1index]['interface']) { + $pconfig['interface'] = $a_phase1[$p1index]['interface']; + } else { + $pconfig['interface'] = "wan"; + } + + list($pconfig['remotenet'], $pconfig['remotebits']) = explode("/", $a_phase1[$p1index]['remote-subnet']); + + if (isset($a_phase1[$p1index]['mobile'])) { + $pconfig['mobile'] = 'true'; + } else { + $pconfig['remotegw'] = $a_phase1[$p1index]['remote-gateway']; + } + + if (empty($a_phase1[$p1index]['iketype'])) { + $pconfig['iketype'] = "ikev1"; + } else { + $pconfig['iketype'] = $a_phase1[$p1index]['iketype']; + } + $pconfig['mode'] = $a_phase1[$p1index]['mode']; + $pconfig['protocol'] = $a_phase1[$p1index]['protocol']; + $pconfig['myid_type'] = $a_phase1[$p1index]['myid_type']; + $pconfig['myid_data'] = $a_phase1[$p1index]['myid_data']; + $pconfig['peerid_type'] = $a_phase1[$p1index]['peerid_type']; + $pconfig['peerid_data'] = $a_phase1[$p1index]['peerid_data']; + $pconfig['ealgo'] = $a_phase1[$p1index]['encryption-algorithm']; + $pconfig['halgo'] = $a_phase1[$p1index]['hash-algorithm']; + $pconfig['dhgroup'] = $a_phase1[$p1index]['dhgroup']; + $pconfig['lifetime'] = $a_phase1[$p1index]['lifetime']; + $pconfig['authentication_method'] = $a_phase1[$p1index]['authentication_method']; + + if (($pconfig['authentication_method'] == "pre_shared_key") || + ($pconfig['authentication_method'] == "xauth_psk_server")) { + $pconfig['pskey'] = $a_phase1[$p1index]['pre-shared-key']; + } else { + $pconfig['certref'] = $a_phase1[$p1index]['certref']; + $pconfig['caref'] = $a_phase1[$p1index]['caref']; + } + + $pconfig['descr'] = $a_phase1[$p1index]['descr']; + $pconfig['nat_traversal'] = $a_phase1[$p1index]['nat_traversal']; + $pconfig['mobike'] = $a_phase1[$p1index]['mobike']; + + if (isset($a_phase1[$p1index]['reauth_enable'])) { + $pconfig['reauth_enable'] = true; + } + if (isset($a_phase1[$p1index]['rekey_enable'])) { + $pconfig['rekey_enable'] = true; + } + if (isset($a_phase1[$p1index]['responderonly'])) { + $pconfig['responderonly'] = true; + } + + if ($a_phase1[$p1index]['dpd_delay'] && $a_phase1[$p1index]['dpd_maxfail']) { + $pconfig['dpd_enable'] = true; + $pconfig['dpd_delay'] = $a_phase1[$p1index]['dpd_delay']; + $pconfig['dpd_maxfail'] = $a_phase1[$p1index]['dpd_maxfail']; + } +} else { + /* defaults */ + $pconfig['interface'] = "wan"; + if ($config['interfaces']['lan']) { + $pconfig['localnet'] = "lan"; + } + $pconfig['mode'] = "main"; + $pconfig['protocol'] = "inet"; + $pconfig['myid_type'] = "myaddress"; + $pconfig['peerid_type'] = "peeraddress"; + $pconfig['authentication_method'] = "pre_shared_key"; + $pconfig['ealgo'] = array(name => "aes"); + $pconfig['halgo'] = "sha1"; + $pconfig['dhgroup'] = "2"; + $pconfig['lifetime'] = "28800"; + $pconfig['nat_traversal'] = 'on'; + $pconfig['mobike'] = 'off'; + $pconfig['dpd_enable'] = true; + $pconfig['iketype'] = "ikev1"; + + /* mobile client */ + if ($_GET['mobile']) { + $pconfig['mobile'] = true; + $pconfig['mode'] = "aggressive"; + } +} + +if (isset($_GET['dup']) && is_numericint($_GET['dup'])) { + unset($p1index); +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + + $method = $pconfig['authentication_method']; + // Unset ca and cert if not required to avoid storing in config + if ($method == "pre_shared_key" || $method == "xauth_psk_server") { + unset($pconfig['caref']); + unset($pconfig['certref']); + } + + // Only require PSK here for normal PSK tunnels (not mobile) or xauth. + // For RSA methods, require the CA/Cert. + switch ($method) { + case 'eap-mschapv2': + if ($pconfig['iketype'] != 'ikev2') { + $input_errors[] = gettext("EAP-MSChapv2 can only be used with IKEv2 type VPNs."); + } + break; + case "eap-tls": + if ($pconfig['iketype'] != 'ikev2') { + $input_errors[] = gettext("EAP-TLS can only be used with IKEv2 type VPNs."); + } + break; + case "eap-radius": + if ($pconfig['iketype'] != 'ikev2') { + $input_errors[] = gettext("EAP-RADIUS can only be used with IKEv2 type VPNs."); + } + break; + case "pre_shared_key": + // If this is a mobile PSK tunnel the user PSKs go on + // the PSK tab, not here, so skip the check. + if ($pconfig['mobile']) { + break; + } + case "xauth_psk_server": + $reqdfields = explode(" ", "pskey"); + $reqdfieldsn = array(gettext("Pre-Shared Key")); + $validate_pskey = true; + break; + case "hybrid_rsa_server": + case "xauth_rsa_server": + case "rsasig": + $reqdfields = explode(" ", "caref certref"); + $reqdfieldsn = array(gettext("Certificate Authority"), gettext("Certificate")); + break; + } + if (!$pconfig['mobile']) { + $reqdfields[] = "remotegw"; + $reqdfieldsn[] = gettext("Remote gateway"); + } + + do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors); + + if (isset($validate_pskey) && isset($pconfig['pskey']) && !preg_match('/^[[:ascii:]]*$/', $pconfig['pskey'])) { + unset($validate_pskey); + $input_errors[] = gettext("Pre-Shared Key contains invalid characters."); + } + + if (($pconfig['lifetime'] && !is_numeric($pconfig['lifetime']))) { + $input_errors[] = gettext("The P1 lifetime must be an integer."); + } + + if ($pconfig['remotegw']) { + if (!is_ipaddr($pconfig['remotegw']) && !is_domain($pconfig['remotegw'])) { + $input_errors[] = gettext("A valid remote gateway address or host name must be specified."); + } elseif (is_ipaddrv4($pconfig['remotegw']) && ($pconfig['protocol'] != "inet")) { + $input_errors[] = gettext("A valid remote gateway IPv4 address must be specified or you need to change protocol to IPv6"); + } elseif (is_ipaddrv6($pconfig['remotegw']) && ($pconfig['protocol'] != "inet6")) { + $input_errors[] = gettext("A valid remote gateway IPv6 address must be specified or you need to change protocol to IPv4"); + } + } + + if ($pconfig['remotegw'] && is_ipaddr($pconfig['remotegw']) && !isset($pconfig['disabled'])) { + $t = 0; + foreach ($a_phase1 as $ph1tmp) { + if ($p1index != $t) { + $tremotegw = $pconfig['remotegw']; + if (($ph1tmp['remote-gateway'] == $tremotegw) && !isset($ph1tmp['disabled'])) { + $input_errors[] = sprintf(gettext('The remote gateway "%1$s" is already used by phase1 "%2$s".'), $tremotegw, $ph1tmp['descr']); + } + } + $t++; + } + } + + if (is_array($a_phase2) && (count($a_phase2))) { + foreach ($a_phase2 as $phase2) { + if ($phase2['ikeid'] == $pconfig['ikeid']) { + if (($pconfig['protocol'] == "inet") && ($phase2['mode'] == "tunnel6")) { + $input_errors[] = gettext("There is a Phase 2 using IPv6, you cannot use IPv4."); + break; + } + if (($pconfig['protocol'] == "inet6") && ($phase2['mode'] == "tunnel")) { + $input_errors[] = gettext("There is a Phase 2 using IPv4, you cannot use IPv6."); + break; + } + } + } + } + + /* My identity */ + + if ($pconfig['myid_type'] == "myaddress") { + $pconfig['myid_data'] = ""; + } + + if ($pconfig['myid_type'] == "address" and $pconfig['myid_data'] == "") { + $input_errors[] = gettext("Please enter an address for 'My Identifier'"); + } + + if ($pconfig['myid_type'] == "keyid tag" and $pconfig['myid_data'] == "") { + $input_errors[] = gettext("Please enter a keyid tag for 'My Identifier'"); + } + + if ($pconfig['myid_type'] == "fqdn" and $pconfig['myid_data'] == "") { + $input_errors[] = gettext("Please enter a fully qualified domain name for 'My Identifier'"); + } + + if ($pconfig['myid_type'] == "user_fqdn" and $pconfig['myid_data'] == "") { + $input_errors[] = gettext("Please enter a user and fully qualified domain name for 'My Identifier'"); + } + + if ($pconfig['myid_type'] == "dyn_dns" and $pconfig['myid_data'] == "") { + $input_errors[] = gettext("Please enter a dynamic domain name for 'My Identifier'"); + } + + if (($pconfig['myid_type'] == "address") && !is_ipaddr($pconfig['myid_data'])) { + $input_errors[] = gettext("A valid IP address for 'My identifier' must be specified."); + } + + if (($pconfig['myid_type'] == "fqdn") && !is_domain($pconfig['myid_data'])) { + $input_errors[] = gettext("A valid domain name for 'My identifier' must be specified."); + } + + if ($pconfig['myid_type'] == "fqdn") { + if (is_domain($pconfig['myid_data']) == false) { + $input_errors[] = gettext("A valid FQDN for 'My identifier' must be specified."); + } + } + + if ($pconfig['myid_type'] == "user_fqdn") { + $user_fqdn = explode("@", $pconfig['myid_data']); + if (is_domain($user_fqdn[1]) == false) { + $input_errors[] = gettext("A valid User FQDN in the form of user@my.domain.com for 'My identifier' must be specified."); + } + } + + if ($pconfig['myid_type'] == "dyn_dns") { + if (is_domain($pconfig['myid_data']) == false) { + $input_errors[] = gettext("A valid Dynamic DNS address for 'My identifier' must be specified."); + } + } + + /* Peer identity */ + + if ($pconfig['myid_type'] == "peeraddress") { + $pconfig['peerid_data'] = ""; + } + + // Only enforce peer ID if we are not dealing with a pure-psk mobile config. + if (!(($pconfig['authentication_method'] == "pre_shared_key") && ($pconfig['mobile']))) { + if ($pconfig['peerid_type'] == "address" and $pconfig['peerid_data'] == "") { + $input_errors[] = gettext("Please enter an address for 'Peer Identifier'"); + } + + if ($pconfig['peerid_type'] == "keyid tag" and $pconfig['peerid_data'] == "") { + $input_errors[] = gettext("Please enter a keyid tag for 'Peer Identifier'"); + } + + if ($pconfig['peerid_type'] == "fqdn" and $pconfig['peerid_data'] == "") { + $input_errors[] = gettext("Please enter a fully qualified domain name for 'Peer Identifier'"); + } + + if ($pconfig['peerid_type'] == "user_fqdn" and $pconfig['peerid_data'] == "") { + $input_errors[] = gettext("Please enter a user and fully qualified domain name for 'Peer Identifier'"); + } + + if ((($pconfig['peerid_type'] == "address") && !is_ipaddr($pconfig['peerid_data']))) { + $input_errors[] = gettext("A valid IP address for 'Peer identifier' must be specified."); + } + + if ((($pconfig['peerid_type'] == "fqdn") && !is_domain($pconfig['peerid_data']))) { + $input_errors[] = gettext("A valid domain name for 'Peer identifier' must be specified."); + } + + if ($pconfig['peerid_type'] == "fqdn") { + if (is_domain($pconfig['peerid_data']) == false) { + $input_errors[] = gettext("A valid FQDN for 'Peer identifier' must be specified."); + } + } + + if ($pconfig['peerid_type'] == "user_fqdn") { + $user_fqdn = explode("@", $pconfig['peerid_data']); + if (is_domain($user_fqdn[1]) == false) { + $input_errors[] = gettext("A valid User FQDN in the form of user@my.domain.com for 'Peer identifier' must be specified."); + } + } + } + + if ($pconfig['dpd_enable']) { + if (!is_numeric($pconfig['dpd_delay'])) { + $input_errors[] = gettext("A numeric value must be specified for DPD delay."); + } + + if (!is_numeric($pconfig['dpd_maxfail'])) { + $input_errors[] = gettext("A numeric value must be specified for DPD retries."); + } + } + + if (!empty($pconfig['iketype']) && $pconfig['iketype'] != "ikev1" && $pconfig['iketype'] != "ikev2") { + $input_errors[] = gettext("Valid arguments for IKE type is v1 or v2"); + } + + if (!empty($_POST['ealgo']) && isset($config['system']['crypto_hardware'])) { + if ($config['system']['crypto_hardware'] == "glxsb") { + if ($_POST['ealgo'] == "aes" && $_POST['ealgo_keylen'] != "128") { + $input_errors[] = gettext("Only 128 bit AES can be used where the glxsb crypto accelerator is enabled."); + } + } + } + + /* build our encryption algorithms array */ + $pconfig['ealgo'] = array(); + $pconfig['ealgo']['name'] = $_POST['ealgo']; + if ($pconfig['ealgo_keylen']) { + $pconfig['ealgo']['keylen'] = $_POST['ealgo_keylen']; + } + + if (!$input_errors) { + $ph1ent['ikeid'] = $pconfig['ikeid']; + $ph1ent['iketype'] = $pconfig['iketype']; + if ($pconfig['iketype'] != 'ikev1') { + unset($ph1ent['mode']); + } else { + $ph1ent['mode'] = $pconfig['mode']; + } + $ph1ent['disabled'] = $pconfig['disabled'] ? true : false; + $ph1ent['interface'] = $pconfig['interface']; + /* if the remote gateway changed and the interface is not WAN then remove route */ + /* the vpn_ipsec_configure() handles adding the route */ + if ($pconfig['interface'] <> "wan") { + if ($old_ph1ent['remote-gateway'] <> $pconfig['remotegw']) { + mwexec("/sbin/route delete -host {$old_ph1ent['remote-gateway']}"); + } + } + + if ($pconfig['mobile']) { + $ph1ent['mobile'] = true; + } else { + $ph1ent['remote-gateway'] = $pconfig['remotegw']; + } + + $ph1ent['protocol'] = $pconfig['protocol']; + + $ph1ent['myid_type'] = $pconfig['myid_type']; + $ph1ent['myid_data'] = $pconfig['myid_data']; + $ph1ent['peerid_type'] = $pconfig['peerid_type']; + $ph1ent['peerid_data'] = $pconfig['peerid_data']; + + $ph1ent['encryption-algorithm'] = $pconfig['ealgo']; + $ph1ent['hash-algorithm'] = $pconfig['halgo']; + $ph1ent['dhgroup'] = $pconfig['dhgroup']; + $ph1ent['lifetime'] = $pconfig['lifetime']; + $ph1ent['pre-shared-key'] = $pconfig['pskey']; + $ph1ent['private-key'] = base64_encode($pconfig['privatekey']); + $ph1ent['certref'] = $pconfig['certref']; + $ph1ent['caref'] = $pconfig['caref']; + $ph1ent['authentication_method'] = $pconfig['authentication_method']; + $ph1ent['descr'] = $pconfig['descr']; + $ph1ent['nat_traversal'] = $pconfig['nat_traversal']; + $ph1ent['mobike'] = $pconfig['mobike']; + + if (isset($pconfig['reauth_enable'])) { + $ph1ent['reauth_enable'] = true; + } else { + unset($ph1ent['reauth_enable']); + } + if (isset($pconfig['rekey_enable'])) { + $ph1ent['rekey_enable'] = true; + } else { + unset($ph1ent['rekey_enable']); + } + + if (isset($pconfig['responderonly'])) { + $ph1ent['responderonly'] = true; + } else { + unset($ph1ent['responderonly']); + } + + if (isset($pconfig['dpd_enable'])) { + $ph1ent['dpd_delay'] = $pconfig['dpd_delay']; + $ph1ent['dpd_maxfail'] = $pconfig['dpd_maxfail']; + } + + /* generate unique phase1 ikeid */ + if ($ph1ent['ikeid'] == 0) { + $ph1ent['ikeid'] = ipsec_ikeid_next(); + } + + if (isset($p1index) && $a_phase1[$p1index]) { + $a_phase1[$p1index] = $ph1ent; + } else { + $a_phase1[] = $ph1ent; + } + + write_config(); + mark_subsystem_dirty('ipsec'); + + header("Location: vpn_ipsec.php"); + exit; + } +} + +function build_interface_list() { + $interfaces = get_configured_interface_with_descr(); + + $carplist = get_configured_carp_interface_list(); + + foreach ($carplist as $cif => $carpip) + $interfaces[$cif] = $carpip." (".get_vip_descr($carpip).")"; + + $aliaslist = get_configured_ip_aliases_list(); + + foreach ($aliaslist as $aliasip => $aliasif) + $interfaces[$aliasip] = $aliasip." (".get_vip_descr($aliasip).")"; + + $grouplist = return_gateway_groups_array(); + + foreach ($grouplist as $name => $group) { + if($group[0]['vip'] != "") + $vipif = $group[0]['vip']; + else + $vipif = $group[0]['int']; + + $interfaces[$name] = "GW Group {$name}"; + } + + return($interfaces); + +} + +function build_auth_method_list() { + global $p1_authentication_methods; + + $list = array(); + + foreach ($p1_authentication_methods as $method_type => $method_params){ + if (!$pconfig['mobile'] && $method_params['mobile']) + continue; + + $list[$method_type] = htmlspecialchars($method_params['name']); + } + + return($list); +} + +function build_myid_list() { + global $my_identifier_list; + + $list = array(); + + foreach ($my_identifier_list as $id_type => $id_params) + $list[$id_type] = htmlspecialchars($id_params['desc']); + + return($list); +} + +function build_peerid_list() { + global $peer_identifier_list; + + $list = array(); + + foreach ($peer_identifier_list as $id_type => $id_params) + $list[$id_type] = htmlspecialchars($id_params['desc']); + + return($list); +} + +function build_cert_list() { + global $config; + + $list = array(); + + if(is_array($config['cert'])) { + foreach ($config['cert'] as $cert) + $list[$cert['refid']] = $cert['descr']; + } + + return($list); +} + +function build_ca_list() { + global $config; + + $list = array(); + + if(is_array($config['ca'])) { + foreach ($config['ca'] as $ca) + $list[$ca['refid']] = $ca['descr']; + } + + return($list); +} + +function build_eal_list() { + global $p1_ealgos; + + $list = array(); + + if(is_array($p1_ealgos)) { + foreach ($p1_ealgos as $algo => $algodata) + $list[$algo] = htmlspecialchars($algodata['name']); + } + + return($list); +} + +if ($pconfig['mobile']) { + $pgtitle = array(gettext("VPN"), gettext("IPsec"), gettext("Edit Phase 1"), gettext("Mobile Client")); +} else { + $pgtitle = array(gettext("VPN"), gettext("IPsec"), gettext("Edit Phase 1")); +} + +$shortcut_section = "ipsec"; + +include("head.inc"); + +$tab_array = array(); +$tab_array[] = array(gettext("Tunnels"), true, "vpn_ipsec.php"); +$tab_array[] = array(gettext("Mobile clients"), false, "vpn_ipsec_mobile.php"); +$tab_array[] = array(gettext("Pre-Shared Keys"), false, "vpn_ipsec_keys.php"); +$tab_array[] = array(gettext("Advanced Settings"), false, "vpn_ipsec_settings.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('General Information'); + +$section->addInput(new Form_Checkbox( + 'disabled', + 'Disabled', + 'Set this option to disable this phase1 without removing it from the list. ', + $pconfig['disabled'] +)); + +$section->addInput(new Form_Select( + 'iketype', + 'Key Exchange version', + $pconfig['iketype'], + array("ikev1" => "V1", "ikev2" => "V2", "auto" => "Auto") +))->setHelp('Select the Internet Key Exchange protocol version to be used, IKEv1 or IKEv2.'); + +$section->addInput(new Form_Select( + 'protocol', + 'Internet Protocol', + $pconfig['protocol'], + array("inet" => "IPv4", "inet6" => "IPv6") +))->setHelp('Select the Internet Protocol family.'); + +$section->addInput(new Form_Select( + 'interface', + 'Interface', + $pconfig['interface'], + build_interface_list() +))->setHelp('Select the interface for the local endpoint of this phase1 entry.'); + +$section->addInput(new Form_Input( + 'remotegw', + 'Remote Gateway', + 'text', + $pconfig['remotegw'] +))->setHelp('Enter the public IP address or host name of the remote gateway'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$form->add($section); + +$section = new Form_Section('Phase 1 proposal (Authentication)'); + +$section->addInput(new Form_Select( + 'authentication_method', + 'Authentication Method', + $pconfig['authentication_method'], + build_auth_method_list() +))->setHelp('Must match the setting chosen on the remote side.'); + +$section->addInput(new Form_Select( + 'mode', + 'Negotiation mode', + $pconfig['mode'], + array("main" => "Main", "aggressive" => "Aggressive") +))->setHelp('Aggressive is more flexible, but less secure.'); + +$group = new Form_Group('My identifier'); + +$group->add(new Form_Select( + 'myid_type', + null, + $pconfig['myid_type'], + build_myid_list() +)); + +$group->add(new Form_Input( + 'myid_data', + null, + 'text', + $pconfig['myid_data'] +)); + +$section->add($group); + +$group = new Form_Group('Peer identifier'); +$group->addClass('peeridgroup'); + +$group->add(new Form_Select( + 'peerid_type', + null, + $pconfig['peerid_type'], + build_peerid_list() +)); + +$group->add(new Form_Input( + 'peerid_data', + null, + 'text', + $pconfig['peerid_data'] +)); + +if($pconfig['mobile']) + $group->setHelp('This is known as the "group" setting on some VPN client implementations'); + +$section->add($group); + +$section->addInput(new Form_Input( + 'pskey', + 'Pre-Shared Key', + 'text', + $pconfig['pskey'] +))->setHelp('Enter your Pre-Shared Key string.'); + +$section->addInput(new Form_Select( + 'certref', + 'My Certificate', + $pconfig['certref'], + build_cert_list() +))->setHelp('Select a certificate previously configured in the Certificate Manager.'); + +$section->addInput(new Form_Select( + 'caref', + 'My Certificate Authority', + $pconfig['caref'], + build_ca_list() +))->setHelp('Select a certificate authority previously configured in the Certificate Manager.'); + +$form->add($section); + +$section = new Form_Section('Phase 1 proposal (Algorithms)'); + +$group = new Form_Group('Encryption Algorithm'); + +$group->add(new Form_Select( + 'ealgo', + null, + $pconfig['ealgo']['name'], + build_eal_list() +)); + +$group->add(new Form_Select( + 'ealgo_keylen', + null, + $pconfig['ealgo_keylen'], + array() +)); + +$section->add($group); + +$section->addInput(new Form_Select( + 'halgo', + 'Hash Algorithm', + $pconfig['halgo'], + $p1_halgos +))->setHelp('Must match the setting chosen on the remote side.'); + +$section->addInput(new Form_Select( + 'dhgroup', + 'DH Group', + $pconfig['dhgroup'], + $p1_dhgroups +))->setHelp('Must match the setting chosen on the remote side.'); + +$section->addInput(new Form_Input( + 'lifetime', + 'Lifetime (Seconds)', + 'number', + $pconfig['lifetime'] +)); + +$form->add($section); + +$section = new Form_Section('Advanced Options'); + +$section->addInput(new Form_Checkbox( + 'rekey_enable', + 'Disable rekey', + 'Disables renegotiation when a connection is about to expire.', + $pconfig['rekey_enable'] +)); + +$section->addInput(new Form_Checkbox( + 'reauth_enable', + 'Disable Reauth', + 'Whether rekeying of an IKE_SA should also reauthenticate the peer. In IKEv1, reauthentication is always done.', + $pconfig['reauth_enable'] +)); + +$section->addInput(new Form_Checkbox( + 'responderonly', + 'Responder Only', + 'Enable this option to never initiate this connection from this side, only respond to incoming requests.', + $pconfig['responderonly'] +)); + +$section->addInput(new Form_Select( + 'nat_traversal', + 'NAT Traversal', + $pconfig['nat_traversal'], + array('on' => 'Auto', 'force' => 'Force') +))->setHelp('Set this option to enable the use of NAT-T (i.e. the encapsulation of ESP in UDP packets) if needed, ' . + 'which can help with clients that are behind restrictive firewalls.'); + +$section->addInput(new Form_Select( + 'mobike', + 'MOBIKE', + $pconfig['mobike'], + array('on' => 'Enable', 'off' => 'Disable') +))->setHelp('Set this option to control the use of MOBIKE'); + +$section->addInput(new Form_Checkbox( + 'dpd_enable', + 'Dead Peer Detection', + 'Enable DPD', + $pconfig['dpd_enable'] +)); + +$section->addInput(new Form_Input( + 'dpd_delay', + 'Delay', + 'number', + $pconfig['dpd_delay'] +))->setHelp('Delay between requesting peer acknowledgement.'); + +$section->addInput(new Form_Input( + 'dpd_maxfail', + 'Max failures', + 'number', + $pconfig['dpd_maxfail'] +))->setHelp('Number of consecutive failures allowed before disconnect. '); + +if (isset($p1index) && $a_phase1[$p1index]) { + $section->addInput(new Form_Input( + 'p1index', + null, + 'hidden', + $pconfig['$p1index'] + )); +} + +if ($pconfig['mobile']) { + $section->addInput(new Form_Input( + 'mobile', + null, + 'hidden', + 'true' + )); +} + +$section->addInput(new Form_Input( + 'ikeid', + null, + 'hidden', + $pconfig['ikeid'] +)); + +$form->add($section); + +print($form); + +/* determine if we should init the key length */ +$keyset = ''; +if (isset($pconfig['ealgo']['keylen'])) + if (is_numeric($pconfig['ealgo']['keylen'])) + $keyset = $pconfig['ealgo']['keylen']; +?> + + +<form action="vpn_ipsec_phase1.php" method="post" name="iform" id="iform"> + +<?php + if ($input_errors) + print_input_errors($input_errors); +?> + +<script type="text/javascript"> +//<![CDATA[ +events.push(function(){ + + function myidsel_change() { + hideGroupInput('myid_data', ($('#myid_type').val() == 'myaddress')); + } + + function iketype_change() { + + if ($('#iketype').val() == 'ikev2') { + hideInput('mode', true); + hideInput('mobike', false); + hideInput('nat_traversal', true); + hideCheckbox('reauth_enable', false); + } else { + hideInput('mode', false); + hideInput('mobike', true); + hideInput('nat_traversal', false); + hideCheckbox('reauth_enable', true); + } + } + + function peeridsel_change() { + hideGroupInput('peerid_data', ($('#peerid_type').val() == 'peeraddress') || ($('#peerid_type').val() == 'any')); + } + + function methodsel_change() { + + switch ($('#authentication_method').val()) { + case 'eap-mschapv2': + case 'eap-tls': + case 'hybrid_rsa_server': + case 'xauth_rsa_server': + case 'rsasig': + hideInput('pskey', true); + hideClass('peeridgroup', false); + hideInput('certref', false); + hideInput('caref', false); + disableInput('certref', false); + disableInput('caref', false); + break; + +<?php if ($pconfig['mobile']) { ?> + case 'pre_shared_key': + hideInput('pskey', true); + hideClass('peeridgroup', true); + hideInput('certref', true); + hideInput('caref', true); + disableInput('certref', true); + disableInput('caref', true); + break; +<?php } ?> + default: /* psk modes*/ + hideInput('pskey', false); + hideClass('peeridgroup', false); + hideInput('certref', true); + hideInput('caref', true); + disableInput('certref', true); + disableInput('caref', true); + break; + } + } + + /* PHP generates javascript case statements for variable length keys */ + function ealgosel_change(bits) { + + $("select[name='ealgo_keylen']").find('option').remove().end(); + + switch ($('#ealgo').find(":selected").index().toString()) { +<?php + $i = 0; + foreach ($p1_ealgos as $algo => $algodata) { + if (is_array($algodata['keysel'])) { +?> + case '<?=$i?>': + hideGroupInput('ealgo_keylen', false); +<?php + $key_hi = $algodata['keysel']['hi']; + $key_lo = $algodata['keysel']['lo']; + $key_step = $algodata['keysel']['step']; + + for ($keylen = $key_hi; $keylen >= $key_lo; $keylen -= $key_step) { +?> + $("select[name='ealgo_keylen']").append($('<option value="<?=$keylen?>"><?=$keylen?> bits</option>')); +<?php + } +?> + break; +<?php + } else { +?> + case '<?=$i?>': + hideGroupInput('ealgo_keylen', true); + break; +<?php + } + $i++; + } +?> + } + + if( bits ) + $('#ealgo_keylen').val(bits); + } + + function dpdchkbox_change() { + hide = ! $('#dpd_enable').prop('checked'); + + hideInput('dpd_delay', hide); + hideInput('dpd_maxfail', hide); + + if(! $('#dpd_delay').val()) + $('#dpd_delay').val('10') + + if(! $('#dpd_maxfail').val()) + $('#dpd_maxfail').val('5') + } + + // ---------- Library of show/hide functions ---------------------------------------------------------------------- + + // Hides the <div> in which the specified input element lives so that the input, + // its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified group input element lives so that the input, + // its label and help text are hidden + function hideGroupInput(id, hide) { + if(hide) + $('#' + id).parent('div').addClass('hidden'); + else + $('#' + id).parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified checkbox lives so that the checkbox, + // its label and help text are hidden + function hideCheckbox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // ---------- Monitor elements for change and call the appropriate display functions ------------------------------ + + // Enable DPD + $('#dpd_enable').click(function () { + dpdchkbox_change(); + }); + + // Peer identifier + $('#peerid_type').click(function () { + peeridsel_change(); + }); + + // My identifier + $('#myid_type').click(function () { + myidsel_change(); + }); + + // ike type + $('#iketype').click(function () { + iketype_change(); + }); + + // authentication method + $('#authentication_method').click(function () { + methodsel_change(); + }); + + // authentication method + $('#ealgo').click(function () { + ealgosel_change(<?=$keyset?>); + }); + + // On ititial page load + myidsel_change(); + peeridsel_change(); + iketype_change(); + methodsel_change(); + ealgosel_change(<?=$keyset?>); + dpdchkbox_change(); +}); +//]]> +</script> +<?php + +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/vpn_ipsec_phase2.php b/src/usr/local/www/vpn_ipsec_phase2.php new file mode 100644 index 0000000..4edecd4 --- /dev/null +++ b/src/usr/local/www/vpn_ipsec_phase2.php @@ -0,0 +1,997 @@ +<?php +/* + vpn_ipsec_phase2.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-ipsec-editphase2 +##|*NAME=VPN: IPsec: Edit Phase 2 page +##|*DESCR=Allow access to the 'VPN: IPsec: Edit Phase 2' page. +##|*MATCH=vpn_ipsec_phase2.php* +##|-PRIV + +require("functions.inc"); +require("guiconfig.inc"); +require_once("ipsec.inc"); +require_once("vpn.inc"); + +if (!is_array($config['ipsec']['client'])) { + $config['ipsec']['client'] = array(); +} + +$a_client = &$config['ipsec']['client']; + +if (!is_array($config['ipsec']['phase1'])) { + $config['ipsec']['phase1'] = array(); +} + +if (!is_array($config['ipsec']['phase2'])) { + $config['ipsec']['phase2'] = array(); +} + +$a_phase1 = &$config['ipsec']['phase1']; +$a_phase2 = &$config['ipsec']['phase2']; + +if (!empty($_GET['p2index'])) { + $uindex = $_GET['p2index']; +} +if (!empty($_POST['uniqid'])) { + $uindex = $_POST['uniqid']; +} + +if (!empty($_GET['dup'])) { + $uindex = $_GET['dup']; +} + +$ph2found = false; +if (isset($uindex)) { + foreach ($a_phase2 as $p2index => $ph2) { + if ($ph2['uniqid'] == $uindex) { + $ph2found = true; + break; + } + } +} + +if ($ph2found === true) { + $pconfig['ikeid'] = $ph2['ikeid']; + $pconfig['disabled'] = isset($ph2['disabled']); + $pconfig['mode'] = $ph2['mode']; + $pconfig['descr'] = $ph2['descr']; + $pconfig['uniqid'] = $ph2['uniqid']; + + if (!empty($ph2['natlocalid'])) { + idinfo_to_pconfig("natlocal", $ph2['natlocalid'], $pconfig); + } + idinfo_to_pconfig("local", $ph2['localid'], $pconfig); + idinfo_to_pconfig("remote", $ph2['remoteid'], $pconfig); + + $pconfig['proto'] = $ph2['protocol']; + ealgos_to_pconfig($ph2['encryption-algorithm-option'], $pconfig); + $pconfig['halgos'] = $ph2['hash-algorithm-option']; + $pconfig['pfsgroup'] = $ph2['pfsgroup']; + $pconfig['lifetime'] = $ph2['lifetime']; + $pconfig['pinghost'] = $ph2['pinghost']; + $pconfig['reqid'] = $ph2['reqid']; + + if (isset($ph2['mobile'])) { + $pconfig['mobile'] = true; + } +} else { + $pconfig['ikeid'] = $_GET['ikeid']; + + /* defaults */ + $pconfig['localid_type'] = "lan"; + $pconfig['remoteid_type'] = "network"; + $pconfig['proto'] = "esp"; + $pconfig['ealgos'] = explode(",", "aes"); + $pconfig['halgos'] = explode(",", "hmac_sha1"); + $pconfig['pfsgroup'] = "0"; + $pconfig['lifetime'] = "3600"; + $pconfig['uniqid'] = uniqid(); + + /* mobile client */ + if ($_GET['mobile']) { + $pconfig['mobile']=true; + } +} + +unset($ph2); +if (!empty($_GET['dup'])) { + unset($uindex); + unset($p2index); + $pconfig['uniqid'] = uniqid(); + $pconfig['reqid'] = ipsec_new_reqid(); +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + if (!isset($_POST['ikeid'])) { + $input_errors[] = gettext("A valid ikeid must be specified."); + } + + /* input validation */ + $reqdfields = explode(" ", "localid_type uniqid"); + $reqdfieldsn = array(gettext("Local network type"), gettext("Unique Identifier")); + if (!isset($pconfig['mobile'])) { + $reqdfields[] = "remoteid_type"; + $reqdfieldsn[] = gettext("Remote network type"); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($pconfig['mode'] == "tunnel") || ($pconfig['mode'] == "tunnel6")) { + switch ($pconfig['localid_type']) { + case "network": + if (($pconfig['localid_netbits'] != 0 && !$pconfig['localid_netbits']) || !is_numeric($pconfig['localid_netbits'])) { + $input_errors[] = gettext("A valid local network bit count must be specified."); + } + case "address": + if (!$pconfig['localid_address'] || !is_ipaddr($pconfig['localid_address'])) { + $input_errors[] = gettext("A valid local network IP address must be specified."); + } elseif (is_ipaddrv4($pconfig['localid_address']) && ($pconfig['mode'] != "tunnel")) { + $input_errors[] = gettext("A valid local network IPv4 address must be specified or you need to change Mode to IPv6"); + } elseif (is_ipaddrv6($pconfig['localid_address']) && ($pconfig['mode'] != "tunnel6")) { + $input_errors[] = gettext("A valid local network IPv6 address must be specified or you need to change Mode to IPv4"); + } + break; + } + /* Check if the localid_type is an interface, to confirm if it has a valid subnet. */ + if (is_array($config['interfaces'][$pconfig['localid_type']])) { + // Don't let an empty subnet into racoon.conf, it can cause parse errors. Ticket #2201. + $address = get_interface_ip($pconfig['localid_type']); + $netbits = get_interface_subnet($pconfig['localid_type']); + + if (empty($address) || empty($netbits)) { + $input_errors[] = gettext("Invalid Local Network.") . " " . convert_friendly_interface_to_friendly_descr($pconfig['localid_type']) . " " . gettext("has no subnet."); + } + } + + if (!empty($pconfig['natlocalid_address'])) { + switch ($pconfig['natlocalid_type']) { + case "network": + if (($pconfig['natlocalid_netbits'] != 0 && !$pconfig['natlocalid_netbits']) || !is_numeric($pconfig['natlocalid_netbits'])) { + $input_errors[] = gettext("A valid NAT local network bit count must be specified."); + } + if ($pconfig['localid_type'] == "address") { + $input_errors[] = gettext("You cannot configure a network type address for NAT while only an address type is selected for local source."); + } + case "address": + if (!empty($pconfig['natlocalid_address']) && !is_ipaddr($pconfig['natlocalid_address'])) { + $input_errors[] = gettext("A valid NAT local network IP address must be specified."); + } elseif (is_ipaddrv4($pconfig['natlocalid_address']) && ($pconfig['mode'] != "tunnel")) { + $input_errors[] = gettext("A valid NAT local network IPv4 address must be specified or you need to change Mode to IPv6"); + } elseif (is_ipaddrv6($pconfig['natlocalid_address']) && ($pconfig['mode'] != "tunnel6")) { + $input_errors[] = gettext("A valid NAT local network IPv6 address must be specified or you need to change Mode to IPv4"); + } + break; + } + + if (is_array($config['interfaces'][$pconfig['natlocalid_type']])) { + // Don't let an empty subnet into racoon.conf, it can cause parse errors. Ticket #2201. + $address = get_interface_ip($pconfig['natlocalid_type']); + $netbits = get_interface_subnet($pconfig['natlocalid_type']); + + if (empty($address) || empty($netbits)) { + $input_errors[] = gettext("Invalid Local Network.") . " " . convert_friendly_interface_to_friendly_descr($pconfig['natlocalid_type']) . " " . gettext("has no subnet."); + } + } + } + + switch ($pconfig['remoteid_type']) { + case "network": + if (($pconfig['remoteid_netbits'] != 0 && !$pconfig['remoteid_netbits']) || !is_numeric($pconfig['remoteid_netbits'])) { + $input_errors[] = gettext("A valid remote network bit count must be specified."); + } + case "address": + if (!$pconfig['remoteid_address'] || !is_ipaddr($pconfig['remoteid_address'])) { + $input_errors[] = gettext("A valid remote network IP address must be specified."); + } elseif (is_ipaddrv4($pconfig['remoteid_address']) && ($pconfig['mode'] != "tunnel")) { + $input_errors[] = gettext("A valid remote network IPv4 address must be specified or you need to change Mode to IPv6"); + } elseif (is_ipaddrv6($pconfig['remoteid_address']) && ($pconfig['mode'] != "tunnel6")) { + $input_errors[] = gettext("A valid remote network IPv6 address must be specified or you need to change Mode to IPv4"); + } + break; + } + } + /* Validate enabled phase2's are not duplicates */ + if (isset($pconfig['mobile'])) { + /* User is adding phase 2 for mobile phase1 */ + foreach ($a_phase2 as $key => $name) { + if (isset($name['mobile']) && $name['uniqid'] != $pconfig['uniqid']) { + /* check duplicate localids only for mobile clents */ + $localid_data = ipsec_idinfo_to_cidr($name['localid'], false, $name['mode']); + $entered = array(); + $entered['type'] = $pconfig['localid_type']; + + if (isset($pconfig['localid_address'])) + $entered['address'] = $pconfig['localid_address']; + + if (isset($pconfig['localid_netbits'])) + $entered['netbits'] = $pconfig['localid_netbits']; + + $entered_localid_data = ipsec_idinfo_to_cidr($entered, false, $pconfig['mode']); + if ($localid_data == $entered_localid_data) { + /* adding new p2 entry */ + $input_errors[] = gettext("Phase2 with this Local Network is already defined for mobile clients."); + break; + } + } + } + } else { + /* User is adding phase 2 for site-to-site phase1 */ + $input_error = 0; + foreach ($a_phase2 as $key => $name) { + if (!isset($name['mobile']) && $pconfig['ikeid'] == $name['ikeid'] && $pconfig['uniqid'] != $name['uniqid']) { + /* check duplicate subnets only for given phase1 */ + $localid_data = ipsec_idinfo_to_cidr($name['localid'], false, $name['mode']); + $remoteid_data = ipsec_idinfo_to_cidr($name['remoteid'], false, $name['mode']); + $entered_local = array(); + $entered_local['type'] = $pconfig['localid_type']; + if (isset($pconfig['localid_address'])) { + $entered_local['address'] = $pconfig['localid_address']; + } + if (isset($pconfig['localid_netbits'])) { + $entered_local['netbits'] = $pconfig['localid_netbits']; + } + $entered_localid_data = ipsec_idinfo_to_cidr($entered_local, false, $pconfig['mode']); + $entered_remote = array(); + $entered_remote['type'] = $pconfig['remoteid_type']; + if (isset($pconfig['remoteid_address'])) { + $entered_remote['address'] = $pconfig['remoteid_address']; + } + if (isset($pconfig['remoteid_netbits'])) { + $entered_remote['netbits'] = $pconfig['remoteid_netbits']; + } + $entered_remoteid_data = ipsec_idinfo_to_cidr($entered_remote, false, $pconfig['mode']); + if ($localid_data == $entered_localid_data && $remoteid_data == $entered_remoteid_data) { + /* adding new p2 entry */ + $input_errors[] = gettext("Phase2 with this Local/Remote networks combination is already defined for this Phase1."); + break; + } + } + } + foreach ($a_phase1 as $phase1) { + if ($phase1['ikeid'] == $pconfig['ikeid']) { + /* This is the P1 for this entry, validate its remote-gateway and local interface isn't within tunnel */ + $entered_local = array(); + $entered_local['type'] = $pconfig['localid_type']; + if (isset($pconfig['localid_address'])) { + $entered_local['address'] = $pconfig['localid_address']; + } + if (isset($pconfig['localid_netbits'])) { + $entered_local['netbits'] = $pconfig['localid_netbits']; + } + $entered_localid_data = ipsec_idinfo_to_cidr($entered_local, false, $pconfig['mode']); + list($entered_local_network, $entered_local_mask) = explode('/', $entered_localid_data); + $entered_remote = array(); + $entered_remote['type'] = $pconfig['remoteid_type']; + if (isset($pconfig['remoteid_address'])) { + $entered_remote['address'] = $pconfig['remoteid_address']; + } + if (isset($pconfig['remoteid_netbits'])) { + $entered_remote['netbits'] = $pconfig['remoteid_netbits']; + } + $entered_remoteid_data = ipsec_idinfo_to_cidr($entered_remote, false, $pconfig['mode']); + list($entered_remote_network, $entered_remote_mask) = explode('/', $entered_remoteid_data); + if ($phase1['protocol'] == "inet6") { + $if = get_failover_interface($phase1['interface'], "inet6"); + $interfaceip = get_interface_ipv6($if); + } else { + $if = get_failover_interface($phase1['interface']); + $interfaceip = get_interface_ip($if); + } + /* skip validation for hostnames, they're subject to change anyway */ + if (is_ipaddr($phase1['remote-gateway'])) { + if ($pconfig['mode'] == "tunnel") { + if (check_subnets_overlap($interfaceip, 32, $entered_local_network, $entered_local_mask) && check_subnets_overlap($phase1['remote-gateway'], 32, $entered_remote_network, $entered_remote_mask)) { + $input_errors[] = gettext("The local and remote networks of a phase 2 entry cannot overlap the outside of the tunnel (interface and remote gateway) configured in its phase 1."); + break; + } + } else if ($pconfig['mode'] == "tunnel6") { + if (check_subnetsv6_overlap($interfaceip, 128, $entered_local_network, $entered_local_mask) && check_subnets_overlap($phase1['remote-gateway'], 128, $entered_remote_network, $entered_remote_mask)) { + $input_errors[] = gettext("The local and remote networks of a phase 2 entry cannot overlap the outside of the tunnel (interface and remote gateway) configured in its phase 1."); + break; + } + } + } + } + } + } + + /* For ESP protocol, handle encryption algorithms */ + if ($pconfig['proto'] == "esp") { + $ealgos = pconfig_to_ealgos($pconfig); + + if (!count($ealgos)) { + $input_errors[] = gettext("At least one encryption algorithm must be selected."); + } else { + foreach ($ealgos as $ealgo) { + if (isset($config['system']['crypto_hardware'])) { + if ($config['system']['crypto_hardware'] == "glxsb") { + if ($ealgo['name'] == "aes" && $ealgo['keylen'] != "128") { + $input_errors[] = gettext("Only 128 bit AES can be used where the glxsb crypto accelerator is enabled."); + } + } + } + if (empty($pconfig['halgos'])) { + if (!strpos($ealgo['name'], "gcm")) { + $input_errors[] = gettext("At least one hashing algorithm needs to be selected."); + break; + } + } + } + } + } + if (($_POST['lifetime'] && !is_numeric($_POST['lifetime']))) { + $input_errors[] = gettext("The P2 lifetime must be an integer."); + } + + if (!$input_errors) { + + $ph2ent = array(); + $ph2ent['ikeid'] = $pconfig['ikeid']; + $ph2ent['uniqid'] = $pconfig['uniqid']; + $ph2ent['mode'] = $pconfig['mode']; + $ph2ent['disabled'] = $pconfig['disabled'] ? true : false; + if (!isset($pconfig['reqid'])) { + $ph2ent['reqid'] = ipsec_new_reqid(); + } else { + $ph2ent['reqid'] = $pconfig['reqid']; + } + + if (($ph2ent['mode'] == "tunnel") || ($ph2ent['mode'] == "tunnel6")) { + if (!empty($pconfig['natlocalid_address'])) { + $ph2ent['natlocalid'] = pconfig_to_idinfo("natlocal", $pconfig); + } + $ph2ent['localid'] = pconfig_to_idinfo("local", $pconfig); + $ph2ent['remoteid'] = pconfig_to_idinfo("remote", $pconfig); + } + + $ph2ent['protocol'] = $pconfig['proto']; + $ph2ent['encryption-algorithm-option'] = $ealgos; + if (!empty($pconfig['halgos'])) { + $ph2ent['hash-algorithm-option'] = $pconfig['halgos']; + } else { + unset($ph2ent['hash-algorithm-option']); + } + $ph2ent['pfsgroup'] = $pconfig['pfsgroup']; + $ph2ent['lifetime'] = $pconfig['lifetime']; + $ph2ent['pinghost'] = $pconfig['pinghost']; + $ph2ent['descr'] = $pconfig['descr']; + + if (isset($pconfig['mobile'])) { + $ph2ent['mobile'] = true; + } + + if ($ph2found === true && $a_phase2[$p2index]) { + $a_phase2[$p2index] = $ph2ent; + } else { + $a_phase2[] = $ph2ent; + } + + write_config(); + mark_subsystem_dirty('ipsec'); + + header("Location: vpn_ipsec.php"); + exit; + } +} + +if ($pconfig['mobile']) { + $pgtitle = array(gettext("VPN"), gettext("IPsec"), gettext("Edit Phase 2"), gettext("Mobile Client")); +} else { + $pgtitle = array(gettext("VPN"), gettext("IPsec"), gettext("Edit Phase 2")); +} +$shortcut_section = "ipsec"; + +include("head.inc"); + +function pconfig_to_ealgos(& $pconfig) { + global $p2_ealgos; + + $ealgos = array(); + if (is_array($pconfig['ealgos'])) { + foreach ($p2_ealgos as $algo_name => $algo_data) { + if (in_array($algo_name, $pconfig['ealgos'])) { + $ealg = array(); + $ealg['name'] = $algo_name; + if (is_array($algo_data['keysel'])) { + $ealg['keylen'] = $_POST["keylen_".$algo_name]; + } + $ealgos[] = $ealg; + } + } + } + + return $ealgos; +} + +function ealgos_to_pconfig(& $ealgos,& $pconfig) { + + $pconfig['ealgos'] = array(); + foreach ($ealgos as $algo_data) { + $pconfig['ealgos'][] = $algo_data['name']; + if (isset($algo_data['keylen'])) { + $pconfig["keylen_".$algo_data['name']] = $algo_data['keylen']; + } + } + + return $ealgos; +} + +function pconfig_to_idinfo($prefix,& $pconfig) { + + $type = $pconfig[$prefix."id_type"]; + $address = $pconfig[$prefix."id_address"]; + $netbits = $pconfig[$prefix."id_netbits"]; + + switch ($type) { + case "address": + return array('type' => $type, 'address' => $address); + case "network": + return array('type' => $type, 'address' => $address, 'netbits' => $netbits); + default: + return array('type' => $type); + } +} + +function idinfo_to_pconfig($prefix,& $idinfo,& $pconfig) { + + switch ($idinfo['type']) { + case "address": + $pconfig[$prefix."id_type"] = $idinfo['type']; + $pconfig[$prefix."id_address"] = $idinfo['address']; + break; + case "network": + $pconfig[$prefix."id_type"] = $idinfo['type']; + $pconfig[$prefix."id_address"] = $idinfo['address']; + $pconfig[$prefix."id_netbits"] = $idinfo['netbits']; + break; + default: + $pconfig[$prefix."id_type"] = $idinfo['type']; + break; + } +} + +if ($input_errors) + print_input_errors($input_errors); + +$tab_array = array(); +$tab_array[0] = array(gettext("Tunnels"), true, "vpn_ipsec.php"); +$tab_array[1] = array(gettext("Mobile clients"), false, "vpn_ipsec_mobile.php"); +$tab_array[2] = array(gettext("Pre-Shared Keys"), false, "vpn_ipsec_keys.php"); +$tab_array[3] = array(gettext("Advanced Settings"), false, "vpn_ipsec_settings.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('General Information'); + +$section->addInput(new Form_Checkbox( + 'disabled', + 'Disabled', + 'Disable this phase 2 entry without removing it from the list. ', + $pconfig['disabled'] +)); + +$section->addInput(new Form_Select( + 'mode', + 'Mode', + $pconfig['mode'], + $p2_modes +)); + +$group = new Form_Group('Local Network'); +$group->addClass('opt_localid'); + +$subnetarray = get_configured_interface_with_descr(); +foreach($subnetarray as $ifname => $ifdescr) + $subnetarray[$ifname] = $ifdescr . ' subnet'; + +$group->add(new Form_Select( + 'localid_type', + null, + $pconfig['localid_type'], + array_merge(array('address' => 'Address', 'network' => 'Network'), $subnetarray) +))->setHelp('Type'); + +$group->add(new Form_IpAddress( + 'localid_address', + null, + $pconfig['localid_address'] +))->setHelp('Address')->addMask(localid_netbits, $pconfig['localid_netbits']); + +$section->add($group); + +$group = new Form_Group('NAT/BINAT translation'); +$group->addClass('opt_natid'); + +$subnetarray = get_configured_interface_with_descr(); +foreach($subnetarray as $ifname => $ifdescr) + $subnetarray[$ifname] = $ifdescr . ' subnet'; + +// Tack none, address & network on the beginning +$subnetarray = array('none' => gettext('None'), 'address' => 'Address', 'network' => 'Network') + $subnetarray; + +$group->add(new Form_Select( + 'natlocalid_type', + null, + $pconfig['natlocalid_type'], + $subnetarray +))->setHelp('Type'); + +$group->add(new Form_IpAddress( + 'natlocalid_address', + null, + $pconfig['localid_address'] +))->setHelp('Address')->addMask(natlocalid_netbits, $pconfig['natlocalid_netbits']); + +$group->setHelp('If NAT/BINAT is required on this network specify the address to be translated'); +$section->add($group); + +$group = new Form_Group('Remote Network'); +$group->addClass('opt_remoteid'); + +$group->add(new Form_Select( + 'remoteid_type', + null, + $pconfig['remoteid_type'], + array('address' => 'Address', 'network' => 'Network') +))->setHelp('Type'); + +$group->add(new Form_IpAddress( + 'remoteid_address', + null, + $pconfig['remoteid_address'] +))->setHelp('Address')->addMask(remoteid_netbits, $pconfig['remoteid_netbits']); + +$section->add($group); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +))->setHelp('You may enter a description here for your reference (not parsed).'); + +$form->add($section); + +$section = new Form_Section('Phase 2 proposal (SA/Key Exchange)'); + +$section->addInput(new Form_Select( + 'proto', + 'Protocol', + $pconfig['proto'], + $p2_protos +))->setHelp('ESP is encryption, AH is authentication only.'); + +$i = 0; +$rows = count($p2_ealgos) - 1; + +foreach ($p2_ealgos as $algo => $algodata) { + $group = new Form_Group($i == 0 ? 'Encryption Algorithms':''); + $group->addClass('encalg'); + + $group->add(new Form_Checkbox( + 'ealgos[]', + null, + $algodata['name'], + (is_array($pconfig['ealgos']) && in_array($algo,$pconfig['ealgos'])), + $algo + ))->addClass('multi'); + + + + if(is_array($algodata['keysel'])) { + $list = array(); + $key_hi = $algodata['keysel']['hi']; + $key_lo = $algodata['keysel']['lo']; + $key_step = $algodata['keysel']['step']; + for ($keylen = $key_hi; $keylen >= $key_lo; $keylen -= $key_step) { + $list[$keylen] = $keylen . ' bits'; + } + + $group->add(new Form_Select( + 'keylen_' . $algo, + null, + $keylen == $pconfig["keylen_".$algo], + array_merge(array('auto' => 'Auto'), $list) + )); + } + + + if($i == $rows) + $group->setHelp('Use 3DES for best compatibility or if you have a hardware crypto accelerator card. Blowfish is usually the fastest in software encryption.'); + + $i++; + $section->add($group); +} + +$group = new Form_Group('Hash Algorithms'); + +foreach ($p2_halgos as $algo => $algoname) { + $group->add(new Form_Checkbox( + 'halgos[]', + null, + $algoname, + (in_array($algo, $pconfig['halgos'])), + $algo + ))->addClass('multi'); +} + +$section->add($group); + +$sm = (!isset($pconfig['mobile']) || !isset($a_client['pfs_group'])); + +$section->addInput(new Form_Select( + 'pfsgroup', + 'PFS key group', + $pconfig['psgroup'], + $sm ? $p2_pfskeygroups:array() +))->setHelp($sm ? '':'Set globally in mobile client options'); + +$section->addInput(new Form_Input( + 'lifetime', + 'Lifetime', + 'number', + $pconfig['lifetime'] +))->setHelp('Seconds'); + +$form->add($section); + +$section = new Form_Section('Advanced Configuration'); + +$section->addInput(new Form_IpAddress( + 'pinghost', + 'Automatically ping host', + $pconfig['pinghost'] +))->setHelp('IP Address'); + +// Hidden inputs +if ($pconfig['mobile']) { + $section->addInput(new Form_Input( + 'mobile', + null, + 'hidden', + 'true' + )); +} + +$section->addInput(new Form_Input( + 'ikeid', + null, + 'hidden', + $pconfig['ikeid'] +)); + +if (!empty($pconfig['reqid'])) { + $section->addInput(new Form_Input( + 'reqid', + null, + 'hidden', + $pconfig['reqid'] + )); +} + +$section->addInput(new Form_Input( + 'uniqid', + null, + 'hidden', + $pconfig['uniqid'] +)); + +$form->add($section); + +print($form); + +?> + +<script type="text/javascript"> +//<![CDATA[ +events.push(function(){ + + // ---------- On changing "Mode" ---------------------------------------------------------------------------------- + function change_mode() { + + value = $('#mode').val(); + + if ((value == 'tunnel') || (value == 'tunnel6')) { + hideClass('opt_localid', false); + hideClass('opt_natid', false); + +<?php if (!isset($pconfig['mobile'])): ?> + hideClass('opt_remoteid', false); + hideClass('opt_natid', false); +<?php endif; ?> + } else { + hideClass('opt_localid', true); + hideClass('opt_natid', true); +<?php if (!isset($pconfig['mobile'])): ?> + hideClass('opt_remoteid', true); +<?php endif; ?> + } + } + + // ---------- On changing "NAT/BINAT" ----------------------------------------------------------------------------- + function typesel_change_natlocal(bits) { + var value = $('#mode').val(); + + if (typeof(bits) === "undefined") { + if (value === "tunnel") { + bits = 24; + } + else if (value === "tunnel6") { + bits = 64; + } + } + + var address_is_blank = !/\S/.test($('#natlocalid_address').val()); + + switch ($("#natlocalid_type option:selected").index()) { + case 0: /* single */ + disableInput('natlocalid_address', false); + + if (address_is_blank) { + $('#natlocalid_netbits').val(0); + } + + disableInput('natlocalid_netbits', true); + break; + case 1: /* network */ + disableInput('natlocalid_address', false); + + if (address_is_blank) { + $('#natlocalid_netbits').val(bits); + } + + disableInput('natlocalid_netbits', false); + break; + case 3: /* none */ + disableInput('natlocalid_address', true); + disableInput('natlocalid_netbits', true); + break; + default: + $('#natlocalid_address').val(""); + disableInput('natlocalid_address', true); + + if (address_is_blank) { + $('#natlocalid_netbits').val(0); + } + + disableInput('natlocalid_netbits', true); + break; + } + } + + // ---------- On changing "Local Network" ------------------------------------------------------------------------- + function typesel_change_local(bits) { + var value = $('#mode').val(); + + if (typeof(bits) === "undefined") { + if (value === "tunnel") { + bits = 24; + } + else if (value === "tunnel6") { + bits = 64; + } + } + + var address_is_blank = !/\S/.test($('#localid_address').val()); + + switch ($("#localid_type option:selected").index()) { + case 0: /* single */ + disableInput('localid_address', false); + + if (address_is_blank) { + $('#localid_netbits').val(0); + } + + disableInput('localid_netbits', true); + break; + case 1: /* network */ + disableInput('localid_address', false); + + if (address_is_blank) { + $('#localid_netbits').val(bits); + } + + disableInput('localid_netbits', false); + break; + case 3: /* none */ + disableInput('localid_address', true); + disableInput('localid_netbits', true); + break; + default: + $('#localid_address').val(""); + disableInput('localid_address', true); + + if (address_is_blank) { + $('#localid_netbits').val(0); + } + + disableInput('localid_netbits', true); + break; + } + } + +<?php + + // ---------- On changing "Remote Network" ------------------------------------------------------------------------ + if (!isset($pconfig['mobile'])): ?> + + function typesel_change_remote(bits) { + + var value = $('#mode').val(); + + if (typeof(bits) === "undefined") { + if (value === "tunnel") { + bits = 24; + } + else if (value === "tunnel6") { + bits = 64; + } + } + + var address_is_blank = !/\S/.test($('#remoteid_address').val()); + + switch ($("#remoteid_type option:selected").index()) { + case 0: /* single */ + disableInput('remoteid_address', false); + + if (address_is_blank) { + $('#remoteid_netbits').val(0); + } + + disableInput('remoteid_netbits', true); + break; + case 1: /* network */ + disableInput('remoteid_address', false); + + if (address_is_blank) { + $('#remoteid_netbits').val(bits); + } + + disableInput('remoteid_netbits', false); + break; + case 3: /* none */ + disableInput('remoteid_address', true); + disableInput('remoteid_netbits', true); + break; + default: + $('#remoteid_address').val(""); + disableInput('remoteid_address', true); + + if (address_is_blank) { + $('#remoteid_netbits').val(0); + } + + disableInput('remoteid_netbits', true); + break; + } + } + + <?php endif; ?> + + function change_protocol() { + hideClass('encalg', ($('#proto').val() != 'esp')); + } + + // ---------- Library of show/hide functions ---------------------------------------------------------------------- + + // Hides the <div> in which the specified input element lives so that the input, + // its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified group input element lives so that the input, + // its label and help text are hidden + function hideGroupInput(id, hide) { + if(hide) + $('#' + id).parent('div').addClass('hidden'); + else + $('#' + id).parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified checkbox lives so that the checkbox, + // its label and help text are hidden + function hideCheckbox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // ---------- Monitor elements for change and call the appropriate display functions ------------------------------ + + // Protocol + $('#proto').click(function () { + change_protocol(); + }); + + // Localid + $('#localid_type').click(function () { + typesel_change_local(<?=htmlspecialchars($pconfig['localid_netbits'])?>); + }); + + // Remoteid + $('#remoteid_type').click(function () { + typesel_change_remote(<?=htmlspecialchars($pconfig['remoteid_netbits'])?>); + }); + + // NATLocalid + $('#natlocalid_type').click(function () { + typesel_change_natlocal(<?=htmlspecialchars($pconfig['natlocalid_netbits'])?>); + }); + + // Mode + $('#mode').click(function () { + change_mode(); + }); + + // ---------- Iniatial page load ---------------------------------------------------------------------------------- + change_mode(); + change_protocol(); + typesel_change_local(<?=htmlspecialchars($pconfig['localid_netbits'])?>); + typesel_change_natlocal(<?=htmlspecialchars($pconfig['natlocalid_netbits'])?>); +<?php + if (!isset($pconfig['mobile'])): +?> + typesel_change_remote(<?=htmlspecialchars($pconfig['remoteid_netbits'])?>); +<?php +endif; +?> +}); +//]]> +</script> +<?php +include("foot.inc"); diff --git a/src/usr/local/www/vpn_ipsec_settings.php b/src/usr/local/www/vpn_ipsec_settings.php new file mode 100644 index 0000000..c6fe0ad --- /dev/null +++ b/src/usr/local/www/vpn_ipsec_settings.php @@ -0,0 +1,387 @@ +<?php +/* + vpn_ipsec_settings.php + + Copyright (C) 2015 Electric Sheep Fencing, LLC + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-ipsec-settings +##|*NAME=VPN: IPsec: Settings page +##|*DESCR=Allow access to the 'VPN: IPsec: Settings' page. +##|*MATCH=vpn_ipsec_settings.php* +##|-PRIV + +require("functions.inc"); +require("guiconfig.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("ipsec.inc"); +require_once("vpn.inc"); + +foreach ($ipsec_loglevels as $lkey => $ldescr) { + if (!empty($config['ipsec']["ipsec_{$lkey}"])) { + $pconfig["ipsec_{$lkey}"] = $config['ipsec']["ipsec_{$lkey}"]; + } +} +$pconfig['unityplugin'] = isset($config['ipsec']['unityplugin']); +$pconfig['strictcrlpolicy'] = isset($config['ipsec']['strictcrlpolicy']); +$pconfig['makebeforebreak'] = isset($config['ipsec']['makebeforebreak']); +$pconfig['noshuntlaninterfaces'] = isset($config['ipsec']['noshuntlaninterfaces']); +$pconfig['compression'] = isset($config['ipsec']['compression']); +$pconfig['enableinterfacesuse'] = isset($config['ipsec']['enableinterfacesuse']); +$pconfig['acceptunencryptedmainmode'] = isset($config['ipsec']['acceptunencryptedmainmode']); +$pconfig['maxmss_enable'] = isset($config['system']['maxmss_enable']); +$pconfig['maxmss'] = $config['system']['maxmss']; +$pconfig['uniqueids'] = $config['ipsec']['uniqueids']; + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + if (!in_array($pconfig['ipsec_dmn'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for Daemon debug."; + } + if (!in_array($pconfig['ipsec_mgr'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for SA Manager debug."; + } + if (!in_array($pconfig['ipsec_ike'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for IKE SA debug."; + } + if (!in_array($pconfig['ipsec_chd'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for IKE Child SA debug."; + } + if (!in_array($pconfig['ipsec_job'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for Job Processing debug."; + } + if (!in_array($pconfig['ipsec_cfg'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for Configuration backend debug."; + } + if (!in_array($pconfig['ipsec_knl'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for Kernel Interface debug."; + } + if (!in_array($pconfig['ipsec_net'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for Networking debug."; + } + if (!in_array($pconfig['ipsec_asn'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for ASN Encoding debug."; + } + if (!in_array($pconfig['ipsec_enc'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for Message encoding debug."; + } + if (!in_array($pconfig['ipsec_imc'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for Integrity checker debug."; + } + if (!in_array($pconfig['ipsec_imv'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for Integrity Verifier debug."; + } + if (!in_array($pconfig['ipsec_pts'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for Platform Trust Service debug."; + } + if (!in_array($pconfig['ipsec_tls'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for TLS Handler debug."; + } + if (!in_array($pconfig['ipsec_esp'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for IPsec Traffic debug."; + } + if (!in_array($pconfig['ipsec_lib'], array('0', '1', '2', '3', '4', '5'), true)) { + $input_errors[] = "A valid value must be specified for StrongSwan Lib debug."; + } + if (isset($pconfig['maxmss'])) { + if (!is_numericint($pconfig['maxmss']) && $pconfig['maxmss'] != '') { + $input_errors[] = "An integer must be specified for Maximum MSS."; + } + if ($pconfig['maxmss'] <> '' && $pconfig['maxmss'] < 576 || $pconfig['maxmss'] > 65535) { + $input_errors[] = "An integer between 576 and 65535 must be specified for Maximum MSS"; + } + } + + if (!$input_errors) { + + foreach ($ipsec_loglevels as $lkey => $ldescr) { + if (empty($_POST["ipsec_{$lkey}"])) { + if (isset($config['ipsec']["ipsec_{$lkey}"])) { + unset($config['ipsec']["ipsec_{$lkey}"]); + } + } else { + $config['ipsec']["ipsec_{$lkey}"] = $_POST["ipsec_{$lkey}"]; + } + } + + $needsrestart = false; + + if ($_POST['compression'] == "yes") { + if (!isset($config['ipsec']['compression'])) { + $needsrestart = true; + } + $config['ipsec']['compression'] = true; + } elseif (isset($config['ipsec']['compression'])) { + $needsrestart = true; + unset($config['ipsec']['compression']); + } + + if ($_POST['enableinterfacesuse'] == "yes") { + if (!isset($config['ipsec']['enableinterfacesuse'])) { + $needsrestart = true; + } + $config['ipsec']['enableinterfacesuse'] = true; + } elseif (isset($config['ipsec']['enableinterfacesuse'])) { + $needsrestart = true; + unset($config['ipsec']['enableinterfacesuse']); + } + + if ($_POST['unityplugin'] == "yes") { + if (!isset($config['ipsec']['unityplugin'])) { + $needsrestart = true; + } + $config['ipsec']['unityplugin'] = true; + } elseif (isset($config['ipsec']['unityplugin'])) { + $needsrestart = true; + unset($config['ipsec']['unityplugin']); + } + + if ($_POST['strictcrlpolicy'] == "yes") { + $config['ipsec']['strictcrlpolicy'] = true; + } elseif (isset($config['ipsec']['strictcrlpolicy'])) { + unset($config['ipsec']['strictcrlpolicy']); + } + + if ($_POST['makebeforebreak'] == "yes") { + $config['ipsec']['makebeforebreak'] = true; + } elseif (isset($config['ipsec']['makebeforebreak'])) { + unset($config['ipsec']['makebeforebreak']); + } + + if ($_POST['noshuntlaninterfaces'] == "yes") { + if (isset($config['ipsec']['noshuntlaninterfaces'])) { + unset($config['ipsec']['noshuntlaninterfaces']); + } + } else { + $config['ipsec']['noshuntlaninterfaces'] = true; + } + + if ($_POST['acceptunencryptedmainmode'] == "yes") { + if (!isset($config['ipsec']['acceptunencryptedmainmode'])) { + $needsrestart = true; + } + $config['ipsec']['acceptunencryptedmainmode'] = true; + } elseif (isset($config['ipsec']['acceptunencryptedmainmode'])) { + $needsrestart = true; + unset($config['ipsec']['acceptunencryptedmainmode']); + } + + if (!empty($_POST['uniqueids'])) { + $config['ipsec']['uniqueids'] = $_POST['uniqueids']; + } else if (isset($config['ipsec']['uniqueids'])) { + unset($config['ipsec']['uniqueids']); + } + + if ($_POST['maxmss_enable'] == "yes") { + $config['system']['maxmss_enable'] = true; + $config['system']['maxmss'] = $_POST['maxmss']; + } else { + if (isset($config['system']['maxmss_enable'])) { + unset($config['system']['maxmss_enable']); + } + if (isset($config['system']['maxmss'])) { + unset($config['system']['maxmss']); + } + } + + write_config(); + + $retval = 0; + $retval = filter_configure(); + if (stristr($retval, "error") <> true) { + $savemsg = get_std_save_message(gettext($retval)); + } else { + $savemsg = gettext($retval); + } + + vpn_ipsec_configure($needsrestart); + vpn_ipsec_configure_loglevels(); + + header("Location: vpn_ipsec_settings.php"); + return; + } + + // The logic value sent by $POST is opposite to the way it is stored in the config. + // Reset the $pconfig value so it reflects the opposite of what was $POSTed. + if ($_POST['noshuntlaninterfaces'] == "yes") { + $pconfig['noshuntlaninterfaces'] = false; + } else { + $pconfig['noshuntlaninterfaces'] = true; + } +} + +$pgtitle = array(gettext("VPN"), gettext("IPsec"), gettext("Settings")); +$shortcut_section = "ipsec"; + +include("head.inc"); +?> + +<script type="text/javascript"> +//<![CDATA[ + +function maxmss_checked(obj) { + if (obj.checked) { + jQuery('#maxmss').attr('disabled', false); + } else { + jQuery('#maxmss').attr('disabled', 'true'); + } +} + +//]]> +</script> + +<?php + if ($savemsg) { + print_info_box($savemsg); + } + if ($input_errors) { + print_input_errors($input_errors); + } +?> + +<?php + +$tab_array = array(); +$tab_array[0] = array(gettext("Tunnels"), false, "vpn_ipsec.php"); +$tab_array[1] = array(gettext("Mobile clients"), false, "vpn_ipsec_mobile.php"); +$tab_array[2] = array(gettext("Pre-Shared Key"), false, "vpn_ipsec_keys.php"); +$tab_array[3] = array(gettext("Advanced Settings"), true, "vpn_ipsec_settings.php"); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); +$form = new Form; + +$section = new Form_Section('Start IPsec in debug mode based on sections selected'); + +foreach ($ipsec_loglevels as $lkey => $ldescr) +{ + $section->addInput(new Form_Select( + 'ipsec_' . $lkey, + $ldescr, + $pconfig['ipsec_' . $lkey], + array('Silent', 'Audit', 'Control', 'Diag', 'Raw', 'Highest') + ))->setWidth(2); +} + +$section->addInput(new Form_StaticText('', ''))->setHelp( + 'Launches IPsec in debug mode so that more verbose logs will be generated to aid in troubleshooting.' +); + +$form->add($section); + +$section = new Form_Section('IPsec Advanced Settings'); + +$section->addInput(new Form_Select( + 'uniqueids', + 'Configure Unique IDs as', + $pconfig['uniqueids'], + $ipsec_idhandling +))->setHelp( + 'Whether a particular participant ID should be kept unique, with any new IKE_SA using an ID ' . + 'deemed to replace all old ones using that ID. Participant IDs normally are unique, so a new ' . + 'IKE_SA using the same ID is almost invariably intended to replace an old one. ' . + 'The difference between <b>no</b> and <b>never</b> is that the old IKE_SAs will be replaced when receiving an ' . + 'INITIAL_CONTACT notify if the option is no but will ignore these notifies if <b>never</b> is configured. ' . + 'The daemon also accepts the value <b>keep</b> to reject ' . + 'new IKE_SA setups and keep the duplicate established earlier. Defaults to Yes.' +); + +$section->addInput(new Form_Checkbox( + 'compression', + 'IP Compression', + 'Enable IPCompression', + $pconfig['compression'] +))->setHelp('IPComp compression of content is proposed on the connection.'); + +$section->addInput(new Form_Checkbox( + 'enableinterfacesuse', + 'Strict interface binding', + 'Enable strict interface binding', + $pconfig['enableinterfacesuse'] +))->setHelp('Enable strongSwan\'s interfaces_use option to bind specific interfaces only. This option is known to break IPsec with dynamic IP interfaces. This is not recommended at this time.'); + +$section->addInput(new Form_Checkbox( + 'acceptunencryptedmainmode', + 'Unencrypted payloads in IKEv1 Main Mode', + 'Accept unencrypted ID and HASH payloads in IKEv1 Main Mode', + $pconfig['acceptunencryptedmainmode'] +))->setHelp( + 'Some implementations send the third Main Mode message unencrypted, probably to find the PSKs for the specified ID for authentication.' . + 'This is very similar to Aggressive Mode, and has the same security implications: ' . + 'A passive attacker can sniff the negotiated Identity, and start brute forcing the PSK using the HASH payload.' . + 'It is recommended to keep this option to no, unless you know exactly what the implications are and require compatibility to such devices (for example, some SonicWall boxes).' +); + +$section->addInput(new Form_Checkbox( + 'maxmss_enable', + 'Enable Maximum MSS', + 'Enable MSS clamping on VPN traffic', + $pconfig['maxmss_enable'] +))->toggles('.toggle-maxmss', 'collapse'); + +$group = new Form_Group('Maximum MSS'); +$group->addClass('toggle-maxmss collapse'); + +if (!empty($pconfig['maxmss_enable'])) + $group->addClass('in'); + +$group->add(new Form_Input( + 'maxmss', + 'Maximum MSS', + 'text', + ($pconfig['maxmss'] ? $pconfig['maxmss'] : '1400') +))->setHelp( + 'Enable MSS clamping on TCP flows over VPN. ' . + 'This helps overcome problems with PMTUD on IPsec VPN links. If left blank, the default value is 1400 bytes. ' +); + +$section->add($group); + +$section->addInput(new Form_Checkbox( + 'unityplugin', + 'Disable Cisco Extensions', + 'Disable Unity Plugin', + $pconfig['unityplugin'] +))->setHelp('Disable Unity Plugin which provides Cisco Extension support as Split-Include, Split-Exclude, Split-Dns, ...'); + +$section->addInput(new Form_Checkbox( + 'shuntlaninterfaces', + 'Bypass LAN address', + 'Enable bypass for LAN interface ip', + $pconfig['shuntlaninterfaces'] +))->setHelp('Prevent LAN ip address to be processed for IPsec traffic.'); + +$form->add($section); + +print $form; + +?> + +<?php include("foot.inc"); ?>
\ No newline at end of file diff --git a/src/usr/local/www/vpn_l2tp.php b/src/usr/local/www/vpn_l2tp.php new file mode 100644 index 0000000..bb61217 --- /dev/null +++ b/src/usr/local/www/vpn_l2tp.php @@ -0,0 +1,525 @@ +<?php +/* + vpn_l2tp.php + part of pfSense + + Copyright (C) 2005 Scott Ullrich (sullrich@gmail.com) + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-vpnl2tp +##|*NAME=VPN: VPN L2TP page +##|*DESCR=Allow access to the 'VPN: VPN L2TP' page. +##|*MATCH=vpn_l2tp.php* +##|-PRIV + +$pgtitle = array(gettext("VPN"), gettext("L2TP"), gettext("L2TP")); +$shortcut_section = "l2tps"; + +require("guiconfig.inc"); +require_once("vpn.inc"); + +if (!is_array($config['l2tp']['radius'])) { + $config['l2tp']['radius'] = array(); +} +$l2tpcfg = &$config['l2tp']; + +$pconfig['remoteip'] = $l2tpcfg['remoteip']; +$pconfig['localip'] = $l2tpcfg['localip']; +$pconfig['l2tp_subnet'] = $l2tpcfg['l2tp_subnet']; +$pconfig['mode'] = $l2tpcfg['mode']; +$pconfig['interface'] = $l2tpcfg['interface']; +$pconfig['l2tp_dns1'] = $l2tpcfg['dns1']; +$pconfig['l2tp_dns2'] = $l2tpcfg['dns2']; +$pconfig['wins'] = $l2tpcfg['wins']; +$pconfig['radiusenable'] = isset($l2tpcfg['radius']['enable']); +$pconfig['radacct_enable'] = isset($l2tpcfg['radius']['accounting']); +$pconfig['radiusserver'] = $l2tpcfg['radius']['server']; +$pconfig['radiussecret'] = $l2tpcfg['radius']['secret']; +$pconfig['radiusissueips'] = $l2tpcfg['radius']['radiusissueips']; +$pconfig['n_l2tp_units'] = $l2tpcfg['n_l2tp_units']; +$pconfig['paporchap'] = $l2tpcfg['paporchap']; +$pconfig['secret'] = $l2tpcfg['secret']; + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if ($_POST['mode'] == "server") { + $reqdfields = explode(" ", "localip remoteip"); + $reqdfieldsn = array(gettext("Server address"), gettext("Remote start address")); + + if ($_POST['radiusenable']) { + $reqdfields = array_merge($reqdfields, explode(" ", "radiusserver radiussecret")); + $reqdfieldsn = array_merge($reqdfieldsn, + array(gettext("RADIUS server address"), gettext("RADIUS shared secret"))); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($_POST['localip'] && !is_ipaddr($_POST['localip']))) { + $input_errors[] = gettext("A valid server address must be specified."); + } + if (is_ipaddr_configured($_POST['localip'])) { + $input_errors[] = gettext("'Server address' parameter should NOT be set to any IP address currently in use on this firewall."); + } + if (($_POST['l2tp_subnet'] && !is_ipaddr($_POST['remoteip']))) { + $input_errors[] = gettext("A valid remote start address must be specified."); + } + if (($_POST['radiusserver'] && !is_ipaddr($_POST['radiusserver']))) { + $input_errors[] = gettext("A valid RADIUS server address must be specified."); + } + + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + + if (!$input_errors) { + $_POST['remoteip'] = $pconfig['remoteip'] = gen_subnet($_POST['remoteip'], $_POST['l2tp_subnet']); + $subnet_start = ip2ulong($_POST['remoteip']); + $subnet_end = ip2ulong($_POST['remoteip']) + $_POST['n_l2tp_units'] - 1; + + if ((ip2ulong($_POST['localip']) >= $subnet_start) && + (ip2ulong($_POST['localip']) <= $subnet_end)) { + $input_errors[] = gettext("The specified server address lies in the remote subnet."); + } + if ($_POST['localip'] == get_interface_ip("lan")) { + $input_errors[] = gettext("The specified server address is equal to the LAN interface address."); + } + } + } + + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + + if (!$input_errors) { + $l2tpcfg['remoteip'] = $_POST['remoteip']; + $l2tpcfg['localip'] = $_POST['localip']; + $l2tpcfg['l2tp_subnet'] = $_POST['l2tp_subnet']; + $l2tpcfg['mode'] = $_POST['mode']; + $l2tpcfg['interface'] = $_POST['interface']; + $l2tpcfg['n_l2tp_units'] = $_POST['n_l2tp_units']; + + $l2tpcfg['radius']['server'] = $_POST['radiusserver']; + $l2tpcfg['radius']['secret'] = $_POST['radiussecret']; + $l2tpcfg['secret'] = $_POST['secret']; + + if ($_POST['wins']) { + $l2tpcfg['wins'] = $_POST['wins']; + } else { + unset($l2tpcfg['wins']); + } + + $l2tpcfg['paporchap'] = $_POST['paporchap']; + + + if ($_POST['l2tp_dns1'] == "") { + if (isset($l2tpcfg['dns1'])) { + unset($l2tpcfg['dns1']); + } + } else { + $l2tpcfg['dns1'] = $_POST['l2tp_dns1']; + } + + if ($_POST['l2tp_dns2'] == "") { + if (isset($l2tpcfg['dns2'])) { + unset($l2tpcfg['dns2']); + } + } else { + $l2tpcfg['dns2'] = $_POST['l2tp_dns2']; + } + + if ($_POST['radiusenable'] == "yes") { + $l2tpcfg['radius']['enable'] = true; + } else { + unset($l2tpcfg['radius']['enable']); + } + + if ($_POST['radacct_enable'] == "yes") { + $l2tpcfg['radius']['accounting'] = true; + } else { + unset($l2tpcfg['radius']['accounting']); + } + + if ($_POST['radiusissueips'] == "yes") { + $l2tpcfg['radius']['radiusissueips'] = true; + } else { + unset($l2tpcfg['radius']['radiusissueips']); + } + + write_config(); + + $retval = 0; + $retval = vpn_l2tp_configure(); + $savemsg = get_std_save_message($retval); + + /* if ajax is calling, give them an update message */ + if (isAjax()) { + print_info_box_np($savemsg); + } + } +} + +include("head.inc"); +?> + +<script type="text/javascript"> +//<![CDATA[ +function get_radio_value(obj) { + for (i = 0; i < obj.length; i++) { + if (obj[i].checked) { + return obj[i].value; + } + } + return null; +} + +function enable_change(enable_over) { + if ((get_radio_value(document.iform.mode) == "server") || enable_over) { + document.iform.remoteip.disabled = 0; + document.iform.localip.disabled = 0; + document.iform.l2tp_subnet.disabled = 0; + document.iform.radiusenable.disabled = 0; + document.iform.radiusissueips.disabled = 0; + document.iform.paporchap.disabled = 0; + document.iform.interface.disabled = 0; + document.iform.n_l2tp_units.disabled = 0; + document.iform.secret.disabled = 0; + document.iform.l2tp_dns1.disabled = 0; + document.iform.l2tp_dns2.disabled = 0; + /* fix colors */ + document.iform.remoteip.style.backgroundColor = '#FFFFFF'; + document.iform.localip.style.backgroundColor = '#FFFFFF'; + document.iform.l2tp_subnet.style.backgroundColor = '#FFFFFF'; + document.iform.radiusenable.style.backgroundColor = '#FFFFFF'; + document.iform.radiusissueips.style.backgroundColor = '#FFFFFF'; + document.iform.paporchap.style.backgroundColor = '#FFFFFF'; + document.iform.interface.style.backgroundColor = '#FFFFFF'; + document.iform.n_l2tp_units.style.backgroundColor = '#FFFFFF'; + document.iform.secret.style.backgroundColor = '#FFFFFF'; + if (document.iform.radiusenable.checked || enable_over) { + document.iform.radacct_enable.disabled = 0; + document.iform.radiusserver.disabled = 0; + document.iform.radiussecret.disabled = 0; + document.iform.radiusissueips.disabled = 0; + /* fix colors */ + document.iform.radacct_enable.style.backgroundColor = '#FFFFFF'; + document.iform.radiusserver.style.backgroundColor = '#FFFFFF'; + document.iform.radiussecret.style.backgroundColor = '#FFFFFF'; + document.iform.radiusissueips.style.backgroundColor = '#FFFFFF'; + } else { + document.iform.radacct_enable.disabled = 1; + document.iform.radiusserver.disabled = 1; + document.iform.radiussecret.disabled = 1; + document.iform.radiusissueips.disabled = 1; + /* fix colors */ + document.iform.radacct_enable.style.backgroundColor = '#D4D0C8'; + document.iform.radiusserver.style.backgroundColor = '#D4D0C8'; + document.iform.radiussecret.style.backgroundColor = '#D4D0C8'; + document.iform.radiusissueips.style.backgroundColor = '#D4D0C8'; + } + } else { + document.iform.interface.disabled = 1; + document.iform.n_l2tp_units.disabled = 1; + document.iform.l2tp_subnet.disabled = 1; + document.iform.l2tp_dns1.disabled = 1; + document.iform.l2tp_dns2.disabled = 1; + document.iform.paporchap.disabled = 1; + document.iform.remoteip.disabled = 1; + document.iform.localip.disabled = 1; + document.iform.radiusenable.disabled = 1; + document.iform.radacct_enable.disabled = 1; + document.iform.radiusserver.disabled = 1; + document.iform.radiussecret.disabled = 1; + document.iform.radiusissueips.disabled = 1; + document.iform.secret.disabled = 1; + /* fix colors */ + document.iform.interface.style.backgroundColor = '#D4D0C8'; + document.iform.n_l2tp_units.style.backgroundColor = '#D4D0C8'; + document.iform.l2tp_subnet.style.backgroundColor = '#D4D0C8'; + document.iform.paporchap.style.backgroundColor = '#D4D0C8'; + document.iform.remoteip.style.backgroundColor = '#D4D0C8'; + document.iform.localip.style.backgroundColor = '#D4D0C8'; + document.iform.radiusenable.style.backgroundColor = '#D4D0C8'; + document.iform.radacct_enable.style.backgroundColor = '#D4D0C8'; + document.iform.radiusserver.style.backgroundColor = '#D4D0C8'; + document.iform.radiussecret.style.backgroundColor = '#D4D0C8'; + document.iform.radiusissueips.style.backgroundColor = '#D4D0C8'; + document.iform.secret.style.backgroundColor = '#D4D0C8'; + } +} +//]]> +</script> + +<form class="form-horizontal" action="vpn_l2tp.php" method="post" name="iform" id="iform"> +<?php if ($input_errors) print_input_errors($input_errors)?> +<?php if ($savemsg) print_info_box($savemsg)?> + +<?php +$tab_array = array(); +$tab_array[0] = array(gettext("Configuration"), true, "vpn_l2tp.php"); +$tab_array[1] = array(gettext("Users"), false, "vpn_l2tp_users.php"); +display_top_tabs($tab_array); +?> + + <div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title"><?=gettext('Enable L2TP'); ?></h2> + </div> + + <div class="panel-body"> + <div class="form-group"> + <div class="col-sm-10"> + <label> + <input name="mode" type="radio" onclick="enable_change(false)" value="off" <?php if (($pconfig['mode'] != "server") && ($pconfig['mode'] != "redir")) echo "checked=\"checked\""?> /> + <?=gettext("Off")?> + </label> + <label> + <input type="radio" name="mode" value="server" onclick="enable_change(false)" <?php if ($pconfig['mode'] == "server") echo "checked=\"checked\""?> /> + <?=gettext("Enable L2TP server")?> + </label> + </div> + </div> + </div> + </div> + + <div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title"><?=gettext('Configuration'); ?></h2> + </div> + + <div class="panel-body"> + <div class="form-group"> + <label for="interface" class="col-sm-2 control-label"><?=gettext("Interface")?></label> + <div class="col-sm-2"> + <select class="form-control" name="interface" class="formselect" id="interface"> +<?php +$interfaces = get_configured_interface_with_descr(); +foreach ($interfaces as $iface => $ifacename): ?> + <option value="<?=$iface?>" <?php if ($iface == $pconfig['interface']) echo "selected=\"selected\""?>> + <?=htmlspecialchars($ifacename)?> + </option> +<?php endforeach?> + </select> + </div> + </div> + <div class="form-group"> + <label for="localip" class="col-sm-2 control-label"><?=gettext("Server Address")?></label> + <div class="col-sm-10"> + <?=$mandfldhtml?><input name="localip" type="text" class="form-control formfld unknown" id="localip" size="20" value="<?=htmlspecialchars($pconfig['localip'])?>" /> + + <span class="help-block"> + <?=gettext("Enter the IP address the L2TP server should give to clients for use as their \"gateway\"")?>. + <br /> + <?=gettext("Typically this is set to an unused IP just outside of the client range")?>. + <br /> + <br /> + <?=gettext("NOTE: This should NOT be set to any IP address currently in use on this firewall")?>. + </span> + </div> + </div> + + <div class="form-group"> + <label for="remoteip" class="col-sm-2 control-label"><?=gettext("Remote Address Range")?></label> + <div class="col-sm-10"> + <?=$mandfldhtml?><input name="remoteip" type="text" class="form-control formfld unknown" id="remoteip" size="20" value="<?=htmlspecialchars($pconfig['remoteip'])?>" /> + <span class="help-block"> + <?=gettext("Specify the starting address for the client IP address subnet.")?> + </span> + </div> + </div> + + <div class="form-group"> + <label for="l2tp_subnet" class="col-sm-2 control-label"><?=gettext("Subnet Mask")?></label> + <div class="col-sm-2"> + <select id="l2tp_subnet" name="l2tp_subnet" class="form-control"> +<?php + for($x=0; $x<33; $x++) { + if($x == $pconfig['l2tp_subnet']) + $SELECTED = " selected=\"selected\""; + else + $SELECTED = ""; + echo "<option value=\"{$x}\"{$SELECTED}>{$x}</option>\n"; + } +?> + </select> + <span class="help-block"> + <?=gettext("Hint:")?> 24 <?=gettext("is")?> 255.255.255.0 + </span> + </div> + </div> + + <div class="form-group"> + <label for="n_l2tp_units" class="col-sm-2 control-label"><?=gettext("Number of L2TP users")?></label> + <div class="col-sm-2"> + <select id="n_l2tp_units" name="n_l2tp_units" class="form-control"> +<?php + for($x=0; $x<255; $x++) { + if($x == $pconfig['n_l2tp_units']) + $SELECTED = " selected=\"selected\""; + else + $SELECTED = ""; + echo "<option value=\"{$x}\"{$SELECTED}>{$x}</option>\n"; + } +?> + </select> + <span class="help-block"> + <?=gettext("Hint:")?> 10 <?=gettext("is ten L2TP clients")?> + </span> + </div> + </div> + + <div class="form-group"> + <label for="secret" class="col-sm-2 control-label"><?=gettext("Secret")?></label> + <div class="col-sm-10"> + <input type="password" name="secret" id="secret" class="formfld pwd form-control" value="<?=htmlspecialchars($pconfig['secret'])?>" /> + <span class="help-block"> + <?=gettext("Specify optional secret shared between peers. Required on some devices/setups.")?> + </span> + </div> + </div> + + <div class="form-group"> + <label for="paporchap" class="col-sm-2 control-label"><?=gettext("Authentication Type")?></label> + <div class="col-sm-2"> + <?=$mandfldhtml?><select name="paporchap" id="paporchap" class="form-control"> + <option value='chap'<?php if($pconfig['paporchap'] == "chap") echo " selected=\"selected\""?>><?=gettext("CHAP")?></option> + <option value='pap'<?php if($pconfig['paporchap'] == "pap") echo " selected=\"selected\""?>><?=gettext("PAP")?></option> + </select> + <span class="help-block"> + <?=gettext("Specifies which protocol to use for authentication.")?> + </span> + </div> + </div> + + <div class="form-group"> + <label for="l2tp_dns1" class="col-sm-2 control-label"><?=gettext("L2TP DNS Servers")?></label> + <div class="col-sm-10"> + <?=$mandfldhtml?><input name="l2tp_dns1" type="text" class="formfld unknown form-control" id="l2tp_dns1" size="20" value="<?=htmlspecialchars($pconfig['l2tp_dns1'])?>" /> + <input name="l2tp_dns2" type="text" class="formfld unknown form-control" id="l2tp_dns2" size="20" value="<?=htmlspecialchars($pconfig['l2tp_dns2'])?>" /> + <span class="help-block"> + <?=gettext("primary and secondary DNS servers assigned to L2TP clients")?> + </span> + </div> + </div> + + <div class="form-group"> + <label for="wins" class="col-sm-2 control-label"><?=gettext("WINS Server")?></label> + <div class="col-sm-10"> + <input name="wins" class="formfld unknown form-control" id="wins" size="20" value="<?=htmlspecialchars($pconfig['wins'])?>" /> + </div> + </div> + </div> + </div> + + <div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title"><?=gettext('RADIUS'); ?></h2> + </div> + + <div class="panel-body"> + <div class="form-group"> + <label for="radiusenable" class="col-sm-2 control-label"><?=gettext('Enable')?></label> + <div class="col-sm-10 checkbox"> + <label> + <input name="radiusenable" type="checkbox" id="radiusenable" onclick="enable_change(false)" value="yes" <?php if ($pconfig['radiusenable']) echo "checked=\"checked\""?> /> + <?=gettext("Use a RADIUS server for authentication")?> + </label> + <span class="help-block"> + <?=gettext("When set, all users will be authenticated using the RADIUS server specified below. The local user database will not be used.")?> + </span> + </div> + </div> + <div class="form-group"> + <label for="radacct_enable" class="col-sm-2 control-label"><?=gettext('Enable accounting')?></label> + <div class="col-sm-10 checkbox"> + <label> + <input name="radacct_enable" type="checkbox" id="radacct_enable" onclick="enable_change(false)" value="yes" <?php if ($pconfig['radacct_enable']) echo "checked=\"checked\""?> /> + <?=gettext("Enable RADIUS accounting")?> + </label> + <span class="help-block"> + <?=gettext("Sends accounting packets to the RADIUS server.")?> + </span> + </div> + </div> + + <div class="form-group"> + <label for="radiusserver" class="col-sm-2 control-label"><?=gettext("RADIUS Server")?></label> + <div class="col-sm-10"> + <input name="radiusserver" type="text" class="formfld unknown form-control" id="radiusserver" size="20" value="<?=htmlspecialchars($pconfig['radiusserver'])?>" /> + <span class="help-block"> + <?=gettext("Enter the IP address of the RADIUS server.")?> + </span> + </div> + </div> + <div class="form-group"> + <label for="radiussecret" class="col-sm-2 control-label"><?=gettext("RADIUS Shared Secret")?></label> + <div class="col-sm-10"> + <input name="radiussecret" type="password" class="formfld pwd form-control" id="radiussecret" size="20" value="<?=htmlspecialchars($pconfig['radiussecret'])?>" /> + <span class="help-block"> + <?=gettext("Enter the shared secret that will be used to authenticate to the RADIUS server.")?> + </span> + </div> + </div> + + <div class="form-group"> + <label for="radiusissueips" class="col-sm-2 control-label"><?=gettext("RADIUS Issued IP's")?></label> + <div class="col-sm-10 checkbox"> + <label> + <input name="radiusissueips" value="yes" type="checkbox" class="formfld" id="radiusissueips"<?php if(isset($pconfig['radiusissueips'])) echo " checked=\"checked\""?> /> + <?=gettext("Issue IP Addresses via RADIUS server.")?> + </label> + </div> + </div> + </div> + </div> + +<?php + // TODO: Is it possible to detect available rules and only show warning if there are no (relevant) rules set? +?> + <div class="alert alert-danger"> + <strong><?=gettext("Note:")?></strong> <?=gettext("Don't forget to add a firewall rule to permit traffic from L2TP clients!")?> + </div> + + <div class="col-sm-10 col-sm-offset-2"> + <input id="submit" name="Submit" type="submit" class="btn btn-primary" value="<?=gettext("Save")?>" onclick="enable_change(true)" /> + </div> +</form> + +<script type="text/javascript"> +//<![CDATA[ + enable_change(false); +//]]> +</script> + +<?php include("foot.inc")?>
\ No newline at end of file diff --git a/src/usr/local/www/vpn_l2tp_users.php b/src/usr/local/www/vpn_l2tp_users.php new file mode 100644 index 0000000..3ec4408 --- /dev/null +++ b/src/usr/local/www/vpn_l2tp_users.php @@ -0,0 +1,128 @@ +<?php +/* + vpn_l2tp_users.php + part of pfSense + + Copyright (C) 2005 Scott Ullrich (sullrich@gmail.com) + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-vpnl2tp-users +##|*NAME=VPN: VPN L2TP : Users page +##|*DESCR=Allow access to the 'VPN: VPN L2TP : Users' page. +##|*MATCH=vpn_l2tp_users.php* +##|-PRIV + +$pgtitle = array(gettext("VPN"), gettext("L2TP"), gettext("Users")); +$shortcut_section = "l2tps"; + +require("guiconfig.inc"); +require_once("vpn.inc"); + +if (!is_array($config['l2tp']['user'])) { + $config['l2tp']['user'] = array(); +} +$a_secret = &$config['l2tp']['user']; + +if ($_POST) { + + $pconfig = $_POST; + + if ($_POST['apply']) { + $retval = 0; + if (!is_subsystem_dirty('rebootreq')) { + $retval = vpn_l2tp_configure(); + } + $savemsg = get_std_save_message($retval); + if ($retval == 0) { + if (is_subsystem_dirty('l2tpusers')) { + clear_subsystem_dirty('l2tpusers'); + } + } + } +} + +if ($_GET['act'] == "del") { + if ($a_secret[$_GET['id']]) { + unset($a_secret[$_GET['id']]); + write_config(); + mark_subsystem_dirty('l2tpusers'); + pfSenseHeader("vpn_l2tp_users.php"); + exit; + } +} + +include("head.inc"); +?> + +<?php if ($savemsg) print_info_box($savemsg)?> +<?php if (isset($config['l2tp']['radius']['enable'])) + print_info_box(gettext("Warning: RADIUS is enabled. The local user database will not be used."))?> +<?php if (is_subsystem_dirty('l2tpusers')):?><br/> +<?php print_info_box_np(gettext("The l2tp user list has been modified") . ".<br />" . gettext("You must apply the changes in order for them to take effect") . ".<br /><b>" . gettext("Warning: this will terminate all current l2tp sessions!") . "</b>")?><br /> +<?php endif?> + +<?php + $tab_array = array(); + $tab_array[0] = array(gettext("Configuration"), false, "vpn_l2tp.php"); + $tab_array[1] = array(gettext("Users"), true, "vpn_l2tp_users.php"); + display_top_tabs($tab_array); +?> +<div class="table-responsive"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th><?=gettext("Username")?></th> + <th><?=gettext("IP address")?></th> + <th></th> + </tr> + </thead> + <tbody> +<?php $i = 0; foreach ($a_secret as $secretent):?> + <tr> + <td> + <?=htmlspecialchars($secretent['name'])?> + </td> + <td> + <?php if($secretent['ip'] == "") $secretent['ip'] = "Dynamic"?> + <?=htmlspecialchars($secretent['ip'])?> + </td> + <td> + <a class="btn btn-xs btn-primary" href="vpn_l2tp_users_edit.php?id=<?=$i?>"><?= gettext('edit') ?></a> + <a class="btn btn-xs btn-danger" href="vpn_l2tp_users.php?act=del&id=<?=$i?>"><?=gettext("delete")?></a> + </td> + </tr> +<?php $i++; endforeach?> + </tbody> + </table> +</div> + +<nav class="action-buttons"> + <a class="btn btn-success" href="vpn_l2tp_users_edit.php"><?=gettext("add user")?></a> +</nav> + + +<?php include("foot.inc")?> diff --git a/src/usr/local/www/vpn_l2tp_users_edit.php b/src/usr/local/www/vpn_l2tp_users_edit.php new file mode 100644 index 0000000..c6ee99b --- /dev/null +++ b/src/usr/local/www/vpn_l2tp_users_edit.php @@ -0,0 +1,209 @@ +<?php +/* + vpn_l2tp_users_edit.php + part of pfSense + + Copyright (C) 2006 Scott Ullrich (sullrich@gmail.com) + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-vpnl2tp-users-edit +##|*NAME=VPN: VPN L2TP : Users : Edit page +##|*DESCR=Allow access to the 'VPN: VPN L2TP : Users : Edit' page. +##|*MATCH=vpn_l2tp_users_edit.php* +##|-PRIV + +$pgtitle = array(gettext("VPN"), gettext("L2TP"), gettext("User"), gettext("Edit")); +$shortcut_section = "l2tps"; + +function l2tpusercmp($a, $b) { + return strcasecmp($a['name'], $b['name']); +} + +function l2tp_users_sort() { + global $config; + + if (!is_array($config['l2tp']['user'])) { + return; + } + + usort($config['l2tp']['user'], "l2tpusercmp"); +} + +require("guiconfig.inc"); +require_once("vpn.inc"); + +if (!is_array($config['l2tp']['user'])) { + $config['l2tp']['user'] = array(); +} +$a_secret = &$config['l2tp']['user']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +if (isset($id) && $a_secret[$id]) { + $pconfig['usernamefld'] = $a_secret[$id]['name']; + $pconfig['ip'] = $a_secret[$id]['ip']; +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if (isset($id) && ($a_secret[$id])) { + $reqdfields = explode(" ", "usernamefld"); + $reqdfieldsn = array(gettext("Username")); + } else { + $reqdfields = explode(" ", "usernamefld passwordfld"); + $reqdfieldsn = array(gettext("Username"), gettext("Password")); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (preg_match("/[^a-zA-Z0-9\.\-_]/", $_POST['usernamefld'])) { + $input_errors[] = gettext("The username contains invalid characters."); + } + + if (preg_match("/[^a-zA-Z0-9\.\-_]/", $_POST['passwordfld'])) { + $input_errors[] = gettext("The password contains invalid characters."); + } + + if (($_POST['passwordfld']) && ($_POST['passwordfld'] != $_POST['passwordfld2'])) { + $input_errors[] = gettext("The passwords do not match."); + } + if (($_POST['ip'] && !is_ipaddr($_POST['ip']))) { + $input_errors[] = gettext("The IP address entered is not valid."); + } + + if (!$input_errors && !(isset($id) && $a_secret[$id])) { + /* make sure there are no dupes */ + foreach ($a_secret as $secretent) { + if ($secretent['name'] == $_POST['usernamefld']) { + $input_errors[] = gettext("Another entry with the same username already exists."); + break; + } + } + } + + /* if this is an AJAX caller then handle via JSON */ + if (isAjax() && is_array($input_errors)) { + input_errors2Ajax($input_errors); + exit; + } + + if (!$input_errors) { + + if (isset($id) && $a_secret[$id]) { + $secretent = $a_secret[$id]; + } + + $secretent['name'] = $_POST['usernamefld']; + $secretent['ip'] = $_POST['ip']; + + if ($_POST['passwordfld']) { + $secretent['password'] = $_POST['passwordfld']; + } + + if (isset($id) && $a_secret[$id]) { + $a_secret[$id] = $secretent; + } else { + $a_secret[] = $secretent; + } + l2tp_users_sort(); + + write_config(); + + $retval = vpn_l2tp_configure(); + + pfSenseHeader("vpn_l2tp_users.php"); + + exit; + } +} + +include("head.inc"); +?> + +<?php +if ($input_errors) + print_input_errors($input_errors); +?> + +<form class="form-horizontal" action="vpn_l2tp_users_edit.php" method="post" name="iform" id="iform"> + <div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title"><?=gettext('User'); ?></h2> + </div> + + <div class="panel-body"> + <div class="form-group"> + <label for="usernamefld" class="col-sm-2 control-label"><?=gettext("Username")?></label> + <div class="col-sm-10"> + <?=$mandfldhtml?><input name="usernamefld" type="text" class="formfld user form-control" id="usernamefld" size="20" value="<?=htmlspecialchars($pconfig['usernamefld'])?>" /> + </div> + </div> + <div class="form-group"> + <label for="passwordfld" class="col-sm-2 control-label"><?=gettext("Password")?></label> + <div class="col-sm-10"> + <?=$mandfldhtml?><input name="passwordfld" type="password" class="formfld pwd form-control" id="passwordfld" size="20" /> + </div> + </div> + <div class="form-group"> + <label for="passwordfld2" class="col-sm-2 control-label"><?=gettext('Confirm')?></label> + <div class="col-sm-10"> + <?=$mandfldhtml?><input name="passwordfld2" type="password" class="formfld pwd form-control" id="passwordfld2" size="20" /> +<?php if (isset($id) && $a_secret[$id]):?> + <span class="help-block"><?=gettext("If you want to change the users password, enter it here twice.")?></span> +<?php endif?> + </div> + </div> + <div class="form-group"> + <label for="ip" class="col-sm-2 control-label"><?=gettext("IP address")?></label> + <div class="col-sm-10"> + <input name="ip" type="text" class="formfld unknown form-control" id="ip" size="20" value="<?=htmlspecialchars($pconfig['ip'])?>" /> + <span class="help-block"><?=gettext("If you want the user to be assigned a specific IP address, enter it here.")?></span> + </div> + </div> + </div> + </div> + + <div class="col-sm-10 col-sm-offset-2"> + <input id="submit" name="Submit" type="submit" class="formbtn btn btn-primary" value="<?=gettext('Save')?>" /> + </div> + +<?php if (isset($id) && $a_secret[$id]):?> + <input name="id" type="hidden" value="<?=htmlspecialchars($id)?>" /> +<?php endif?> +</form> + +<?php +include("foot.inc"); diff --git a/src/usr/local/www/vpn_openvpn_client.php b/src/usr/local/www/vpn_openvpn_client.php new file mode 100644 index 0000000..41296a3 --- /dev/null +++ b/src/usr/local/www/vpn_openvpn_client.php @@ -0,0 +1,987 @@ +<?php +/* + vpn_openvpn_client.php + + Copyright (C) 2008 Shrew Soft Inc. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-openvpn-client +##|*NAME=OpenVPN: Client page +##|*DESCR=Allow access to the 'OpenVPN: Client' page. +##|*MATCH=vpn_openvpn_client.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("openvpn.inc"); +require_once("pkg-utils.inc"); + +$pgtitle = array(gettext("OpenVPN"), gettext("Client")); +$shortcut_section = "openvpn"; + +if (!is_array($config['openvpn']['openvpn-client'])) { + $config['openvpn']['openvpn-client'] = array(); +} + +$a_client = &$config['openvpn']['openvpn-client']; + +if (!is_array($config['ca'])) { + $config['ca'] = array(); +} + +$a_ca =& $config['ca']; + +if (!is_array($config['cert'])) { + $config['cert'] = array(); +} + +$a_cert =& $config['cert']; + +if (!is_array($config['crl'])) { + $config['crl'] = array(); +} + +$a_crl =& $config['crl']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +$act = $_GET['act']; +if (isset($_POST['act'])) { + $act = $_POST['act']; +} + +if (isset($id) && $a_client[$id]) { + $vpnid = $a_client[$id]['vpnid']; +} else { + $vpnid = 0; +} + +if ($_GET['act'] == "del") { + + if (!isset($a_client[$id])) { + pfSenseHeader("vpn_openvpn_client.php"); + exit; + } + if (!empty($a_client[$id])) { + openvpn_delete('client', $a_client[$id]); + } + unset($a_client[$id]); + write_config(); + $savemsg = gettext("Client successfully deleted")."<br />"; +} + +if ($_GET['act'] == "new") { + $pconfig['autokey_enable'] = "yes"; + $pconfig['tlsauth_enable'] = "yes"; + $pconfig['autotls_enable'] = "yes"; + $pconfig['interface'] = "wan"; + $pconfig['server_port'] = 1194; + $pconfig['verbosity_level'] = 1; // Default verbosity is 1 + // OpenVPN Defaults to SHA1 + $pconfig['digest'] = "SHA1"; +} + +global $simplefields; +$simplefields = array('auth_user', 'auth_pass'); + +if ($_GET['act'] == "edit") { + + if (isset($id) && $a_client[$id]) { + foreach ($simplefields as $stat) { + $pconfig[$stat] = $a_client[$id][$stat]; + } + + $pconfig['disable'] = isset($a_client[$id]['disable']); + $pconfig['mode'] = $a_client[$id]['mode']; + $pconfig['protocol'] = $a_client[$id]['protocol']; + $pconfig['interface'] = $a_client[$id]['interface']; + if (!empty($a_client[$id]['ipaddr'])) { + $pconfig['interface'] = $pconfig['interface'] . '|' . $a_client[$id]['ipaddr']; + } + $pconfig['local_port'] = $a_client[$id]['local_port']; + $pconfig['server_addr'] = $a_client[$id]['server_addr']; + $pconfig['server_port'] = $a_client[$id]['server_port']; + $pconfig['resolve_retry'] = $a_client[$id]['resolve_retry']; + $pconfig['proxy_addr'] = $a_client[$id]['proxy_addr']; + $pconfig['proxy_port'] = $a_client[$id]['proxy_port']; + $pconfig['proxy_user'] = $a_client[$id]['proxy_user']; + $pconfig['proxy_passwd'] = $a_client[$id]['proxy_passwd']; + $pconfig['proxy_authtype'] = $a_client[$id]['proxy_authtype']; + $pconfig['description'] = $a_client[$id]['description']; + $pconfig['custom_options'] = $a_client[$id]['custom_options']; + $pconfig['ns_cert_type'] = $a_client[$id]['ns_cert_type']; + $pconfig['dev_mode'] = $a_client[$id]['dev_mode']; + + if ($pconfig['mode'] != "p2p_shared_key") { + $pconfig['caref'] = $a_client[$id]['caref']; + $pconfig['certref'] = $a_client[$id]['certref']; + if ($a_client[$id]['tls']) { + $pconfig['tlsauth_enable'] = "yes"; + $pconfig['tls'] = base64_decode($a_client[$id]['tls']); + } + } else { + $pconfig['shared_key'] = base64_decode($a_client[$id]['shared_key']); + } + $pconfig['crypto'] = $a_client[$id]['crypto']; + // OpenVPN Defaults to SHA1 if unset + $pconfig['digest'] = !empty($a_client[$id]['digest']) ? $a_client[$id]['digest'] : "SHA1"; + $pconfig['engine'] = $a_client[$id]['engine']; + + $pconfig['tunnel_network'] = $a_client[$id]['tunnel_network']; + $pconfig['tunnel_networkv6'] = $a_client[$id]['tunnel_networkv6']; + $pconfig['remote_network'] = $a_client[$id]['remote_network']; + $pconfig['remote_networkv6'] = $a_client[$id]['remote_networkv6']; + $pconfig['use_shaper'] = $a_client[$id]['use_shaper']; + $pconfig['compression'] = $a_client[$id]['compression']; + $pconfig['passtos'] = $a_client[$id]['passtos']; + + // just in case the modes switch + $pconfig['autokey_enable'] = "yes"; + $pconfig['autotls_enable'] = "yes"; + + $pconfig['no_tun_ipv6'] = $a_client[$id]['no_tun_ipv6']; + $pconfig['route_no_pull'] = $a_client[$id]['route_no_pull']; + $pconfig['route_no_exec'] = $a_client[$id]['route_no_exec']; + if (isset($a_client[$id]['verbosity_level'])) { + $pconfig['verbosity_level'] = $a_client[$id]['verbosity_level']; + } else { + $pconfig['verbosity_level'] = 1; // Default verbosity is 1 + } + } +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + if (isset($id) && $a_client[$id]) { + $vpnid = $a_client[$id]['vpnid']; + } else { + $vpnid = 0; + } + + list($iv_iface, $iv_ip) = explode ("|", $pconfig['interface']); + if (is_ipaddrv4($iv_ip) && (stristr($pconfig['protocol'], "6") !== false)) { + $input_errors[] = gettext("Protocol and IP address families do not match. You cannot select an IPv6 protocol and an IPv4 IP address."); + } elseif (is_ipaddrv6($iv_ip) && (stristr($pconfig['protocol'], "6") === false)) { + $input_errors[] = gettext("Protocol and IP address families do not match. You cannot select an IPv4 protocol and an IPv6 IP address."); + } elseif ((stristr($pconfig['protocol'], "6") === false) && !get_interface_ip($iv_iface) && ($pconfig['interface'] != "any")) { + $input_errors[] = gettext("An IPv4 protocol was selected, but the selected interface has no IPv4 address."); + } elseif ((stristr($pconfig['protocol'], "6") !== false) && !get_interface_ipv6($iv_iface) && ($pconfig['interface'] != "any")) { + $input_errors[] = gettext("An IPv6 protocol was selected, but the selected interface has no IPv6 address."); + } + + if ($pconfig['mode'] != "p2p_shared_key") { + $tls_mode = true; + } else { + $tls_mode = false; + } + + /* input validation */ + if ($pconfig['local_port']) { + + if ($result = openvpn_validate_port($pconfig['local_port'], 'Local port')) { + $input_errors[] = $result; + } + + $portused = openvpn_port_used($pconfig['protocol'], $pconfig['interface'], $pconfig['local_port'], $vpnid); + if (($portused != $vpnid) && ($portused != 0)) { + $input_errors[] = gettext("The specified 'Local port' is in use. Please select another value"); + } + } + + if ($result = openvpn_validate_host($pconfig['server_addr'], 'Server host or address')) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_port($pconfig['server_port'], 'Server port')) { + $input_errors[] = $result; + } + + if ($pconfig['proxy_addr']) { + + if ($result = openvpn_validate_host($pconfig['proxy_addr'], 'Proxy host or address')) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_port($pconfig['proxy_port'], 'Proxy port')) { + $input_errors[] = $result; + } + + if ($pconfig['proxy_authtype'] != "none") { + if (empty($pconfig['proxy_user']) || empty($pconfig['proxy_passwd'])) { + $input_errors[] = gettext("User name and password are required for proxy with authentication."); + } + } + } + + if ($pconfig['tunnel_network']) { + if ($result = openvpn_validate_cidr($pconfig['tunnel_network'], 'IPv4 Tunnel Network', false, "ipv4")) { + $input_errors[] = $result; + } + } + + if ($pconfig['tunnel_networkv6']) { + if ($result = openvpn_validate_cidr($pconfig['tunnel_networkv6'], 'IPv6 Tunnel Network', false, "ipv6")) { + $input_errors[] = $result; + } + } + + if ($result = openvpn_validate_cidr($pconfig['remote_network'], 'IPv4 Remote Network', true, "ipv4")) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_cidr($pconfig['remote_networkv6'], 'IPv6 Remote Network', true, "ipv6")) { + $input_errors[] = $result; + } + + if (!empty($pconfig['use_shaper']) && (!is_numeric($pconfig['use_shaper']) || ($pconfig['use_shaper'] <= 0))) { + $input_errors[] = gettext("The bandwidth limit must be a positive numeric value."); + } + + if ($pconfig['autokey_enable']) { + $pconfig['shared_key'] = openvpn_create_key(); + } + + if (!$tls_mode && !$pconfig['autokey_enable']) { + if (!strstr($pconfig['shared_key'], "-----BEGIN OpenVPN Static key V1-----") || + !strstr($pconfig['shared_key'], "-----END OpenVPN Static key V1-----")) { + $input_errors[] = gettext("The field 'Shared Key' does not appear to be valid"); + } + } + + if ($tls_mode && $pconfig['tlsauth_enable'] && !$pconfig['autotls_enable']) { + if (!strstr($pconfig['tls'], "-----BEGIN OpenVPN Static key V1-----") || + !strstr($pconfig['tls'], "-----END OpenVPN Static key V1-----")) { + $input_errors[] = gettext("The field 'TLS Authentication Key' does not appear to be valid"); + } + } + + /* If we are not in shared key mode, then we need the CA/Cert. */ + if ($pconfig['mode'] != "p2p_shared_key") { + $reqdfields = explode(" ", "caref"); + $reqdfieldsn = array(gettext("Certificate Authority")); + } elseif (!$pconfig['autokey_enable']) { + /* We only need the shared key filled in if we are in shared key mode and autokey is not selected. */ + $reqdfields = array('shared_key'); + $reqdfieldsn = array(gettext('Shared key')); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($pconfig['mode'] != "p2p_shared_key") && empty($pconfig['certref']) && empty($pconfig['auth_user']) && empty($pconfig['auth_pass'])) { + $input_errors[] = gettext("If no Client Certificate is selected, a username and/or password must be entered."); + } + + if (!$input_errors) { + + $client = array(); + + foreach ($simplefields as $stat) { + update_if_changed($stat, $client[$stat], $_POST[$stat]); + } + + if ($vpnid) { + $client['vpnid'] = $vpnid; + } else { + $client['vpnid'] = openvpn_vpnid_next(); + } + + if ($_POST['disable'] == "yes") { + $client['disable'] = true; + } + $client['protocol'] = $pconfig['protocol']; + $client['dev_mode'] = $pconfig['dev_mode']; + list($client['interface'], $client['ipaddr']) = explode ("|", $pconfig['interface']); + $client['local_port'] = $pconfig['local_port']; + $client['server_addr'] = $pconfig['server_addr']; + $client['server_port'] = $pconfig['server_port']; + $client['resolve_retry'] = $pconfig['resolve_retry']; + $client['proxy_addr'] = $pconfig['proxy_addr']; + $client['proxy_port'] = $pconfig['proxy_port']; + $client['proxy_authtype'] = $pconfig['proxy_authtype']; + $client['proxy_user'] = $pconfig['proxy_user']; + $client['proxy_passwd'] = $pconfig['proxy_passwd']; + $client['description'] = $pconfig['description']; + $client['mode'] = $pconfig['mode']; + $client['custom_options'] = str_replace("\r\n", "\n", $pconfig['custom_options']); + + if ($tls_mode) { + $client['caref'] = $pconfig['caref']; + $client['certref'] = $pconfig['certref']; + if ($pconfig['tlsauth_enable']) { + if ($pconfig['autotls_enable']) { + $pconfig['tls'] = openvpn_create_key(); + } + $client['tls'] = base64_encode($pconfig['tls']); + } + } else { + $client['shared_key'] = base64_encode($pconfig['shared_key']); + } + $client['crypto'] = $pconfig['crypto']; + $client['digest'] = $pconfig['digest']; + $client['engine'] = $pconfig['engine']; + + $client['tunnel_network'] = $pconfig['tunnel_network']; + $client['tunnel_networkv6'] = $pconfig['tunnel_networkv6']; + $client['remote_network'] = $pconfig['remote_network']; + $client['remote_networkv6'] = $pconfig['remote_networkv6']; + $client['use_shaper'] = $pconfig['use_shaper']; + $client['compression'] = $pconfig['compression']; + $client['passtos'] = $pconfig['passtos']; + + $client['no_tun_ipv6'] = $pconfig['no_tun_ipv6']; + $client['route_no_pull'] = $pconfig['route_no_pull']; + $client['route_no_exec'] = $pconfig['route_no_exec']; + $client['verbosity_level'] = $pconfig['verbosity_level']; + + if (isset($id) && $a_client[$id]) { + $a_client[$id] = $client; + } else { + $a_client[] = $client; + } + + openvpn_resync('client', $client); + write_config(); + + header("Location: vpn_openvpn_client.php"); + exit; + } +} + +include("head.inc"); + +function build_if_list() { + $list = array(); + + $interfaces = get_configured_interface_with_descr(); + $carplist = get_configured_carp_interface_list(); + + foreach ($carplist as $cif => $carpip) + $interfaces[$cif.'|'.$carpip] = $carpip." (".get_vip_descr($carpip).")"; + + $aliaslist = get_configured_ip_aliases_list(); + + foreach ($aliaslist as $aliasip => $aliasif) + $interfaces[$aliasif.'|'.$aliasip] = $aliasip." (".get_vip_descr($aliasip).")"; + + $grouplist = return_gateway_groups_array(); + + foreach ($grouplist as $name => $group) { + if($group['ipprotocol'] != inet) + continue; + + if($group[0]['vip'] != "") + $vipif = $group[0]['vip']; + else + $vipif = $group[0]['int']; + + $interfaces[$name] = "GW Group {$name}"; + } + + $interfaces['lo0'] = "Localhost"; + $interfaces['any'] = "any"; + + foreach ($interfaces as $iface => $ifacename) + $list[$iface] = $ifacename; + + return($list); +} + +function build_cert_list() { + global $a_cert; + + $list = array(); + + foreach ($a_cert as $cert) { + $caname = ""; + $inuse = ""; + $revoked = ""; + $ca = lookup_ca($cert['caref']); + + if ($ca) + $caname = " (CA: {$ca['descr']})"; + + if ($pconfig['certref'] == $cert['refid']) + $selected = "selected=\"selected\""; + + if (cert_in_use($cert['refid'])) + $inuse = " *In Use"; + + if (is_cert_revoked($cert)) + $revoked = " *Revoked"; + + $list[$cert['refid']] = $cert['descr'] . $caname . $inuse . $revoked; + } + + return($list); +} + +if (!$savemsg) + $savemsg = ""; + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +$tab_array = array(); +$tab_array[] = array(gettext("Server"), false, "vpn_openvpn_server.php"); +$tab_array[] = array(gettext("Client"), true, "vpn_openvpn_client.php"); +$tab_array[] = array(gettext("Client Specific Overrides"), false, "vpn_openvpn_csc.php"); +$tab_array[] = array(gettext("Wizards"), false, "wizard.php?xml=openvpn_wizard.xml"); +add_package_tabs("OpenVPN", $tab_array); +display_top_tabs($tab_array); + +if($act=="new" || $act=="edit") : + require('classes/Form.class.php'); + + $form = new Form(); + + $section = new Form_Section('General Information'); + + $section->addInput(new Form_checkbox( + 'disable', + 'Disabled', + 'Disable this server', + $pconfig['disable'] + ))->setHelp('Set this option to disable this client without removing it from the list'); + + $section->addInput(new Form_Select( + 'mode', + 'Server mode', + $pconfig['mode'], + $openvpn_client_modes + )); + + $section->addInput(new Form_Select( + 'protocol', + 'Protocol', + $pconfig['protocol'], + $openvpn_prots + )); + + $section->addInput(new Form_Select( + 'dev_mode', + 'Device mode', + empty($pconfig['dev_mode']) ? 'tun':$pconfig['dev_mode'], + array_combine($openvpn_dev_mode, $openvpn_dev_mode) + )); + + $section->addInput(new Form_Select( + 'interface', + 'Interface', + $pconfig['interface'], + build_if_list() + )); + + $section->addInput(new Form_Input( + 'local_port', + 'Local port', + 'number', + $pconfig['local_port'] + ))->setHelp('Set this option if you would like to bind to a specific port. Leave this blank or enter 0 for a random dynamic port.'); + + $section->addInput(new Form_Input( + 'sever_addr', + 'Server host or address', + 'text', + $pconfig['sever_addr'] + )); + + $section->addInput(new Form_Input( + 'server_port', + 'Server port', + 'number', + $pconfig['server_port'] + )); + + $section->addInput(new Form_Input( + 'proxy_addr', + 'Proxy host or address', + 'text', + $pconfig['proxy_addr'] + )); + + $section->addInput(new Form_Select( + 'proxy_authtype', + 'Proxy Auth. - Extra options', + $pconfig['proxy_authtype'], + array('none' => 'none', 'basic' => 'basic', 'ntlm' => 'ntlm') + )); + + $section->addInput(new Form_Input( + 'proxy_user', + 'Username', + 'text', + $pconfig['proxy_user'] + )); + + $section->addInput(new Form_Input( + 'proxy_passwd', + 'Password', + 'password', + $pconfig['proxy_passwd'] + )); + + $section->addInput(new Form_checkbox( + 'resolve_retry', + 'Server hostname resolution', + 'Infinitely resolve server ', + $pconfig['resolve_retry'] + ))->setHelp('Continuously attempt to resolve the server host name. ' . + 'Useful when communicating with a server that is not permanently connected to the Internet.'); + + $section->addInput(new Form_Input( + 'description', + 'Description', + 'text', + $pconfig['description'] + ))->setHelp('You may enter a description here for your reference (not parsed).'); + + $form->add($section); + $section = new Form_Section('User Authentication settings'); + $section->addClass('authentication'); + + $section->addInput(new Form_Input( + 'auth_user', + 'Username', + 'text', + $pconfig['auth_user'] + ))->setHelp('Leave empty when no user name is needed'); + + $section->addInput(new Form_Input( + 'auth_passwd', + 'Password', + 'password', + $pconfig['auth_passwd'] + ))->setHelp('Leave empty when no password is needed'); + + $form->add($section); + + $section = new Form_Section('Cryptographic settings'); + + $section->addInput(new Form_checkbox( + 'tlsauth_enable', + 'TLS authentication', + 'Enable authentication of TLS packets.', + $pconfig['tlsauth_enable'] + )); + + if (!$pconfig['tls']) { + $section->addInput(new Form_checkbox( + 'autotls_enable', + null, + 'Automatically generate a shared TLS authentication key.', + $pconfig['autotls_enable'] + )); + } + + $section->addInput(new Form_TextArea( + 'tls', + 'Key', + $pconfig['tls'] + ))->setHelp('Paste your shared key here'); + + $section->addInput(new Form_Select( + 'caref', + 'Peer Certifiacte Authority', + $pconfig['caref'], + count($a_ca) ? array_combine($a_ca, $a_ca) : ['' => 'None'] + ))->setHelp(count($a_ca) ? '':sprintf('No Certificate Authorities defined. You may create one here: %s', '<a href="system_camanager.php">System > Cert Manager</a>')); + + $section->addInput(new Form_Select( + 'certref', + 'Peer Certifiacte Authority', + $pconfig['certref'], + build_cert_list() + ))->setHelp(count($a_cert) ? '':sprintf('No Certificates defined. You may create one here: %s', '<a href="system_camanager.php">System > Cert Manager</a>')); + + if (!$pconfig['shared_key']) { + $section->addInput(new Form_checkbox( + 'autokey_enable', + 'Auto generate', + 'Automatically generate a shared key', + $pconfig['autokey_enable'] + )); + } + + $section->addInput(new Form_TextArea( + 'shared_key', + 'Shared Key', + $pconfig['shared_key'] + ))->setHelp('Paste your shared key here'); + + $section->addInput(new Form_Select( + 'crypto', + 'Encryption Algorithm', + $pconfig['crypto'], + openvpn_get_cipherlist() + )); + + $section->addInput(new Form_Select( + 'digest', + 'Auth digest algorithm', + $pconfig['digest'], + openvpn_get_digestlist() + ))->setHelp('Leave this set to SHA1 unless all clients are set to match. SHA1 is the default for OpenVPN. '); + + $section->addInput(new Form_Select( + 'engine', + 'Hardware Crypto', + $pconfig['engine'], + openvpn_get_engines() + )); + + $form->add($section); + + $section = new Form_Section('Tunnel settings'); + + $section->addInput(new Form_Input( + 'tunnel_network', + 'IPv4 Tunnel Network', + 'text', + $pconfig['tunnel_network'] + ))->setHelp('This is the IPv4 virtual network used for private communications between this client and the sercer ' . + 'expressed using CIDR (eg. 10.0.8.0/24). The first network address will be assigned to ' . + 'the client virtual interface.'); + + $section->addInput(new Form_Input( + 'tunnel_networkv6', + 'IPv6 Tunnel Network', + 'text', + $pconfig['tunnel_networkv6'] + ))->setHelp('This is the IPv6 virtual network used for private ' . + 'communications between this client and the server expressed using CIDR (eg. fe80::/64). ' . + 'The first network address will be assigned to the server virtual interface.'); + + $section->addInput(new Form_Input( + 'remote_network', + 'IPv4 Remote network(s)', + 'text', + $pconfig['remote_network'] + ))->setHelp('IPv4 networks that will be routed through the tunnel, so that a site-to-site VPN can be established without manually ' . + 'changing the routing tables. Expressed as a comma-separated list of one or more CIDR ranges. ' . + 'If this is a site-to-site VPN, enter the remote LAN/s here. You may leave this blank if you don\'t want a site-to-site VPN.'); + + $section->addInput(new Form_Input( + 'remote_networkv6', + 'IPv6 Remote network(s)', + 'text', + $pconfig['remote_networkv6'] + ))->setHelp('These are the IPv6 networks that will be routed through the tunnel, so that a site-to-site VPN can be established without manually ' . + 'changing the routing tables. Expressed as a comma-separated list of one or more IP/PREFIX. ' . + 'If this is a site-to-site VPN, enter the remote LAN/s here. You may leave this blank if you don\'t want a site-to-site VPN.'); + +$section->addInput(new Form_Input( + 'use_shaper', + 'Limit outgoing bandwidth', + 'number', + $pconfig['use_shaper'], + ['min' => 100, 'max' => 100000000, 'placeholder' => 'Between 100 and 100,000,000 bytes/sec'] + ))->setHelp('Maximum outgoing bandwidth for this tunnel. Leave empty for no limit. The input value has to be something between 100 bytes/sec and 100 Mbytes/sec (entered as bytes per second).'); + + $section->addInput(new Form_Select( + 'compression', + 'Compression', + $pconfig['compression'], + $openvpn_compression_modes + ))->setHelp('Compress tunnel packets using the LZO algorithm. Adaptive compression will dynamically disable compression for a period of time if OpenVPN detects that the data in the packets is not being compressed efficiently.'); + + $section->addInput(new Form_checkbox( + 'passtos', + 'Type-of-Service', + 'Set the TOS IP header value of tunnel packets to match the encapsulated packet value.', + $pconfig['passtos'] + )); + + $section->addInput(new Form_checkbox( + 'no_tun_ipv6', + 'Disable IPv6', + 'Don\'t forward IPv6 traffic. ', + $pconfig['no_tun_ipv6'] + )); + + $section->addInput(new Form_checkbox( + 'route_no_pull', + 'Don\'t pull routes', + 'Bars the server from adding routes to the client\'s routing table', + $pconfig['route_no_pull'] + ))->setHelp('This option still allows the server to set the TCP/IP properties of the client\'s TUN/TAP interface. '); + + $section->addInput(new Form_checkbox( + 'route_no_exec', + 'Don\'t add/remove routes', + 'Don\'t add or remove routes automatically', + $pconfig['route_no_exec'] + ))->setHelp('Pass routes to --route-upscript using environmental variables'); + + $form->add($section); + + $section = new Form_Section('Advanced Configuration'); + $section->addClass('advanced'); + + $section->addInput(new Form_TextArea( + 'custom_options', + 'Custom options', + $pconfig['custom_options'] + ))->setHelp('Enter any additional options you would like to add to the OpenVPN server configuration here, separated by semicolon' . '<br />' . + 'EXAMPLE: push "route 10.0.0.0 255.255.255.0"'); + + $section->addInput(new Form_Select( + 'verbosity_level', + 'Verbosity level', + $pconfig['verbosity_level'], + $openvpn_verbosity_level + ))->setHelp('Each level shows all info from the previous levels. Level 3 is recommended if you want a good summary of what\'s happening without being swamped by output' . '<br /><br />' . + 'None: Only fatal errors' . '<br />' . + 'Default: Normal usage range' . '<br />' . + '5: Output R and W characters to the console for each packet read and write, uppercase is used for TCP/UDP packets and lowercase is used for TUN/TAP packets' .'<br />' . + '6: Debug info range'); + + $section->addInput(new Form_Input( + 'act', + null, + 'hidden', + $act + )); + + if (isset($id) && $a_server[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); + } + + $form->add($section); + print($form); +else: +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('OpenVPN Clients')?></h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Protocol")?></th> + <th><?=gettext("Server")?></th> + <th><?=gettext("Description")?></th> + <th><!-- Buttons --></th> + </tr> + </thead> + + <tbody> +<?php + $i = 0; + foreach($a_client as $client): + $server = "{$client['server_addr']}:{$client['server_port']}"; +?> + <tr <?=isset($server['disable']) ? 'class="disabled"':''?>> + <td> + <?=htmlspecialchars($client['protocol'])?> + </td> + <td> + <?=htmlspecialchars($server)?> + </td> + <td> + <?=htmlspecialchars($client['description'])?> + </td> + <td> + <a href="vpn_openvpn_client.php?act=edit&id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext("Edit")?></a> + <a href="vpn_openvpn_client.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger"><?=gettext("Delete")?></a> + </td> + </tr> +<?php + $i++; + endforeach; +?> + </tbody> + </table> + </div> +</div> + +<nav class="action-buttons"> + <a href="vpn_openvpn_client.php?act=new" class="btn btn-sm btn-success"> + <?=gettext("Add server")?> + </a> +</nav> + +<?php +endif; + +// Note: +// The following *_change() functions were converted from Javascript/DOM to JQuery but otherwise +// mostly left unchanged. The logic on this form is complex andthis works! +?> + +<script type="text/javascript"> +//<![CDATA[ +events.push(function(){ + + function mode_change() { + value = $('#mode').val(); + + switch(value) { + case "p2p_tls": + hideInput('tls', false); + hideCheckbox('tlsauth_enable', false); + hideCheckbox('autotls_enable', false); + hideInput('caref', false); + hideInput('certreft', false); + hideClass('authentication', false); + hideCheckbox('autokey_enable', true); + hideInput('shared_key', true); + break; + case "p2p_shared_key": + hideInput('tls', true); + hideCheckbox('tlsauth_enable', true); + hideCheckbox('autotls_enable', true); + hideInput('caref', true); + hideInput('certreft', true); + hideClass('authentication', true); + hideCheckbox('autokey_enable', false); + hideInput('shared_key', false); + break; + } + } + + function dev_mode_change() { + hideCheckbox('no_tun_ipv6', ($('#dev_mode').val() == 'tap')); + } + + function autokey_change() { + hideInput('shared_key', $('#autokey_enable').prop('checked')); + } + + function useproxy_changed() { + hideInput('proxy_user', ($('#proxy_authtype').val() == 'none')); + hideInput('proxy_passwd', ($('#proxy_authtype').val() == 'none')); + } + + function tlsauth_change() { + var hide = ! $('#tlsauth_enable').prop('checked') + + <?php if (!$pconfig['tls']): ?> + hideCheckbox('autotls_enable', hide); + <?php endif; ?> + + autotls_change(); + } + + function autotls_change() { + + <?php if (!$pconfig['tls']): ?> + autocheck = $('#autotls_enable').prop('checked'); + <?php else: ?> + autocheck = false; + <?php endif; ?> + + if ($('#tlsauth_enable').prop('checked') && !autocheck) + hideInput('tls', false); + else + hideInput('tls', true); + } + + // ---------- Library of show/hide functions ---------------------------------------------------------------------- + + // Hides the <div> in which the specified input element lives so that the input, + // its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified checkbox lives so that the checkbox, + // its label and help text are hidden + function hideCheckbox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // ---------- Monitor elements for change and call the appropriate display functions ------------------------------ + + // TLS Authorization + $('#tlsauth_enable').click(function () { + tlsauth_change(); + }); + + // Auto key + $('#autokey_enable').click(function () { + autokey_change(); + }); + + // Mode + $('#mode').click(function () { + mode_change(); + }); + + // Use proxy + $('#proxy_authtype').click(function () { + useproxy_changed(); + }); + + // Tun/tap + $('#dev_mode').click(function () { + dev_mode_change(); + }); + + // ---------- Set initial page display state ---------------------------------------------------------------------- + mode_change(); + autokey_change(); + tlsauth_change(); + useproxy_changed(); + dev_mode_change(); +}); +//]]> +</script> + +<?php include("foot.inc"); diff --git a/src/usr/local/www/vpn_openvpn_csc.php b/src/usr/local/www/vpn_openvpn_csc.php new file mode 100644 index 0000000..d3ffd22 --- /dev/null +++ b/src/usr/local/www/vpn_openvpn_csc.php @@ -0,0 +1,682 @@ +<?php +/* + vpn_openvpn_csc.php + + Copyright (C) 2008 Shrew Soft Inc. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-openvpn-csc +##|*NAME=OpenVPN: Client Specific Override page +##|*DESCR=Allow access to the 'OpenVPN: Client Specific Override' page. +##|*MATCH=vpn_openvpn_csc.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("openvpn.inc"); +require_once("pkg-utils.inc"); + +$pgtitle = array(gettext("OpenVPN"), gettext("Client Specific Override")); +$shortcut_section = "openvpn"; + +if (!is_array($config['openvpn']['openvpn-csc'])) { + $config['openvpn']['openvpn-csc'] = array(); +} + +$a_csc = &$config['openvpn']['openvpn-csc']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +$act = $_GET['act']; +if (isset($_POST['act'])) { + $act = $_POST['act']; +} + +if ($_GET['act'] == "del") { + if (!$a_csc[$id]) { + pfSenseHeader("vpn_openvpn_csc.php"); + exit; + } + + openvpn_delete_csc($a_csc[$id]); + unset($a_csc[$id]); + write_config(); + $savemsg = gettext("Client Specific Override successfully deleted")."<br />"; +} + +if ($_GET['act'] == "edit") { + + if (isset($id) && $a_csc[$id]) { + $pconfig['custom_options'] = $a_csc[$id]['custom_options']; + $pconfig['disable'] = isset($a_csc[$id]['disable']); + $pconfig['common_name'] = $a_csc[$id]['common_name']; + $pconfig['block'] = $a_csc[$id]['block']; + $pconfig['description'] = $a_csc[$id]['description']; + + $pconfig['tunnel_network'] = $a_csc[$id]['tunnel_network']; + $pconfig['local_network'] = $a_csc[$id]['local_network']; + $pconfig['local_networkv6'] = $a_csc[$id]['local_networkv6']; + $pconfig['remote_network'] = $a_csc[$id]['remote_network']; + $pconfig['remote_networkv6'] = $a_csc[$id]['remote_networkv6']; + $pconfig['gwredir'] = $a_csc[$id]['gwredir']; + + $pconfig['push_reset'] = $a_csc[$id]['push_reset']; + + $pconfig['dns_domain'] = $a_csc[$id]['dns_domain']; + if ($pconfig['dns_domain']) { + $pconfig['dns_domain_enable'] = true; + } + + $pconfig['dns_server1'] = $a_csc[$id]['dns_server1']; + $pconfig['dns_server2'] = $a_csc[$id]['dns_server2']; + $pconfig['dns_server3'] = $a_csc[$id]['dns_server3']; + $pconfig['dns_server4'] = $a_csc[$id]['dns_server4']; + + if ($pconfig['dns_server1'] || + $pconfig['dns_server2'] || + $pconfig['dns_server3'] || + $pconfig['dns_server4']) { + $pconfig['dns_server_enable'] = true; + } + + $pconfig['ntp_server1'] = $a_csc[$id]['ntp_server1']; + $pconfig['ntp_server2'] = $a_csc[$id]['ntp_server2']; + + if ($pconfig['ntp_server1'] || + $pconfig['ntp_server2']) { + $pconfig['ntp_server_enable'] = true; + } + + $pconfig['netbios_enable'] = $a_csc[$id]['netbios_enable']; + $pconfig['netbios_ntype'] = $a_csc[$id]['netbios_ntype']; + $pconfig['netbios_scope'] = $a_csc[$id]['netbios_scope']; + + $pconfig['wins_server1'] = $a_csc[$id]['wins_server1']; + $pconfig['wins_server2'] = $a_csc[$id]['wins_server2']; + + if ($pconfig['wins_server1'] || + $pconfig['wins_server2']) { + $pconfig['wins_server_enable'] = true; + } + + $pconfig['nbdd_server1'] = $a_csc[$id]['nbdd_server1']; + if ($pconfig['nbdd_server1']) { + $pconfig['nbdd_server_enable'] = true; + } + } +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if ($result = openvpn_validate_cidr($pconfig['tunnel_network'], 'Tunnel network')) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_cidr($pconfig['local_network'], 'IPv4 Local Network', true, "ipv4")) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_cidr($pconfig['local_networkv6'], 'IPv6 Local Network', true, "ipv6")) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_cidr($pconfig['remote_network'], 'IPv4 Remote Network', true, "ipv4")) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_cidr($pconfig['remote_networkv6'], 'IPv6 Remote Network', true, "ipv6")) { + $input_errors[] = $result; + } + + if ($pconfig['dns_server_enable']) { + if (!empty($pconfig['dns_server1']) && !is_ipaddr(trim($pconfig['dns_server1']))) { + $input_errors[] = gettext("The field 'DNS Server #1' must contain a valid IP address"); + } + if (!empty($pconfig['dns_server2']) && !is_ipaddr(trim($pconfig['dns_server2']))) { + $input_errors[] = gettext("The field 'DNS Server #2' must contain a valid IP address"); + } + if (!empty($pconfig['dns_server3']) && !is_ipaddr(trim($pconfig['dns_server3']))) { + $input_errors[] = gettext("The field 'DNS Server #3' must contain a valid IP address"); + } + if (!empty($pconfig['dns_server4']) && !is_ipaddr(trim($pconfig['dns_server4']))) { + $input_errors[] = gettext("The field 'DNS Server #4' must contain a valid IP address"); + } + } + + if ($pconfig['ntp_server_enable']) { + if (!empty($pconfig['ntp_server1']) && !is_ipaddr(trim($pconfig['ntp_server1']))) { + $input_errors[] = gettext("The field 'NTP Server #1' must contain a valid IP address"); + } + if (!empty($pconfig['ntp_server2']) && !is_ipaddr(trim($pconfig['ntp_server2']))) { + $input_errors[] = gettext("The field 'NTP Server #2' must contain a valid IP address"); + } + if (!empty($pconfig['ntp_server3']) && !is_ipaddr(trim($pconfig['ntp_server3']))) { + $input_errors[] = gettext("The field 'NTP Server #3' must contain a valid IP address"); + } + if (!empty($pconfig['ntp_server4']) && !is_ipaddr(trim($pconfig['ntp_server4']))) { + $input_errors[] = gettext("The field 'NTP Server #4' must contain a valid IP address"); + } + } + + if ($pconfig['netbios_enable']) { + if ($pconfig['wins_server_enable']) { + if (!empty($pconfig['wins_server1']) && !is_ipaddr(trim($pconfig['wins_server1']))) { + $input_errors[] = gettext("The field 'WINS Server #1' must contain a valid IP address"); + } + if (!empty($pconfig['wins_server2']) && !is_ipaddr(trim($pconfig['wins_server2']))) { + $input_errors[] = gettext("The field 'WINS Server #2' must contain a valid IP address"); + } + } + if ($pconfig['nbdd_server_enable']) { + if (!empty($pconfig['nbdd_server1']) && !is_ipaddr(trim($pconfig['nbdd_server1']))) { + $input_errors[] = gettext("The field 'NetBIOS Data Distribution Server #1' must contain a valid IP address"); + } + } + } + + $reqdfields[] = 'common_name'; + $reqdfieldsn[] = 'Common name'; + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (!$input_errors) { + $csc = array(); + + $csc['custom_options'] = $pconfig['custom_options']; + if ($_POST['disable'] == "yes") { + $csc['disable'] = true; + } + $csc['common_name'] = $pconfig['common_name']; + $csc['block'] = $pconfig['block']; + $csc['description'] = $pconfig['description']; + $csc['tunnel_network'] = $pconfig['tunnel_network']; + $csc['local_network'] = $pconfig['local_network']; + $csc['local_networkv6'] = $pconfig['local_networkv6']; + $csc['remote_network'] = $pconfig['remote_network']; + $csc['remote_networkv6'] = $pconfig['remote_networkv6']; + $csc['gwredir'] = $pconfig['gwredir']; + $csc['push_reset'] = $pconfig['push_reset']; + + if ($pconfig['dns_domain_enable']) { + $csc['dns_domain'] = $pconfig['dns_domain']; + } + + if ($pconfig['dns_server_enable']) { + $csc['dns_server1'] = $pconfig['dns_server1']; + $csc['dns_server2'] = $pconfig['dns_server2']; + $csc['dns_server3'] = $pconfig['dns_server3']; + $csc['dns_server4'] = $pconfig['dns_server4']; + } + + if ($pconfig['ntp_server_enable']) { + $csc['ntp_server1'] = $pconfig['ntp_server1']; + $csc['ntp_server2'] = $pconfig['ntp_server2']; + } + + $csc['netbios_enable'] = $pconfig['netbios_enable']; + $csc['netbios_ntype'] = $pconfig['netbios_ntype']; + $csc['netbios_scope'] = $pconfig['netbios_scope']; + + if ($pconfig['netbios_enable']) { + if ($pconfig['wins_server_enable']) { + $csc['wins_server1'] = $pconfig['wins_server1']; + $csc['wins_server2'] = $pconfig['wins_server2']; + } + + if ($pconfig['dns_server_enable']) { + $csc['nbdd_server1'] = $pconfig['nbdd_server1']; + } + } + + if (isset($id) && $a_csc[$id]) { + $old_csc_cn = $a_csc[$id]['common_name']; + $a_csc[$id] = $csc; + } else { + $a_csc[] = $csc; + } + + if (!empty($old_csc_cn)) { + openvpn_cleanup_csc($old_csc_cn); + } + openvpn_resync_csc($csc); + write_config(); + + header("Location: vpn_openvpn_csc.php"); + exit; + } +} + +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +$tab_array = array(); +$tab_array[] = array(gettext("Server"), false, "vpn_openvpn_server.php"); +$tab_array[] = array(gettext("Client"), false, "vpn_openvpn_client.php"); +$tab_array[] = array(gettext("Client Specific Overrides"), true, "vpn_openvpn_csc.php"); +$tab_array[] = array(gettext("Wizards"), false, "wizard.php?xml=openvpn_wizard.xml"); +add_package_tabs("OpenVPN", $tab_array); +display_top_tabs($tab_array); + +if($act=="new" || $act=="edit"): + require('classes/Form.class.php'); + + $form = new Form(); + + $section = new Form_Section('General Information'); + + $section->addInput(new Form_Checkbox( + 'disable', + 'Disable', + 'Disable this override', + $pconfig['disable'] + ))->setHelp('Set this option to disable this client-specific override without removing it from the list.'); + + $section->addInput(new Form_Input( + 'common_name', + 'Common name', + 'text', + $pconfig['common_name'] + ))->setHelp('Enter the client\'s X.509 common name.'); + + $section->addInput(new Form_Input( + 'description', + 'Description', + 'text', + $pconfig['description'] + ))->setHelp('You may enter a description here for your reference (not parsed). '); + + $section->addInput(new Form_Checkbox( + 'block', + 'Connection blocking', + 'Block this client connection based on its common name. ', + $pconfig['block'] + ))->setHelp('Don\'t use this option to permanently disable a client due to a compromised key or password. Use a CRL (certificate revocation list) instead. '); + + $form->add($section); + + $section = new Form_Section('Tunnel settings'); + + $section->addInput(new Form_Input( + 'tunnel_network', + 'Tunnel Network', + 'text', + $pconfig['tunnel_network'] + ))->setHelp('This is the virtual network used for private communications between this client and the server expressed using CIDR (eg. 10.0.8.0/24). ' . + 'The first network address is assumed to be the server address and the second network address will be assigned to the client virtual interface. '); + + $section->addInput(new Form_Input( + 'local_network', + 'IPv4 Local Network/s', + 'text', + $pconfig['local_network'] + ))->setHelp('These are the IPv4 networks that will be accessible from this particular client. Expressed as a comma-separated list of one or more CIDR ranges. ' . '<br />' . + 'NOTE: You do not need to specify networks here if they have already been defined on the main server configuration.'); + + $section->addInput(new Form_Input( + 'local_networkv6', + 'IPv6 Local Network/s', + 'text', + $pconfig['local_networkv6'] + ))->setHelp('These are the IPv4 networks that will be accessible from this particular client. Expressed as a comma-separated list of one or more IP/PREFIX networks.' . '<br />' . + 'NOTE: You do not need to specify networks here if they have already been defined on the main server configuration.'); + + $section->addInput(new Form_Input( + 'remote_network', + 'IPv4 Remote Network/s', + 'text', + $pconfig['remote_network'] + ))->setHelp('These are the IPv4 networks that will be routed to this client specifically using iroute, so that a site-to-site VPN can be established. ' . + 'Expressed as a comma-separated list of one or more CIDR ranges. You may leave this blank if there are no client-side networks to be routed.' . '<br />' . + 'NOTE: Remember to add these subnets to the IPv4 Remote Networks list on the corresponding OpenVPN server settings.'); + + $section->addInput(new Form_Input( + 'remote_networkv6', + 'IPv6 Remote Network/s', + 'text', + $pconfig['remote_networkv6'] + ))->setHelp('These are the IPv4 networks that will be routed to this client specifically using iroute, so that a site-to-site VPN can be established. ' . + 'Expressed as a comma-separated list of one or more IP/PREFIX networks. You may leave this blank if there are no client-side networks to be routed.' . '<br />' . + 'NOTE: Remember to add these subnets to the IPv6 Remote Networks list on the corresponding OpenVPN server settings.'); + + $section->addInput(new Form_Checkbox( + 'gwredir', + 'Redirect Gateway', + 'Force all client generated traffic through the tunnel.', + $pconfig['gwredir'] + )); + + $form->add($section); + + $section = new Form_Section('Client settings'); + + // Default domain name + $section->addInput(new Form_Checkbox( + 'push_reset', + 'Server Definitions', + 'Prevent this client from receiving any server-defined client settings. ', + $pconfig['push_reset'] + )); + + $section->addInput(new Form_Checkbox( + 'dns_domain_enable', + 'DNS Default Domain', + 'Provide a default domain name to clients', + $pconfig['dns_domain_enable'] + ))->toggles('.dnsdomain'); + + $group = new Form_Group('DNS Domain'); + $group->addClass('dnsdomain'); + + $group->add(new Form_Input( + 'dns_domain', + 'DNS Domain', + 'text', + $pconfig['dns_domain'] + )); + + $section->add($group); + + // DNS servers + $section->addInput(new Form_Checkbox( + 'dns_server_enable', + 'DNS Servers', + 'Provide a DNS server list to clients', + $pconfig['dns_server_enable'] + ))->toggles('.dnsservers'); + + $group = new Form_Group(null); + $group->addClass('dnsservers'); + + $group->add(new Form_Input( + 'dns_server1', + null, + 'text', + $pconfig['dns_server1'] + ))->setHelp('Server 1'); + + $group->add(new Form_Input( + 'dns_server2', + null, + 'text', + $pconfig['dns_server2'] + ))->setHelp('Server 2'); + + $group->add(new Form_Input( + 'dns_server3', + null, + 'text', + $pconfig['dns_server3'] + ))->setHelp('Server 3'); + + $group->add(new Form_Input( + 'dns_server4', + null, + 'text', + $pconfig['dns_server4'] + ))->setHelp('Server 4'); + + $section->add($group); + + // NTP servers + $section->addInput(new Form_Checkbox( + 'ntp_server_enable', + 'NTP Servers', + 'Provide an NTP server list to clients', + $pconfig['ntp_server_enable'] + ))->toggles('.ntpservers'); + + $group = new Form_Group(null); + $group->addClass('ntpservers'); + + $group->add(new Form_Input( + 'ntp_server1', + null, + 'text', + $pconfig['ntp_server1'] + ))->setHelp('Server 1'); + + $group->add(new Form_Input( + 'ntp_server2', + null, + 'text', + $pconfig['ntp_server2'] + ))->setHelp('Server 2'); + + $section->add($group); + + // NTP servers - For this section we need to use Javascript hiding since there + // are nested toggles + $section->addInput(new Form_Checkbox( + 'netbios_enable', + 'Netbios Option', + 'Enable Netbios over TCP/IP', + $pconfig['netbios_enable'] + ))->setHelp('If this option is not set, all NetBIOS-over-TCP/IP options (including WINS) will be disabled. '); + + $section->addInput(new Form_Select( + 'netbios_ntype', + 'Node Type', + $pconfig['netbios_ntype'], + $netbios_nodetypes + ))->setHelp('Possible options: b-node (broadcasts), p-node (point-to-point name queries to a WINS server), m-node (broadcast then query name server), ' . + 'and h-node (query name server, then broadcast). '); + + $section->addInput(new Form_Input( + 'netbios_scope', + null, + 'text', + $pconfig['netbios_scope'] + ))->setHelp('A NetBIOS Scope ID provides an extended naming service for NetBIOS over TCP/IP. ' . + 'The NetBIOS scope ID isolates NetBIOS traffic on a single network to only those nodes with the same NetBIOS scope ID. '); + + $section->addInput(new Form_Checkbox( + 'wins_server_enable', + 'WINS servers', + 'Provide a WINS server list to clients', + $pconfig['wins_server_enable'] + )); + + $group = new Form_Group(null); + + $group->add(new Form_Input( + 'wins_server1', + null, + 'text', + $pconfig['wins_server1'] + ))->setHelp('Server 1'); + + $group->add(new Form_Input( + 'wins_server2', + null, + 'text', + $pconfig['wins_server2'] + ))->setHelp('Server 2'); + + $group->addClass('winsservers'); + + $section->add($group); + + $section->addInput(new Form_TextArea( + 'custom_options', + 'Advanced', + $pconfig['custom_options'] + ))->setHelp('Enter any additional options you would like to add for this client specific override, separated by a semicolon. ' . '<br />' . + 'EXAMPLE: push "route 10.0.0.0 255.255.255.0"; '); + + // The hidden fields + $section->addInput(new Form_Input( + 'act', + null, + 'hidden', + $act + )); + + if (isset($id) && $a_csc[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); + } + + $form->add($section); + print($form); + +?> + +<script> +//<![CDATA[ +events.push(function(){ + var visible = false; + + // Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified checkbox lives so that the checkbox, its label and help text are hidden + function hideCheckbox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // Hide/show that section, but have to also respect the wins_server_enable checkbox + function setNetbios() { + if($('#netbios_enable').prop('checked')) { + hideInput('netbios_ntype', false); + hideInput('netbios_scope', false); + hideCheckbox('wins_server_enable', false); + setWins(); + } else { + hideInput('netbios_ntype', true); + hideInput('netbios_scope', true); + hideCheckbox('wins_server_enable', true); + hideClass('winsservers', true); + } + } + + function setWins() { + hideClass('winsservers', ! $('#wins_server_enable').prop('checked')); + } + + // On clicking the netbios_enable checkbox + $('#netbios_enable').click(function () { + setNetbios(); + }); + + // On clicking the wins_server_enable checkbox + $('#wins_server_enable').click(function () { + setWins(); + }); + + // On initial page load + setNetbios(); +}); +//]]> +</script> + +<?php +else : // Not an 'add' or an 'edit'. Just the table of Override CSCs +?> + +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('CSC Overrides')?></h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Disabled")?></th> + <th><?=gettext("Common Name")?></th> + <th><?=gettext("Description")?></th> + <th> <!-- Buttons --></th> + </tr> + </thead> + <tbody> +<?php + $i = 0; + foreach($a_csc as $csc): + $disabled = isset($csc['disable']) ? "Yes":"No"; +?> + <tr> + <td class="listlr"> + <?=$disabled?> + </td> + <td class="listr"> + <?=htmlspecialchars($csc['common_name'])?> + </td> + <td class="listbg"> + <?=htmlspecialchars($csc['description'])?> + </td> + <td> + <a href="vpn_openvpn_csc.php?act=edit&id=<?=$i?>" class="btn btn-info btn-xs"><?=gettext('Edit')?></a> + <a href="vpn_openvpn_csc.php?act=del&id=<?=$i?>" class="btn btn-danger btn-xs"><?=gettext('Delete')?></a> + </td> + </tr> +<?php + $i++; + endforeach; +?> + </tbody> + </table> + + <nav class="action-buttons"> + <a href="vpn_openvpn_csc.php?act=new" class="btn btn-success btn-sm"><?=gettext('Add CSC')?></a> + </nav> + + </div> +</div> + +<?php +endif; +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/vpn_openvpn_server.php b/src/usr/local/www/vpn_openvpn_server.php new file mode 100644 index 0000000..bdbd08c --- /dev/null +++ b/src/usr/local/www/vpn_openvpn_server.php @@ -0,0 +1,1618 @@ +<?php +/* + vpn_openvpn_server.php + + Copyright (C) 2008 Shrew Soft Inc. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-openvpn-server +##|*NAME=OpenVPN: Server page +##|*DESCR=Allow access to the 'OpenVPN: Server' page. +##|*MATCH=vpn_openvpn_server.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("openvpn.inc"); +require_once("pkg-utils.inc"); + +if (!is_array($config['openvpn']['openvpn-server'])) { + $config['openvpn']['openvpn-server'] = array(); +} + +$a_server = &$config['openvpn']['openvpn-server']; + +if (!is_array($config['ca'])) { + $config['ca'] = array(); +} + +$a_ca =& $config['ca']; + +if (!is_array($config['cert'])) { + $config['cert'] = array(); +} + +$a_cert =& $config['cert']; + +if (!is_array($config['crl'])) { + $config['crl'] = array(); +} + +$a_crl =& $config['crl']; + +foreach ($a_crl as $cid => $acrl) { + if (!isset($acrl['refid'])) { + unset ($a_crl[$cid]); + } +} + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; +} +if (isset($_POST['id']) && is_numericint($_POST['id'])) { + $id = $_POST['id']; +} + +$act = $_GET['act']; +if (isset($_POST['act'])) { + $act = $_POST['act']; +} + +if (isset($id) && $a_server[$id]) { + $vpnid = $a_server[$id]['vpnid']; +} else { + $vpnid = 0; +} + +if ($_GET['act'] == "del") { + + if (!isset($a_server[$id])) { + pfSenseHeader("vpn_openvpn_server.php"); + exit; + } + if (!empty($a_server[$id])) { + openvpn_delete('server', $a_server[$id]); + } + unset($a_server[$id]); + write_config(); + $savemsg = gettext("Server successfully deleted")."<br />"; +} + +if ($_GET['act'] == "new") { + $pconfig['autokey_enable'] = "yes"; + $pconfig['tlsauth_enable'] = "yes"; + $pconfig['autotls_enable'] = "yes"; + $pconfig['dh_length'] = 1024; + $pconfig['dev_mode'] = "tun"; + $pconfig['interface'] = "wan"; + $pconfig['local_port'] = openvpn_port_next('UDP'); + $pconfig['pool_enable'] = "yes"; + $pconfig['cert_depth'] = 1; + $pconfig['verbosity_level'] = 1; // Default verbosity is 1 + // OpenVPN Defaults to SHA1 + $pconfig['digest'] = "SHA1"; +} + +if ($_GET['act'] == "edit") { + + if (isset($id) && $a_server[$id]) { + $pconfig['disable'] = isset($a_server[$id]['disable']); + $pconfig['mode'] = $a_server[$id]['mode']; + $pconfig['protocol'] = $a_server[$id]['protocol']; + $pconfig['authmode'] = $a_server[$id]['authmode']; + $pconfig['dev_mode'] = $a_server[$id]['dev_mode']; + $pconfig['interface'] = $a_server[$id]['interface']; + + if (!empty($a_server[$id]['ipaddr'])) { + $pconfig['interface'] = $pconfig['interface'] . '|' . $a_server[$id]['ipaddr']; + } + + $pconfig['local_port'] = $a_server[$id]['local_port']; + $pconfig['description'] = $a_server[$id]['description']; + $pconfig['custom_options'] = $a_server[$id]['custom_options']; + + if ($pconfig['mode'] != "p2p_shared_key") { + if ($a_server[$id]['tls']) { + $pconfig['tlsauth_enable'] = "yes"; + $pconfig['tls'] = base64_decode($a_server[$id]['tls']); + } + + $pconfig['caref'] = $a_server[$id]['caref']; + $pconfig['crlref'] = $a_server[$id]['crlref']; + $pconfig['certref'] = $a_server[$id]['certref']; + $pconfig['dh_length'] = $a_server[$id]['dh_length']; + if (isset($a_server[$id]['cert_depth'])) { + $pconfig['cert_depth'] = $a_server[$id]['cert_depth']; + } else { + $pconfig['cert_depth'] = 1; + } + if ($pconfig['mode'] == "server_tls_user") { + $pconfig['strictusercn'] = $a_server[$id]['strictusercn']; + } + } else { + $pconfig['shared_key'] = base64_decode($a_server[$id]['shared_key']); + } + $pconfig['crypto'] = $a_server[$id]['crypto']; + // OpenVPN Defaults to SHA1 if unset + $pconfig['digest'] = !empty($a_server[$id]['digest']) ? $a_server[$id]['digest'] : "SHA1"; + $pconfig['engine'] = $a_server[$id]['engine']; + + $pconfig['tunnel_network'] = $a_server[$id]['tunnel_network']; + $pconfig['tunnel_networkv6'] = $a_server[$id]['tunnel_networkv6']; + + $pconfig['remote_network'] = $a_server[$id]['remote_network']; + $pconfig['remote_networkv6'] = $a_server[$id]['remote_networkv6']; + $pconfig['gwredir'] = $a_server[$id]['gwredir']; + $pconfig['local_network'] = $a_server[$id]['local_network']; + $pconfig['local_networkv6'] = $a_server[$id]['local_networkv6']; + $pconfig['maxclients'] = $a_server[$id]['maxclients']; + $pconfig['compression'] = $a_server[$id]['compression']; + $pconfig['passtos'] = $a_server[$id]['passtos']; + $pconfig['client2client'] = $a_server[$id]['client2client']; + + $pconfig['dynamic_ip'] = $a_server[$id]['dynamic_ip']; + $pconfig['pool_enable'] = $a_server[$id]['pool_enable']; + $pconfig['topology_subnet'] = $a_server[$id]['topology_subnet']; + + $pconfig['serverbridge_dhcp'] = $a_server[$id]['serverbridge_dhcp']; + $pconfig['serverbridge_interface'] = $a_server[$id]['serverbridge_interface']; + $pconfig['serverbridge_dhcp_start'] = $a_server[$id]['serverbridge_dhcp_start']; + $pconfig['serverbridge_dhcp_end'] = $a_server[$id]['serverbridge_dhcp_end']; + + $pconfig['dns_domain'] = $a_server[$id]['dns_domain']; + if ($pconfig['dns_domain']) { + $pconfig['dns_domain_enable'] = true; + } + + $pconfig['dns_server1'] = $a_server[$id]['dns_server1']; + $pconfig['dns_server2'] = $a_server[$id]['dns_server2']; + $pconfig['dns_server3'] = $a_server[$id]['dns_server3']; + $pconfig['dns_server4'] = $a_server[$id]['dns_server4']; + + if ($pconfig['dns_server1'] || + $pconfig['dns_server2'] || + $pconfig['dns_server3'] || + $pconfig['dns_server4']) { + $pconfig['dns_server_enable'] = true; + } + + $pconfig['ntp_server1'] = $a_server[$id]['ntp_server1']; + $pconfig['ntp_server2'] = $a_server[$id]['ntp_server2']; + + if ($pconfig['ntp_server1'] || + $pconfig['ntp_server2']) { + $pconfig['ntp_server_enable'] = true; + } + + $pconfig['netbios_enable'] = $a_server[$id]['netbios_enable']; + $pconfig['netbios_ntype'] = $a_server[$id]['netbios_ntype']; + $pconfig['netbios_scope'] = $a_server[$id]['netbios_scope']; + + $pconfig['wins_server1'] = $a_server[$id]['wins_server1']; + $pconfig['wins_server2'] = $a_server[$id]['wins_server2']; + + if ($pconfig['wins_server1'] || + $pconfig['wins_server2']) { + $pconfig['wins_server_enable'] = true; + } + + $pconfig['client_mgmt_port'] = $a_server[$id]['client_mgmt_port']; + if ($pconfig['client_mgmt_port']) { + $pconfig['client_mgmt_port_enable'] = true; + } + + $pconfig['nbdd_server1'] = $a_server[$id]['nbdd_server1']; + if ($pconfig['nbdd_server1']) { + $pconfig['nbdd_server_enable'] = true; + } + + // just in case the modes switch + $pconfig['autokey_enable'] = "yes"; + $pconfig['autotls_enable'] = "yes"; + + $pconfig['duplicate_cn'] = isset($a_server[$id]['duplicate_cn']); + + $pconfig['no_tun_ipv6'] = $a_server[$id]['no_tun_ipv6']; + if (isset($a_server[$id]['verbosity_level'])) { + $pconfig['verbosity_level'] = $a_server[$id]['verbosity_level']; + } else { + $pconfig['verbosity_level'] = 1; // Default verbosity is 1 + } + + $pconfig['push_register_dns'] = $a_server[$id]['push_register_dns']; + } +} +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + if (isset($id) && $a_server[$id]) { + $vpnid = $a_server[$id]['vpnid']; + } else { + $vpnid = 0; + } + + list($iv_iface, $iv_ip) = explode ("|", $pconfig['interface']); + if (is_ipaddrv4($iv_ip) && (stristr($pconfig['protocol'], "6") !== false)) { + $input_errors[] = gettext("Protocol and IP address families do not match. You cannot select an IPv6 protocol and an IPv4 IP address."); + } elseif (is_ipaddrv6($iv_ip) && (stristr($pconfig['protocol'], "6") === false)) { + $input_errors[] = gettext("Protocol and IP address families do not match. You cannot select an IPv4 protocol and an IPv6 IP address."); + } elseif ((stristr($pconfig['protocol'], "6") === false) && !get_interface_ip($iv_iface) && ($pconfig['interface'] != "any")) { + $input_errors[] = gettext("An IPv4 protocol was selected, but the selected interface has no IPv4 address."); + } elseif ((stristr($pconfig['protocol'], "6") !== false) && !get_interface_ipv6($iv_iface) && ($pconfig['interface'] != "any")) { + $input_errors[] = gettext("An IPv6 protocol was selected, but the selected interface has no IPv6 address."); + } + + if ($pconfig['mode'] != "p2p_shared_key") { + $tls_mode = true; + } else { + $tls_mode = false; + } + + if (empty($pconfig['authmode']) && (($pconfig['mode'] == "server_user") || ($pconfig['mode'] == "server_tls_user"))) { + $input_errors[] = gettext("You must select a Backend for Authentication if the server mode requires User Auth."); + } + + /* input validation */ + if ($result = openvpn_validate_port($pconfig['local_port'], 'Local port')) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_cidr($pconfig['tunnel_network'], 'IPv4 Tunnel Network', false, "ipv4")) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_cidr($pconfig['tunnel_networkv6'], 'IPv6 Tunnel Network', false, "ipv6")) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_cidr($pconfig['remote_network'], 'IPv4 Remote Network', true, "ipv4")) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_cidr($pconfig['remote_networkv6'], 'IPv6 Remote Network', true, "ipv6")) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_cidr($pconfig['local_network'], 'IPv4 Local Network', true, "ipv4")) { + $input_errors[] = $result; + } + + if ($result = openvpn_validate_cidr($pconfig['local_networkv6'], 'IPv6 Local Network', true, "ipv6")) { + $input_errors[] = $result; + } + + $portused = openvpn_port_used($pconfig['protocol'], $pconfig['interface'], $pconfig['local_port'], $vpnid); + if (($portused != $vpnid) && ($portused != 0)) { + $input_errors[] = gettext("The specified 'Local port' is in use. Please select another value"); + } + + if ($pconfig['autokey_enable']) { + $pconfig['shared_key'] = openvpn_create_key(); + } + + if (!$tls_mode && !$pconfig['autokey_enable']) { + if (!strstr($pconfig['shared_key'], "-----BEGIN OpenVPN Static key V1-----") || + !strstr($pconfig['shared_key'], "-----END OpenVPN Static key V1-----")) { + $input_errors[] = gettext("The field 'Shared Key' does not appear to be valid"); + } + } + + if ($tls_mode && $pconfig['tlsauth_enable'] && !$pconfig['autotls_enable']) { + if (!strstr($pconfig['tls'], "-----BEGIN OpenVPN Static key V1-----") || + !strstr($pconfig['tls'], "-----END OpenVPN Static key V1-----")) { + $input_errors[] = gettext("The field 'TLS Authentication Key' does not appear to be valid"); + } + } + + if ($pconfig['dns_server_enable']) { + if (!empty($pconfig['dns_server1']) && !is_ipaddr(trim($pconfig['dns_server1']))) { + $input_errors[] = gettext("The field 'DNS Server #1' must contain a valid IP address"); + } + if (!empty($pconfig['dns_server2']) && !is_ipaddr(trim($pconfig['dns_server2']))) { + $input_errors[] = gettext("The field 'DNS Server #2' must contain a valid IP address"); + } + if (!empty($pconfig['dns_server3']) && !is_ipaddr(trim($pconfig['dns_server3']))) { + $input_errors[] = gettext("The field 'DNS Server #3' must contain a valid IP address"); + } + if (!empty($pconfig['dns_server4']) && !is_ipaddr(trim($pconfig['dns_server4']))) { + $input_errors[] = gettext("The field 'DNS Server #4' must contain a valid IP address"); + } + } + + if ($pconfig['ntp_server_enable']) { + if (!empty($pconfig['ntp_server1']) && !is_ipaddr(trim($pconfig['ntp_server1']))) { + $input_errors[] = gettext("The field 'NTP Server #1' must contain a valid IP address"); + } + if (!empty($pconfig['ntp_server2']) && !is_ipaddr(trim($pconfig['ntp_server2']))) { + $input_errors[] = gettext("The field 'NTP Server #2' must contain a valid IP address"); + } + if (!empty($pconfig['ntp_server3']) && !is_ipaddr(trim($pconfig['ntp_server3']))) { + $input_errors[] = gettext("The field 'NTP Server #3' must contain a valid IP address"); + } + if (!empty($pconfig['ntp_server4']) && !is_ipaddr(trim($pconfig['ntp_server4']))) { + $input_errors[] = gettext("The field 'NTP Server #4' must contain a valid IP address"); + } + } + + if ($pconfig['netbios_enable']) { + if ($pconfig['wins_server_enable']) { + if (!empty($pconfig['wins_server1']) && !is_ipaddr(trim($pconfig['wins_server1']))) { + $input_errors[] = gettext("The field 'WINS Server #1' must contain a valid IP address"); + } + if (!empty($pconfig['wins_server2']) && !is_ipaddr(trim($pconfig['wins_server2']))) { + $input_errors[] = gettext("The field 'WINS Server #2' must contain a valid IP address"); + } + } + if ($pconfig['nbdd_server_enable']) { + if (!empty($pconfig['nbdd_server1']) && !is_ipaddr(trim($pconfig['nbdd_server1']))) { + $input_errors[] = gettext("The field 'NetBIOS Data Distribution Server #1' must contain a valid IP address"); + } + } + } + + if ($pconfig['client_mgmt_port_enable']) { + if ($result = openvpn_validate_port($pconfig['client_mgmt_port'], 'Client management port')) { + $input_errors[] = $result; + } + } + + if ($pconfig['maxclients'] && !is_numeric($pconfig['maxclients'])) { + $input_errors[] = gettext("The field 'Concurrent connections' must be numeric."); + } + + /* If we are not in shared key mode, then we need the CA/Cert. */ + if ($pconfig['mode'] != "p2p_shared_key") { + $reqdfields = explode(" ", "caref certref"); + $reqdfieldsn = array(gettext("Certificate Authority"), gettext("Certificate")); + } elseif (!$pconfig['autokey_enable']) { + /* We only need the shared key filled in if we are in shared key mode and autokey is not selected. */ + $reqdfields = array('shared_key'); + $reqdfieldsn = array(gettext('Shared key')); + } + + if ($pconfig['dev_mode'] != "tap") { + $reqdfields[] = 'tunnel_network'; + $reqdfieldsn[] = gettext('Tunnel network'); + } else { + if ($pconfig['serverbridge_dhcp'] && $pconfig['tunnel_network']) { + $input_errors[] = gettext("Using a tunnel network and server bridge settings together is not allowed."); + } + if (($pconfig['serverbridge_dhcp_start'] && !$pconfig['serverbridge_dhcp_end']) || + (!$pconfig['serverbridge_dhcp_start'] && $pconfig['serverbridge_dhcp_end'])) { + $input_errors[] = gettext("Server Bridge DHCP Start and End must both be empty, or defined."); + } + if (($pconfig['serverbridge_dhcp_start'] && !is_ipaddrv4($pconfig['serverbridge_dhcp_start']))) { + $input_errors[] = gettext("Server Bridge DHCP Start must be an IPv4 address."); + } + if (($pconfig['serverbridge_dhcp_end'] && !is_ipaddrv4($pconfig['serverbridge_dhcp_end']))) { + $input_errors[] = gettext("Server Bridge DHCP End must be an IPv4 address."); + } + if (ip2ulong($pconfig['serverbridge_dhcp_start']) > ip2ulong($pconfig['serverbridge_dhcp_end'])) { + $input_errors[] = gettext("The Server Bridge DHCP range is invalid (start higher than end)."); + } + } + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (!$input_errors) { + + $server = array(); + + if ($id && $pconfig['dev_mode'] <> $a_server[$id]['dev_mode']) { + openvpn_delete('server', $a_server[$id]);// delete(rename) old interface so a new TUN or TAP interface can be created. + } + + if ($vpnid) { + $server['vpnid'] = $vpnid; + } else { + $server['vpnid'] = openvpn_vpnid_next(); + } + + if ($_POST['disable'] == "yes") { + $server['disable'] = true; + } + $server['mode'] = $pconfig['mode']; + if (!empty($pconfig['authmode']) && (($pconfig['mode'] == "server_user") || ($pconfig['mode'] == "server_tls_user"))) { + $server['authmode'] = implode(",", $pconfig['authmode']); + } + $server['protocol'] = $pconfig['protocol']; + $server['dev_mode'] = $pconfig['dev_mode']; + list($server['interface'], $server['ipaddr']) = explode ("|", $pconfig['interface']); + $server['local_port'] = $pconfig['local_port']; + $server['description'] = $pconfig['description']; + $server['custom_options'] = str_replace("\r\n", "\n", $pconfig['custom_options']); + + if ($tls_mode) { + if ($pconfig['tlsauth_enable']) { + if ($pconfig['autotls_enable']) { + $pconfig['tls'] = openvpn_create_key(); + } + $server['tls'] = base64_encode($pconfig['tls']); + } + $server['caref'] = $pconfig['caref']; + $server['crlref'] = $pconfig['crlref']; + $server['certref'] = $pconfig['certref']; + $server['dh_length'] = $pconfig['dh_length']; + $server['cert_depth'] = $pconfig['cert_depth']; + if ($pconfig['mode'] == "server_tls_user") { + $server['strictusercn'] = $pconfig['strictusercn']; + } + } else { + $server['shared_key'] = base64_encode($pconfig['shared_key']); + } + + $server['crypto'] = $pconfig['crypto']; + $server['digest'] = $pconfig['digest']; + $server['engine'] = $pconfig['engine']; + + $server['tunnel_network'] = $pconfig['tunnel_network']; + $server['tunnel_networkv6'] = $pconfig['tunnel_networkv6']; + $server['remote_network'] = $pconfig['remote_network']; + $server['remote_networkv6'] = $pconfig['remote_networkv6']; + $server['gwredir'] = $pconfig['gwredir']; + $server['local_network'] = $pconfig['local_network']; + $server['local_networkv6'] = $pconfig['local_networkv6']; + $server['maxclients'] = $pconfig['maxclients']; + $server['compression'] = $pconfig['compression']; + $server['passtos'] = $pconfig['passtos']; + $server['client2client'] = $pconfig['client2client']; + + $server['dynamic_ip'] = $pconfig['dynamic_ip']; + $server['pool_enable'] = $pconfig['pool_enable']; + $server['topology_subnet'] = $pconfig['topology_subnet']; + + $server['serverbridge_dhcp'] = $pconfig['serverbridge_dhcp']; + $server['serverbridge_interface'] = $pconfig['serverbridge_interface']; + $server['serverbridge_dhcp_start'] = $pconfig['serverbridge_dhcp_start']; + $server['serverbridge_dhcp_end'] = $pconfig['serverbridge_dhcp_end']; + + if ($pconfig['dns_domain_enable']) { + $server['dns_domain'] = $pconfig['dns_domain']; + } + + if ($pconfig['dns_server_enable']) { + $server['dns_server1'] = $pconfig['dns_server1']; + $server['dns_server2'] = $pconfig['dns_server2']; + $server['dns_server3'] = $pconfig['dns_server3']; + $server['dns_server4'] = $pconfig['dns_server4']; + } + + if ($pconfig['push_register_dns']) { + $server['push_register_dns'] = $pconfig['push_register_dns']; + } + + if ($pconfig['ntp_server_enable']) { + $server['ntp_server1'] = $pconfig['ntp_server1']; + $server['ntp_server2'] = $pconfig['ntp_server2']; + } + + $server['netbios_enable'] = $pconfig['netbios_enable']; + $server['netbios_ntype'] = $pconfig['netbios_ntype']; + $server['netbios_scope'] = $pconfig['netbios_scope']; + + $server['no_tun_ipv6'] = $pconfig['no_tun_ipv6']; + $server['verbosity_level'] = $pconfig['verbosity_level']; + + if ($pconfig['netbios_enable']) { + + if ($pconfig['wins_server_enable']) { + $server['wins_server1'] = $pconfig['wins_server1']; + $server['wins_server2'] = $pconfig['wins_server2']; + } + + if ($pconfig['dns_server_enable']) { + $server['nbdd_server1'] = $pconfig['nbdd_server1']; + } + } + + if ($pconfig['client_mgmt_port_enable']) { + $server['client_mgmt_port'] = $pconfig['client_mgmt_port']; + } + + if ($_POST['duplicate_cn'] == "yes") { + $server['duplicate_cn'] = true; + } + + if (isset($id) && $a_server[$id]) { + $a_server[$id] = $server; + } else { + $a_server[] = $server; + } + + openvpn_resync('server', $server); + write_config(); + + header("Location: vpn_openvpn_server.php"); + exit; + } + if (!empty($pconfig['authmode'])) { + $pconfig['authmode'] = implode(",", $pconfig['authmode']); + } +} + +$pgtitle = array(gettext("OpenVPN"), gettext("Server")); +$shortcut_section = "openvpn"; + +include("head.inc"); + +function build_mode_list() { + global $openvpn_server_modes; + + $list = array(); + + foreach ($openvpn_server_modes as $name => $desc) + $list[$name] = $desc; + + return($list); +} + +function build_if_list() { + $list = array(); + + $interfaces = get_configured_interface_with_descr(); + $carplist = get_configured_carp_interface_list(); + + foreach ($carplist as $cif => $carpip) + $interfaces[$cif.'|'.$carpip] = $carpip." (".get_vip_descr($carpip).")"; + + $aliaslist = get_configured_ip_aliases_list(); + + foreach ($aliaslist as $aliasip => $aliasif) + $interfaces[$aliasif.'|'.$aliasip] = $aliasip." (".get_vip_descr($aliasip).")"; + + $grouplist = return_gateway_groups_array(); + + foreach ($grouplist as $name => $group) { + if($group['ipprotocol'] != inet) + continue; + + if($group[0]['vip'] != "") + $vipif = $group[0]['vip']; + else + $vipif = $group[0]['int']; + + $interfaces[$name] = "GW Group {$name}"; + } + + $interfaces['lo0'] = "Localhost"; + $interfaces['any'] = "any"; + + foreach ($interfaces as $iface => $ifacename) + $list[$iface] = $ifacename; + + return($list); +} + +function build_crl_list() { + global $a_crl; + + $list = array('' => 'None'); + + foreach ($a_crl as $crl) { + $caname = ""; + $ca = lookup_ca($crl['caref']); + + if ($ca) + $caname = " (CA: {$ca['descr']})"; + + $list[$crl['refid']] = $crl['descr'] . $caname; + } + + return($list); +} + +function build_cert_list() { + global $a_cert; + + $list = array(); + + foreach ($a_cert as $cert) { + $caname = ""; + $inuse = ""; + $revoked = ""; + $ca = lookup_ca($cert['caref']); + + if ($ca) + $caname = " (CA: {$ca['descr']})"; + + if ($pconfig['certref'] == $cert['refid']) + $selected = "selected=\"selected\""; + + if (cert_in_use($cert['refid'])) + $inuse = " *In Use"; + + if (is_cert_revoked($cert)) + $revoked = " *Revoked"; + + $list[$cert['refid']] = $cert['descr'] . $caname . $inuse . $revoked; + } + + return($list); +} + +function build_bridge_list() { + $list = array(); + + $serverbridge_interface['none'] = "none"; + $serverbridge_interface = array_merge($serverbridge_interface, get_configured_interface_with_descr()); + $carplist = get_configured_carp_interface_list(); + + foreach ($carplist as $cif => $carpip) + $serverbridge_interface[$cif.'|'.$carpip] = $carpip." (".get_vip_descr($carpip).")"; + + $aliaslist = get_configured_ip_aliases_list(); + + foreach ($aliaslist as $aliasip => $aliasif) + $serverbridge_interface[$aliasif.'|'.$aliasip] = $aliasip." (".get_vip_descr($aliasip).")"; + + foreach ($serverbridge_interface as $iface => $ifacename) + $list[$iface] = htmlspecialchars($ifacename); + + return($list); +} + +if (!$savemsg) + $savemsg = ""; + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box_np($savemsg, 'success'); + +$tab_array = array(); +$tab_array[] = array(gettext("Server"), true, "vpn_openvpn_server.php"); +$tab_array[] = array(gettext("Client"), false, "vpn_openvpn_client.php"); +$tab_array[] = array(gettext("Client Specific Overrides"), false, "vpn_openvpn_csc.php"); +$tab_array[] = array(gettext("Wizards"), false, "wizard.php?xml=openvpn_wizard.xml"); +add_package_tabs("OpenVPN", $tab_array); +display_top_tabs($tab_array); + +require('classes/Form.class.php'); + +$form = new Form(); + +if($act=="new" || $act=="edit") : + $section = new Form_Section('General Information'); + + $section->addInput(new Form_checkbox( + 'disable', + 'Disabled', + 'Disable this server', + $pconfig['disable'] + ))->setHelp('Set this option to disable this server without removing it from the list'); + + $section->addInput(new Form_Select( + 'mode', + 'Server mode', + $pconfig['mode'], + build_mode_list() + )); + + $section->addInput(new Form_Select( + 'dev_mode', + 'Device mode', + empty($pconfig['dev_mode']) ? 'tun':$pconfig['dev_mode'], + array_combine($openvpn_dev_mode, $openvpn_dev_mode) + )); + + $section->addInput(new Form_Select( + 'interface', + 'Interface', + $pconfig['interface'], + build_if_list() + )); + + $section->addInput(new Form_Input( + 'local_port', + 'Local port', + 'number', + $pconfig['local_port'] + )); + + $section->addInput(new Form_Input( + 'description', + 'Description', + 'text', + $pconfig['description'] + ))->setHelp('You may enter a description here for your reference (not parsed).'); + + $form->add($section); + + $section = new Form_Section('Cryptographic settings'); + + $section->addInput(new Form_checkbox( + 'tlsauth_enable', + 'TLS authentication', + 'Enable authentication of TLS packets.', + $pconfig['tlsauth_enable'] + )); + + if (!$pconfig['tls']) { + $section->addInput(new Form_checkbox( + 'autotls_enable', + null, + 'Automatically generate a shared TLS authentication key.', + $pconfig['autotls_enable'] + )); + } + + $section->addInput(new Form_TextArea( + 'tls', + 'Key', + $pconfig['tls'] + ))->setHelp('Paste your shared key here'); + + $section->addInput(new Form_Select( + 'caref', + 'Peer Certifiacte Authority', + $pconfig['caref'], + count($a_ca) ? array_combine($a_ca, $a_ca) : ['' => 'None'] + ))->setHelp(count($a_ca) ? '':sprintf('No Certificate Authorities defined. You may create one here: %s', '<a href="system_camanager.php">System > Cert Manager</a>')); + + $section->addInput(new Form_Select( + 'crlref', + 'Peer Certifiacte Revocation list', + $pconfig['crlref'], + build_crl_list() + ))->setHelp(count($a_crl) ? '':sprintf('No Certificate Revocation Lists defined. You may create one here: %s', '<a href="system_camanager.php">System > Cert Manager</a>')); + + $section->addInput(new Form_Select( + 'certref', + 'Peer Certifiacte Authority', + $pconfig['certref'], + build_cert_list() + ))->setHelp(count($a_cert) ? '':sprintf('No Certificates defined. You may create one here: %s', '<a href="system_camanager.php">System > Cert Manager</a>')); + + $section->addInput(new Form_Select( + 'dh_length', + 'DH Parameter length (bits)', + $pconfig['dh_length'], + array_combine($openvpn_dh_lengths,$openvpn_dh_lengths) + ))->setHelp(count($a_cert) ? '':sprintf('No Certificates defined. You may create one here: %s', '<a href="system_camanager.php">System > Cert Manager</a>')); + + if (!$pconfig['shared_key']) { + $section->addInput(new Form_checkbox( + 'autokey_enable', + 'Auto generate', + 'Automatically generate a shared key', + $pconfig['autokey_enable'] + )); + } + + $section->addInput(new Form_TextArea( + 'shared_key', + 'Shared Key', + $pconfig['shared_key'] + ))->setHelp('Paste your shared key here'); + + $section->addInput(new Form_Select( + 'crypto', + 'Encryption Algorithm', + $pconfig['crypto'], + openvpn_get_cipherlist() + )); + + $section->addInput(new Form_Select( + 'digest', + 'Auth digest algorithm', + $pconfig['digest'], + openvpn_get_digestlist() + ))->setHelp('Leave this set to SHA1 unless all clients are set to match. SHA1 is the default for OpenVPN. '); + + $section->addInput(new Form_Select( + 'engine', + 'Hardware Crypto', + $pconfig['engine'], + openvpn_get_engines() + )); + + $section->addInput(new Form_Select( + 'cert_depth', + 'Certificate Depth', + $pconfig['cert_depth'], + $openvpn_cert_depths + ))->setHelp('When a certificate-based client logs in, do not accept certificates below this depth. ' . + 'Useful for denying certificates made with intermediate CAs generated from the same CA as the server.'); + + $section->addInput(new Form_checkbox( + 'strictusercn', + 'Strict User-CN Matching', + null, + $pconfig['strictusercn'] + ))->setHelp('When authenticating users, enforce a match between the common name of the client certificate and the username given at login.'); + + $form->add($section); + + $section = new Form_Section('Tunnel settings'); + + $section->addInput(new Form_Input( + 'tunnel_network', + 'IPv4 Tunnel Network', + 'text', + $pconfig['tunnel_network'] + ))->setHelp('This is the IPv4 virtual network used for private communications between this server and client ' . + 'hosts expressed using CIDR (eg. 10.0.8.0/24). The first network address will be assigned to ' . + 'the server virtual interface. The remaining network addresses can optionally be assigned ' . + 'to connecting clients. (see Address Pool)'); + + $section->addInput(new Form_Input( + 'tunnel_networkv6', + 'IPv6 Tunnel Network', + 'text', + $pconfig['tunnel_networkv6'] + ))->setHelp('This is the IPv6 virtual network used for private ' . + 'communications between this server and client hosts expressed using CIDR (eg. fe80::/64). ' . + 'The first network address will be assigned to the server virtual interface. The remaining ' . + 'network addresses can optionally be assigned to connecting clients. (see Address Pool)'); + + $section->addInput(new Form_checkbox( + 'serverbridge_dhcp', + 'Bridge DHCP', + 'Allow clients on the bridge to obtain DHCP.', + $pconfig['serverbridge_dhcp'] + )); + + $section->addInput(new Form_Select( + 'serberbridge_inerface', + 'Bridge Interface', + $pconfig['serberbridge_inerface'], + build_bridge_list() + ))->setHelp('The interface to which this tap instance will be bridged. This is not done automatically. You must assign this ' . + 'interface and create the bridge separately. This setting controls which existing IP address and subnet ' . + 'mask are used by OpenVPN for the bridge. Setting this to "none" will cause the Server Bridge DHCP settings below to be ignored.'); + + $section->addInput(new Form_Input( + 'serverbridge_dhcp_start', + 'Server Bridge DHCP Start', + 'text', + $pconfig['serverbridge_dhcp_start'] + ))->setHelp('When using tap mode as a multi-point server, you may optionally supply a DHCP range to use on the ' . + 'interface to which this tap instance is bridged. If these settings are left blank, DHCP will be passed ' . + 'through to the LAN, and the interface setting above will be ignored.'); + + $section->addInput(new Form_Input( + 'serverbridge_dhcp_end', + 'Server Bridge DHCP End', + 'text', + $pconfig['serverbridge_dhcp_end'] + )); + + $section->addInput(new Form_checkbox( + 'gwredir', + 'Redirect Gateway', + 'Force all client generated traffic through the tunnel.', + $pconfig['gwredit'] + )); + + $section->addInput(new Form_Input( + 'local_network', + 'IPv4 Local network(s)', + 'text', + $pconfig['local_network'] + ))->setHelp('IPv4 networks that will be accessible from the remote endpoint. ' . + 'Expressed as a comma-separated list of one or more CIDR ranges. ' . + 'You may leave this blank if you don\'t want to add a route to the local network through this tunnel on the remote machine. ' . + 'This is generally set to your LAN network.'); + + $section->addInput(new Form_Input( + 'local_networkv6', + 'IPv6 Local network(s)', + 'text', + $pconfig['local_networkv6'] + ))->setHelp('IPv6 networks that will be accessible from the remote endpoint. ' . + 'Expressed as a comma-separated list of one or more IP/PREFIX. You may leave this blank if you don\'t want to add a ' . + 'route to the local network through this tunnel on the remote machine. This is generally set to your LAN network.'); + + $section->addInput(new Form_Input( + 'remote_network', + 'IPv4 Remote network(s)', + 'text', + $pconfig['remote_network'] + ))->setHelp('IPv4 networks that will be routed through the tunnel, so that a site-to-site VPN can be established without manually ' . + 'changing the routing tables. Expressed as a comma-separated list of one or more CIDR ranges. ' . + 'If this is a site-to-site VPN, enter the remote LAN/s here. You may leave this blank if you don\'t want a site-to-site VPN.'); + + $section->addInput(new Form_Input( + 'remote_networkv6', + 'IPv6 Remote network(s)', + 'text', + $pconfig['remote_networkv6'] + ))->setHelp('These are the IPv6 networks that will be routed through the tunnel, so that a site-to-site VPN can be established without manually ' . + 'changing the routing tables. Expressed as a comma-separated list of one or more IP/PREFIX. ' . + 'If this is a site-to-site VPN, enter the remote LAN/s here. You may leave this blank if you don\'t want a site-to-site VPN.'); + + $section->addInput(new Form_Input( + 'maxclients', + 'Concurrent connections', + 'number', + $pconfig['maxclients'] + ))->setHelp('Specify the maximum number of clients allowed to concurrently connect to this server.'); + + $section->addInput(new Form_Select( + 'compression', + 'Compression', + $pconfig['compression'], + $openvpn_compression_modes + ))->setHelp('Compress tunnel packets using the LZO algorithm. ' . + 'Adaptive compression will dynamically disable compression for a period of time if OpenVPN detects that the data in the ' . + 'packets is not being compressed efficiently"'); + + $section->addInput(new Form_checkbox( + 'passtos', + 'Type-of-Service', + 'Set the TOS IP header value of tunnel packets to match the encapsulated packet value.', + $pconfig['passtos'] + )); + + $section->addInput(new Form_checkbox( + 'client2client', + 'Inter-client communication', + 'Allow communication between clients connected to this server', + $pconfig['client2client'] + )); + + $section->addInput(new Form_checkbox( + 'duplicate_cn', + 'Duplicate Connection', + 'Allow multiple concurrent connections from clients using the same Common Name.', + $pconfig['duplicate_cn'] + ))->setHelp('(This is not generally recommended, but may be needed for some scenarios.)'); + + $section->addInput(new Form_checkbox( + 'no_tun_ipv6', + 'Disable IPv6', + 'Don\'t forward IPv6 traffic. ', + $pconfig['no_tun_ipv6'] + )); + + $form->add($section); + + $section = new Form_Section('Client Settings'); + + $section->addInput(new Form_checkbox( + 'dynamic_ip', + 'Dynamic IP', + 'Allow connected clients to retain their connections if their IP address changes', + $pconfig['dynamic_ip'] + )); + + $section->addInput(new Form_checkbox( + 'pool_enable', + 'Address Pool', + 'Provide a virtual adapter IP address to clients (see Tunnel Network)', + $pconfig['pool_enable'] + )); + + $section->addInput(new Form_checkbox( + 'topology_subnet', + 'Topology', + 'Allocate only one IP per client (topology subnet), rather than an isolated subnet per client (topology net30).', + $pconfig['topology_subnet'] + ))->setHelp('Relevant when supplying a virtual adapter IP address to clients when using tun mode on IPv4.").' . '<br />' . + 'Some clients may require this even for IPv6, such as OpenVPN Connect (iOS/Android). ' . + 'Others may break if it is present, such as older versions of OpenVPN or clients such as Yealink phones.'); + + $section->addInput(new Form_checkbox( + 'dns_domain_enable', + 'DNS Default Domain', + 'Provide a default domain name to clients', + $pconfig['dns_domain_enable'] + )); + + $section->addInput(new Form_Input( + 'dns_domain', + 'DNS Default Domain', + 'text', + $pconfig['dns_domain'] + )); + + $section->addInput(new Form_checkbox( + 'dns_server_enable', + 'DNS Server enable', + 'Provide a DNS server list to clients', + $pconfig['dns_server_enable'] + )); + + $section->addInput(new Form_Input( + 'dns_server1', + 'DNS Server 1', + 'text', + $pconfig['dns_server1'] + )); + + $section->addInput(new Form_Input( + 'dns_server2', + 'DNS Server 2', + 'text', + $pconfig['dns_server2'] + )); + + $section->addInput(new Form_Input( + 'dns_server3', + 'DNS Server 3', + 'text', + $pconfig['dns_server3'] + )); + + $section->addInput(new Form_Input( + 'dns_server4', + 'DNS Server 4', + 'text', + $pconfig['dns_server4'] + )); + + $section->addInput(new Form_checkbox( + 'push_register_dns', + 'Force DNS cache update', + 'Run "net stop dnscache", "net start dnscache", "ipconfig /flushdns" and "ipconfig /registerdns" on connection initiation.', + $pconfig['push_register_dns'] + ))->setHelp('This is known to kick Windows into recognizing pushed DNS servers.'); + + $section->addInput(new Form_checkbox( + 'ntp_server_enable', + 'NTP Server enable', + 'Provide an NTP server list to clients', + $pconfig['ntp_server_enable'] + )); + + $section->addInput(new Form_Input( + 'ntp_server1', + 'NTP Server 1', + 'text', + $pconfig['ntp_server1'] + )); + + $section->addInput(new Form_Input( + 'ntp_server2', + 'NTP Server 2', + 'text', + $pconfig['ntp_server2'] + )); + + $form->add($section); + + $section = new Form_Section('Netbios Options'); + + $section->addInput(new Form_checkbox( + 'netbios_enable', + 'Netbios enable', + 'Enable NetBIOS over TCP/IP', + $pconfig['ntp_server_enable'] + ))->setHelp('If this option is not set, all NetBIOS-over-TCP/IP options (including WINS) will be disabled'); + + $section->addInput(new Form_Select( + 'netbios_ntype', + 'Node Type', + $pconfig['nbios_ntype'], + $netbios_nodetypes + ))->setHelp('Possible options: b-node (broadcasts), p-node (point-to-point name queries to a WINS server), ' . + 'm-node (broadcast then query name server), and h-node (query name server, then broadcast)'); + + $section->addInput(new Form_Input( + 'netbios_scope', + 'Scope ID', + 'text', + $pconfig['netbios_scope'] + ))->setHelp('A NetBIOS Scope ID provides an extended naming service for NetBIOS over TCP/IP. The NetBIOS ' . + 'scope ID isolates NetBIOS traffic on a single network to only those nodes with the same ' . + 'NetBIOS scope ID'); + + $section->addInput(new Form_checkbox( + 'wins_server_enable', + 'WINS server enable', + 'Provide a WINS server list to clients', + $pconfig['wins_server_enable'] + )); + + $section->addInput(new Form_Input( + 'wins_server1', + 'WINS Server 1', + 'text', + $pconfig['wins_server1'] + )); + + $section->addInput(new Form_Input( + 'wins_server2', + 'WINS Server 2', + 'text', + $pconfig['wins_server2'] + )); + + $section->addInput(new Form_checkbox( + 'client_mgmt_port_enable', + 'Enable custom port ', + 'Use a different management port for clients.', + $pconfig['client_mgmt_port_enable'] + )); + + $section->addInput(new Form_Input( + 'client_mgmt_port', + 'Management port', + 'number', + $pconfig['client_mgmt_port'] + ))->setHelp('The default port is 166. Specify a different port if the client machines need to select from multiple OpenVPN links.'); + + + $form->add($section); + + $section = new Form_Section('Advanced Configuration'); + $section->addClass('advanced'); + + $section->addInput(new Form_TextArea( + 'custom_options', + 'Custom options', + $pconfig['custom_options'] + ))->setHelp('Enter any additional options you would like to add to the OpenVPN server configuration here, separated by semicolon' . '<br />' . + 'EXAMPLE: push "route 10.0.0.0 255.255.255.0"'); + + $section->addInput(new Form_Select( + 'verbosity_level', + 'Verbosity level', + $pconfig['verbosity_level'], + $openvpn_verbosity_level + ))->setHelp('Each level shows all info from the previous levels. Level 3 is recommended if you want a good summary of what\'s happening without being swamped by output' . '<br /><br />' . + 'None: Only fatal errors' . '<br />' . + 'Default: Normal usage range' . '<br />' . + '5: Output R and W characters to the console for each packet read and write, uppercase is used for TCP/UDP packets and lowercase is used for TUN/TAP packets' .'<br />' . + '6: Debug info range'); + + $section->addInput(new Form_Input( + 'act', + null, + 'hidden', + $act + )); + + if (isset($id) && $a_server[$id]) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + $id + )); + } + + $form->add($section); + print($form); + +else: +?> +<div class="panel panel-default"> + <div class="panel-heading"><h2 class="panel-title"><?=gettext('OpenVPN Servers')?></h2></div> + <div class="panel-body table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Protocol / Port")?></th> + <th><?=gettext("Tunnel Network")?></th> + <th><?=gettext("Description")?></th> + <th><!-- Buttons --></th> + </tr> + </thead> + + <tbody> +<?php + $i = 0; + foreach($a_server as $server): +?> + <tr <?=isset($server['disable']) ? 'class="disabled"':''?>> + <td> + <?=htmlspecialchars($server['protocol'])?> / <?=htmlspecialchars($server['local_port'])?> + </td> + <td> + <?=htmlspecialchars($server['tunnel_network'])?><br /> + <?=htmlspecialchars($server['tunnel_networkv6'])?> + </td> + <td> + <?=htmlspecialchars($server['description'])?> + </td> + <td> + <a href="vpn_openvpn_server.php?act=edit&id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext("Edit")?></a> + <a href="vpn_openvpn_server.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger"><?=gettext("Delete")?></a> + </td> + </tr> +<?php + $i++; + endforeach; +?> + </tbody> + </table> + </div> +</div> + +<nav class="action-buttons"> + <a href="vpn_openvpn_server.php?act=new" class="btn btn-sm btn-success"> + <?=gettext("Add server")?> + </a> +</nav> + +<?php +endif; + +// Note: +// The following *_change() functions were converted from Javascript/DOM to JQuery but otherwise +// mostly left unchanged. The logic on this form is complex andthis works! +?> + +<script type="text/javascript"> +//<![CDATA[ +events.push(function(){ + + function mode_change() { + value = $('#mode').val(); + + switch(value) { + case "p2p_tls": + case "server_tls": + case "server_user": + hideCheckbox('tlsauth_enable', false); + hideCheckbox('auto_tls_enable', false); + hideInput('tls', false); + hideInput('ca_ref', false); + hideInput('crl_ref', false); + hideInput('certref', false); + hideInput('dh_length', false); + hideInput('cert_depth', false); + hideInput('strictusercn', true); + hideInput('autokey_enable', false); + hideInput('shared_key', false); + break; + case "server_tls_user": + hideCheckbox('tlsauth_enable', false); + hideCheckbox('auto_tls_enable', false); + hideInput('tls', false); + hideInput('ca_ref', false); + hideInput('crl_ref', false); + hideInput('certref', false); + hideInput('dh_length', false); + hideInput('cert_depth', false); + hideInput('strictusercn', false); + hideInput('autokey_enable', true); + hideInput('shared_key', true); + break; + case "p2p_shared_key": + hideCheckbox('tlsauth_enable', true); + hideCheckbox('auto_tls_enable', true); + hideInput('tls', true); + hideInput('ca_ref', true); + hideInput('crl_ref', true); + hideInput('certref', true); + hideInput('dh_length', true); + hideInput('cert_depth', true); + hideInput('strictusercn', true); + hideInput('autokey_enable', false); + hideInput('shared_key', false); + break; + } + + switch(value) { + case "p2p_shared_key": + hideClass('advanced', true); + hideInput('remote_network', false); + hideInput('remote_networkv6', false); + hideInput('gwredir', true); + hideInput('local_network', true); + hideInput('local_networkv6', true); + hideInput('authmode', true); + hideInput('client2client', true); + break; + case "p2p_tls": + hideClass('advanced', true); + hideInput('remote_network', false); + hideInput('remote_networkv6', false); + hideInput('gwredir', false); + hideInput('local_network', false); + hideInput('local_networkv6', false); + hideInput('authmode', true); + hideInput('client2client', true); + break; + case "server_user": + case "server_tls_user": + hideClass('advanced', false); + hideInput('remote_network', true); + hideInput('remote_networkv6', true); + hideInput('gwredir', false); + hideInput('local_network', false); + hideInput('local_networkv6', false); + hideInput('authmode', false); + hideInput('client2client', false); + break; + case "server_tls": + hideInput('authmode', true); + default: + hideInput('custom_options', false); + hideInput('verbosity_level', false); + hideInput('remote_network', true); + hideInput('remote_networkv6', true); + hideInput('gwredir', false); + hideInput('local_network', false); + hideInput('local_networkv6', false); + hideInput('client2client', false); + break; + } + + gwredir_change(); + } + + function autokey_change() { + var hide = ! $('#autokey_enable').prop('checked') + + hideInput('shared_key', hide); + } + + function tlsauth_change() { + var hide = ! $('#tlsauth_enable').prop('checked') + + <?php if (!$pconfig['tls']): ?> + hideCheckbox('autotls_enable', hide); + <?php endif; ?> + + autotls_change(); + } + + function autotls_change() { + + <?php if (!$pconfig['tls']): ?> + autocheck = $('#autotls_enable').prop('checked'); + <?php else: ?> + autocheck = false; + <?php endif; ?> + + if ($('#tlsauth_enable').prop('checked') && !autocheck) + hideInput('tls', false); + else + hideInput('tls', true); + } + + function gwredir_change() { + var hide = ! $('#gwredir').prop('checked') + + hideInput('local_network', hide); + hideInput('local_networkv6', hide); + hideInput('remote_network', hide); + hideInput('remote_networkv6', hide); + } + + function dns_domain_change() { + var hide = ! $('#dns_domain_enable').prop('checked') + + hideInput('dns_domain', hide); + } + + function dns_server_change() { + var hide = ! $('#dns_server_enable').prop('checked') + + hideInput('dns_server1', hide); + hideInput('dns_server2', hide); + hideInput('dns_server3', hide); + hideInput('dns_server4', hide); + } + + function wins_server_change() { + var hide = ! $('#wins_server_enable').prop('checked') + + hideInput('wins_server1', hide); + hideInput('wins_server2', hide); + } + + function client_mgmt_port_change() { + var hide = ! $('#client_mgmt_port_enable').prop('checked') + + hideInput('client_mgmt_port', hide); + } + + function ntp_server_change() { + var hide = ! $('#ntp_server_enable').prop('checked') + + hideInput('ntp_server1', hide); + hideInput('ntp_server2', hide); + } + + function netbios_change() { + var hide = ! $('#netbios_enable').prop('checked') + + hideInput('netbios_ntype', hide); + hideInput('netbios_scope', hide); + hideCheckbox('wins_server_enable', hide); + wins_server_change(); + hideCheckbox('client_mgmt_port_enable', hide); + client_mgmt_port_change(); + } + + function tuntap_change() { + + mvalue = $('#mode').val(); + + switch(mvalue) { + case "p2p_tls": + case "p2p_shared_key": + p2p = true; + break; + default: + p2p = false; + break; + } + + value = $('#dev_mode').val(); + + switch(value) { + case "tun": + hideCheckbox('no_tun_ipv6', false); + hideInput('tunnel_network', false); + hideCheckbox('serverbridge_dhcp', true); + hideInput('serverbridge_interface', true); + hideInput('serverbridge_dhcp_start', true); + hideInput('serverbridge_dhcp_end', true); + hideInput('topology_subnet', false); + break; + + case "tap": + hideCheckbox('no_tun_ipv6', true); + hideInput('tunnel_network', false); + + if (!p2p) { + hideCheckbox('serverbridge_dhcp', false); + hideInput('serverbridge_interface', false); + hideInput('serverbridge_dhcp_start', false); + hideInput('serverbridge_dhcp_end', false); + hideInput('topology_subnet', false); + + if( $('#serverbridge_dhcp').prop('checked')) { + disableInput('serverbridge_interface', false); + disableInput('serverbridge_dhcp_start', false); + disableInput('serverbridge_dhcp_end', false); + } else { + disableInput('serverbridge_interface', true); + disableInput('serverbridge_dhcp_start', true); + disableInput('serverbridge_dhcp_end', true); + } + } else { + hideInput('topology_subnet', true); + disableInput('serverbridge_dhcp', true); + disableInput('serverbridge_interface', true); + disableInput('serverbridge_dhcp_start', true); + disableInput('serverbridge_dhcp_end', true); + } + + break; + } + } + + // ---------- Library of show/hide functions ---------------------------------------------------------------------- + + // Hides the <div> in which the specified input element lives so that the input, + // its label and help text are hidden + function hideInput(id, hide) { + if(hide) + $('#' + id).parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent('div').removeClass('hidden'); + } + + // Hides the <div> in which the specified checkbox lives so that the checkbox, + // its label and help text are hidden + function hideCheckbox(id, hide) { + if(hide) + $('#' + id).parent().parent().parent('div').addClass('hidden'); + else + $('#' + id).parent().parent().parent('div').removeClass('hidden'); + } + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // Hides all elements of the specified class. This will usually be a section or group + function hideClass(s_class, hide) { + if(hide) + $('.' + s_class).hide(); + else + $('.' + s_class).show(); + } + + // ---------- Monitor elements for change and call the appropriate display functions ------------------------------ + + // NTP + $('#ntp_server_enable').click(function () { + ntp_server_change(); + }); + + // Netbios + $('#netbios_enable').click(function () { + netbios_change(); + }); + + // Client management port + $('#client_mgmt_port_enable').click(function () { + client_mgmt_port_change(); + }); + + // Wins server port + $('#wins_server_enable').click(function () { + wins_server_change(); + }); + + // DNS server port + $('#dns_server_enable').click(function () { + dns_server_change(); + }); + + // DNS server port + $('#dns_domain_enable').click(function () { + dns_domain_change(); + }); + + // Gateway redirect + $('#gwredir').click(function () { + gwredir_change(); + }); + + // Auto TLSkey generation + $('#autotls_enable').click(function () { + autotls_change(); + }); + + // TLS Authorization + $('#tlsauth_enable').click(function () { + tlsauth_change(); + }); + + // Auto key + $('#autokey_enable').click(function () { + autokey_change(); + }); + + // Mode + $('#mode').click(function () { + mode_change(); + }); + + // Tun/tap mode + $('#dev_mode').click(function () { + tuntap_change(); + }); + + // ---------- Set initial page display state ---------------------------------------------------------------------- + mode_change(); + autokey_change(); + tlsauth_change(); + gwredir_change(); + dns_domain_change(); + dns_server_change(); + wins_server_change(); + client_mgmt_port_change(); + ntp_server_change(); + netbios_change(); + tuntap_change(); +}); +//]]> +</script> +<?php + +include("foot.inc"); diff --git a/src/usr/local/www/vpn_pppoe.php b/src/usr/local/www/vpn_pppoe.php new file mode 100644 index 0000000..726cfaf --- /dev/null +++ b/src/usr/local/www/vpn_pppoe.php @@ -0,0 +1,151 @@ +<?php +/* + vpn_pppoe.php + Copyright (C) 2010 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_MODULE: pppoe +*/ + +##|+PRIV +##|*IDENT=page-services-pppoeserver +##|*NAME=Services: PPPoE Server page +##|*DESCR=Allow access to the 'Services: PPPoE Server' page. +##|*MATCH=vpn_pppoe.php* +##|-PRIV + +require_once("guiconfig.inc"); +require_once("filter.inc"); +require_once("vpn.inc"); + +if (!is_array($config['pppoes']['pppoe'])) { + $config['pppoes']['pppoe'] = array(); +} + +$a_pppoes = &$config['pppoes']['pppoe']; + +if ($_POST) { + $pconfig = $_POST; + + if ($_POST['apply']) { + if (file_exists("{$g['tmp_path']}/.vpn_pppoe.apply")) { + $toapplylist = unserialize(file_get_contents("{$g['tmp_path']}/.vpn_pppoe.apply")); + foreach ($toapplylist as $pppoeid) { + if (!is_numeric($pppoeid)) { + continue; + } + if (is_array($config['pppoes']['pppoe'])) { + foreach ($config['pppoes']['pppoe'] as $pppoe) { + if ($pppoe['pppoeid'] == $pppoeid) { + vpn_pppoe_configure($pppoe); + break; + } + } + } + } + @unlink("{$g['tmp_path']}/.vpn_pppoe.apply"); + } + $retval = 0; + $retval |= filter_configure(); + $savemsg = get_std_save_message($retval); + clear_subsystem_dirty('vpnpppoe'); + } +} + +if ($_GET['act'] == "del") { + if ($a_pppoes[$_GET['id']]) { + if ("{$g['varrun_path']}/pppoe" . $a_pppoes[$_GET['id']]['pppoeid'] . "-vpn.pid") { + killbypid("{$g['varrun_path']}/pppoe" . $a_pppoes[$_GET['id']]['pppoeid'] . "-vpn.pid"); + } + if (is_dir("{$g['varetc_path']}/pppoe" . $a_pppoes[$_GET['id']]['pppoeid'])) { + mwexec("/bin/rm -r {$g['varetc_path']}/pppoe" . $a_pppoes[$_GET['id']]['pppoeid']); + } + unset($a_pppoes[$_GET['id']]); + write_config(); + header("Location: vpn_pppoe.php"); + exit; + } +} + +$pgtitle = array(gettext("VPN"), gettext("PPPoE")); +$shortcut_section = "pppoes"; +include("head.inc"); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +if (is_subsystem_dirty('vpnpppoe')) + print_info_box_np(gettext('The PPPoE entry list has been changed') . '.<br />' . gettext('You must apply the changes in order for them to take effect.')); +?> + +<div class="table-responsive"> + <table class="table table-striped table-hover table-condensed"> + <thead> + <tr> + <th><?=gettext("Interface")?></th> + <th><?=gettext("Local IP")?></th> + <th><?=gettext("Number of users")?></th> + <th><?=gettext("Description")?></th> + <th><!-- Action buttons --></th> + </tr> + </thead> + <tbody> +<?php +$i = 0; +foreach ($a_pppoes as $pppoe): +?> + <tr> + <td> + <?=htmlspecialchars(convert_friendly_interface_to_friendly_descr($pppoe['interface']))?> + </td> + <td> + <?=htmlspecialchars($pppoe['localip'])?> + </td> + <td> + <?=htmlspecialchars($pppoe['n_pppoe_units'])?> + </td> + <td> + <?=htmlspecialchars($pppoe['descr'])?> + </td> + <td> + <a href="vpn_pppoe_edit.php?id=<?=$i?>" class="btn btn-xs btn-info"><?=gettext('Edit')?></a> + <a href="vpn_pppoe.php?act=del&id=<?=$i?>" class="btn btn-xs btn-danger"><?=gettext('Delete')?></a> + </td> + </tr> +<?php + $i++; +endforeach; +?> + </tbody> + </table> +</div> + +<nav class="action-buttons"> + <a href="vpn_pppoe_edit.php" class="btn btn-success"><?=gettext("Add")?></a> +</nav> + +<?php +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/vpn_pppoe_edit.php b/src/usr/local/www/vpn_pppoe_edit.php new file mode 100644 index 0000000..e4617a8 --- /dev/null +++ b/src/usr/local/www/vpn_pppoe_edit.php @@ -0,0 +1,619 @@ +<?php +/* + vpn_pppoe_edit.php + part of pfSense + + Copyright (C) 2005 Scott Ullrich (sullrich@gmail.com) + Copyright (C) 2010 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-services-pppoeserver-edit +##|*NAME=Services: PPPoE Server: Edit page +##|*DESCR=Allow access to the 'Services: PPPoE Server: Edit' page. +##|*MATCH=vpn_pppoe_edit.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("vpn.inc"); +$addrow = false; +$dltrow = 9999; + +function vpn_pppoe_get_id() { + global $config; + + $vpnid = 1; + if (is_array($config['pppoes']['pppoe'])) { + foreach ($config['pppoes']['pppoe'] as $pppoe) { + if ($vpnid == $pppoe['pppoeid']) { + $vpnid++; + } else { + return $vpnid; + } + } + } + + return $vpnid; +} + +if (!is_array($config['pppoes']['pppoe'])) { + $config['pppoes']['pppoe'] = array(); +} + +$a_pppoes = &$config['pppoes']['pppoe']; + +if (is_numericint($_GET['id'])) { + $id = $_GET['id']; + +if($_GET['addrow'] == 'true') + $addrow = true; + +if (is_numericint($_GET['dltrow'])) + $dltrow = $_GET['dltrow']; + +if (isset($_POST['id']) && is_numericint($_POST['id'])) + $id = $_POST['id']; +} + +if (isset($id) && $a_pppoes[$id]) { + $pppoecfg =& $a_pppoes[$id]; + + $pconfig['remoteip'] = $pppoecfg['remoteip']; + $pconfig['localip'] = $pppoecfg['localip']; + $pconfig['mode'] = $pppoecfg['mode']; + $pconfig['interface'] = $pppoecfg['interface']; + $pconfig['n_pppoe_units'] = $pppoecfg['n_pppoe_units']; + $pconfig['pppoe_subnet'] = $pppoecfg['pppoe_subnet']; + $pconfig['pppoe_dns1'] = $pppoecfg['dns1']; + $pconfig['pppoe_dns2'] = $pppoecfg['dns2']; + $pconfig['descr'] = $pppoecfg['descr']; + $pconfig['username'] = $pppoecfg['username']; + $pconfig['pppoeid'] = $pppoecfg['pppoeid']; + if (is_array($pppoecfg['radius'])) { + $pconfig['radacct_enable'] = isset($pppoecfg['radius']['accounting']); + $pconfig['radiusissueips'] = isset($pppoecfg['radius']['radiusissueips']); + if (is_array($pppoecfg['radius']['server'])) { + $pconfig['radiusenable'] = isset($pppoecfg['radius']['server']['enable']); + $pconfig['radiusserver'] = $pppoecfg['radius']['server']['ip']; + $pconfig['radiusserverport'] = $pppoecfg['radius']['server']['port']; + $pconfig['radiusserveracctport'] = $pppoecfg['radius']['server']['acctport']; + $pconfig['radiussecret'] = $pppoecfg['radius']['server']['secret']; + } + + if (is_array($pppoecfg['radius']['server2'])) { + $pconfig['radiussecenable'] = isset($pppoecfg['radius']['server2']['enable']); + $pconfig['radiusserver2'] = $pppoecfg['radius']['server2']['ip']; + $pconfig['radiusserver2port'] = $pppoecfg['radius']['server2']['port']; + $pconfig['radiusserver2acctport'] = $pppoecfg['radius']['server2']['acctport']; + $pconfig['radiussecret2'] = $pppoecfg['radius']['server2']['secret2']; + } + + $pconfig['radius_nasip'] = $pppoecfg['radius']['nasip']; + $pconfig['radius_acct_update'] = $pppoecfg['radius']['acct_update']; + } +} + +if ($_POST) { + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if ($_POST['mode'] == "server") { + $reqdfields = explode(" ", "localip remoteip"); + $reqdfieldsn = array(gettext("Server address"), gettext("Remote start address")); + + if ($_POST['radiusenable']) { + $reqdfields = array_merge($reqdfields, explode(" ", "radiusserver radiussecret")); + $reqdfieldsn = array_merge($reqdfieldsn, + array(gettext("RADIUS server address"), gettext("RADIUS shared secret"))); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($_POST['localip'] && !is_ipaddr($_POST['localip']))) { + $input_errors[] = gettext("A valid server address must be specified."); + } + if (($_POST['pppoe_subnet'] && !is_ipaddr($_POST['remoteip']))) { + $input_errors[] = gettext("A valid remote start address must be specified."); + } + if (($_POST['radiusserver'] && !is_ipaddr($_POST['radiusserver']))) { + $input_errors[] = gettext("A valid RADIUS server address must be specified."); + } + + $_POST['remoteip'] = $pconfig['remoteip'] = gen_subnet($_POST['remoteip'], $_POST['pppoe_subnet']); + $subnet_start = ip2ulong($_POST['remoteip']); + $subnet_end = ip2ulong($_POST['remoteip']) + $_POST['pppoe_subnet'] - 1; + if ((ip2ulong($_POST['localip']) >= $subnet_start) && + (ip2ulong($_POST['localip']) <= $subnet_end)) { + $input_errors[] = gettext("The specified server address lies in the remote subnet."); + } + if ($_POST['localip'] == get_interface_ip($_POST['interface'])) { + $input_errors[] = gettext("The specified server address is equal to an interface ip address."); + } + + for ($x = 0; $x < 4999; $x++) { + if ($_POST["username{$x}"]) { + if (empty($_POST["password{$x}"])) { + $input_errors[] = sprintf(gettext("No password specified for username %s"), $_POST["username{$x}"]); + } + if ($_POST["ip{$x}"] <> "" && !is_ipaddr($_POST["ip{$x}"])) { + $input_errors[] = sprintf(gettext("Incorrect ip address specified for username %s"), $_POST["username{$x}"]); + } + } + } + } + + if ($_POST['pppoeid'] && !is_numeric($_POST['pppoeid'])) { + $input_errors[] = gettext("Wrong data submitted"); + } + + if (!$input_errors) { + $pppoecfg = array(); + + $pppoecfg['remoteip'] = $_POST['remoteip']; + $pppoecfg['localip'] = $_POST['localip']; + $pppoecfg['mode'] = $_POST['mode']; + $pppoecfg['interface'] = $_POST['interface']; + $pppoecfg['n_pppoe_units'] = $_POST['n_pppoe_units']; + $pppoecfg['pppoe_subnet'] = $_POST['pppoe_subnet']; + $pppoecfg['descr'] = $_POST['descr']; + if ($_POST['radiusserver'] || $_POST['radiusserver2']) { + $pppoecfg['radius'] = array(); + + $pppoecfg['radius']['nasip'] = $_POST['radius_nasip']; + $pppoecfg['radius']['acct_update'] = $_POST['radius_acct_update']; + } + + if ($_POST['radiusserver']) { + $pppoecfg['radius']['server'] = array(); + + $pppoecfg['radius']['server']['ip'] = $_POST['radiusserver']; + $pppoecfg['radius']['server']['secret'] = $_POST['radiussecret']; + $pppoecfg['radius']['server']['port'] = $_POST['radiusserverport']; + $pppoecfg['radius']['server']['acctport'] = $_POST['radiusserveracctport']; + } + + if ($_POST['radiusserver2']) { + $pppoecfg['radius']['server2'] = array(); + + $pppoecfg['radius']['server2']['ip'] = $_POST['radiusserver2']; + $pppoecfg['radius']['server2']['secret2'] = $_POST['radiussecret2']; + $pppoecfg['radius']['server2']['port'] = $_POST['radiusserver2port']; + $pppoecfg['radius']['server2']['acctport'] = $_POST['radiusserver2acctport']; + } + + if ($_POST['pppoe_dns1'] <> "") { + $pppoecfg['dns1'] = $_POST['pppoe_dns1']; + } + + if ($_POST['pppoe_dns2'] <> "") { + $pppoecfg['dns2'] = $_POST['pppoe_dns2']; + } + + if ($_POST['radiusenable'] == "yes") { + $pppoecfg['radius']['server']['enable'] = true; + } + + if ($_POST['radiussecenable'] == "yes") { + $pppoecfg['radius']['server2']['enable'] = true; + } + + if ($_POST['radacct_enable'] == "yes") { + $pppoecfg['radius']['accounting'] = true; + } + + if ($_POST['radiusissueips'] == "yes") { + $pppoecfg['radius']['radiusissueips'] = true; + } + + if ($_POST['pppoeid']) { + $pppoecfg['pppoeid'] = $_POST['pppoeid']; + } else { + $pppoecfg['pppoeid'] = vpn_pppoe_get_id(); + } + + $users = array(); + for ($x = 0; $x < 4999; $x++) { + if ($_POST["username{$x}"]) { + $usernam = $_POST["username{$x}"] . ":" . base64_encode($_POST["password{$x}"]); + if ($_POST["ip{$x}"]) { + $usernam .= ":" . $_POST["ip{$x}"]; + } + $users[] = $usernam; + } + } + if (count($users) > 0) { + $pppoecfg['username'] = implode(" ", $users); + } + + if (!isset($id)) { + $id = count($a_pppoes); + } + if (file_exists("{$g['tmp_path']}/.vpn_pppoe.apply")) { + $toapplylist = unserialize(file_get_contents("{$g['tmp_path']}/.vpn_pppoe.apply")); + } else { + $toapplylist = array(); + } + + $toapplylist[] = $pppoecfg['pppoeid']; + $a_pppoes[$id] = $pppoecfg; + + write_config(); + mark_subsystem_dirty('vpnpppoe'); + file_put_contents("{$g['tmp_path']}/.vpn_pppoe.apply", serialize($toapplylist)); + header("Location: vpn_pppoe.php"); + exit; + } +} + +function build_interface_list() { + $list = array(); + + $interfaces = get_configured_interface_with_descr(); + + foreach ($interfaces as $iface => $ifacename) + $list[$iface] = $ifacename; + + return($list); +} + +$pgtitle = array(gettext("Services"),gettext("PPPoE Server"), gettext("Edit")); +$shortcut_section = "pppoes"; +include("head.inc"); + +if ($input_errors) + print_input_errors($input_errors); + +if ($savemsg) + print_info_box($savemsg, 'success'); + +require('classes/Form.class.php'); + +$form = new Form(); + +$section = new Form_Section('PPPoE Server Configuration'); + +$section->addInput(new Form_Checkbox( + 'mode', + 'Enable', + 'Enable PPPoE Server', + ($pconfig['mode'] == "server") +)) ->toggles('.form-group:not(:first-child)'); + +$section->addInput(new Form_Select( + 'interface', + 'Interface', + $pconfig['interface'], + build_interface_list() + +)); + +$section->addInput(new Form_Select( + 'pppoe_subnet', + 'Subnet netask', + $pconfig['pppoe_subnet'], + array_combine(range(0, 32, 1), range(0, 32, 1)) +))->setHelp('Hint: 24 is 255.255.255.0'); + +$section->addInput(new Form_Select( + 'n_pppoe_units', + 'No. of PPPoE Users', + $pconfig['n_pppoe_units'], + array_combine(range(0, 255, 1), range(0, 255, 1)) +)); + +$section->addInput(new Form_IpAddress( + 'localip', + 'Server Address', + $pconfig['localip'] +))->setHelp('Enter the IP address the PPPoE server should give to clients for use as their "gateway"' . '<br />' . + 'Typically this is set to an unused IP just outside of the client range '. '<br />' . + 'NOTE: This should NOT be set to any IP address currently in use on this firewall'); + +$section->addInput(new Form_IpAddress( + 'remoteip', + 'Remote Address Range', + $pconfig['remoteip'] +))->setHelp('Specify the starting address for the client IP address subnet'); + +$section->addInput(new Form_Input( + 'descr', + 'Description', + 'text', + $pconfig['descr'] +)); + +$section->addInput(new Form_Input( + 'pppoe_dns1', + 'DNS Servers', + 'text', + $pconfig['pppoe_dns1'] +)); + +$section->addInput(new Form_IpAddress( + 'pppoe_dns2', + null, + $pconfig['pppoe_dns2'] +))->setHelp('If entered these servers will be given to all PPPoE clients, otherwise LAN DNS and one WAN DNS will go to all clients'); + +$section->addInput(new Form_Checkbox( + 'radiusenable', + 'RADIUS', + 'Use a Radius Server for authentication', + $pconfig['radiusenable'] +))->setHelp('All users will be authenticated using the RADIUS server specified below. The local user database ' . + 'will not be used'); + +$section->addInput(new Form_Checkbox( + 'radacct_enable', + null, + 'Enable Radius Accounting', + $pconfig['radacct_enable'] +))->setHelp('Sends accounting packets to the RADIUS server'); + +$section->addInput(new Form_Checkbox( + 'radiussecenable', + null, + 'Use backup RADIUS server', + $pconfig['radiussecenable'] +))->setHelp('If primary server fails all requests will be sent via backup server'); + +$section->addInput(new Form_IpAddress( + 'radius_nasip', + 'NAS IP Address', + $pconfig['radius_nasip'] +))->setHelp('RADIUS server NAS IP Address'); + +$section->addInput(new Form_Input( + 'radius_acct_update', + 'RADIUS Accounting Update', + 'text', + $pconfig['radius_acct_update'] +))->setHelp('RADIUS accounting update period in seconds'); + +$section->addInput(new Form_Checkbox( + 'radiusissueips', + 'Radius Issued IPs', + 'Issue IP Addresses via RADIUS server', + $pconfig['radiusissueips'] +)); + +$group = new Form_Group('RADIUS server Primary'); + +$group->add(new Form_IpAddress( + 'radiusserver', + null, + $pconfig['radiusserver'] +))->setHelp('IP Address'); + +$group->add(new Form_Input( + 'radiusserverport', + null, + 'text', + $pconfig['radiusserverport'] +))->setHelp('Authentication port '); + +$group->add(new Form_Input( + 'radiusserveracctport', + null, + 'text', + $pconfig['radiusserveracctport'] +))->setHelp('Accounting port (optional)'); + +$group->setHelp('Standard ports are 1812 (authentication) and 1813 (accounting)'); + +$section->add($group); + +$section->addInput(new Form_Input( + 'radiussecret', + 'RADIUS primary shared secret', + 'password', + $pconfig['radiussecret'] +))->setHelp('Enter the shared secret that will be used to authenticate to the RADIUS server.'); + +$group = new Form_Group('RADIUS server Secondary'); + +$group->add(new Form_IpAddress( + 'radiusserver2', + null, + $pconfig['radiusserver2'] +))->setHelp('IP Address'); + +$group->add(new Form_Input( + 'radiusserver2port', + null, + 'text', + $pconfig['radiusserver2port'] +))->setHelp('Authentication port '); + +$group->add(new Form_Input( + 'radiusserver2acctport', + null, + 'text', + $pconfig['radiusserver2acctport'] +))->setHelp('Accounting port (optional)'); + +$group->setHelp('Standard ports are 1812 (authentication) and 1813 (accounting)'); + +$section->add($group); + +$section->addInput(new Form_Input( + 'radiussecret2', + 'RADIUS secondary shared secret', + 'password', + $pconfig['radiussecret2'] +))->setHelp('Enter the shared secret that will be used to authenticate to the backup RADIUS server.'); + +$counter = 0; +$usernames = $pconfig['username']; + +//DEBUG +$usernames = 'sbeaver:TXlQYXNzd2Q=:192.168.1.1 smith:TXlQYXNzd2Q=:192.168.2.1 sjones:TXlQYXNzd2Q=:192.168.3.1 salpha:TXlQYXNzd2Q=:192.168.4.1'; + +if($addrow) + $usernames .= ' ::'; + +if ($usernames != ""): + $item = explode(" ", $usernames); + + $numrows = count($item) -1; + + foreach($item as $ww): + $wws = explode(":", $ww); + $user = $wws[0]; + $passwd = base64_decode($wws[1]); + $ip = $wws[2]; + + $tracker = $counter; + + if($tracker != $dltrow) { + $group = new Form_Group($counter == 0 ? 'User table':null); + + $group->add(new Form_Input( + 'username' . $tracker, + null, + 'text', + $user + ))->setHelp($numrows == $tracker ? 'User name':null); + + $group->add(new Form_Input( + 'password' . $tracker, + null, + 'password', + $passwd + ))->setHelp($numrows == $tracker ? 'Password':null); + + $group->add(new Form_IpAddress( + 'ip' . $tracker, + null, + $ip + ))->setHelp($numrows == $tracker ? 'IP Address':null); + + $btndltrow = new Form_Button( + 'btndltrow' . $tracker, + 'Delete', + 'vpn_pppoe_edit.php?id=' . $id . '&dltrow=' . $tracker + ); + + $btndltrow->removeClass('btn-primary')->addClass('btn-danger btn-sm'); + + $group->add($btndltrow); + $section->add($group); + } + + $counter++; + endforeach; +endif; + +$btnaddrow = new Form_Button( + 'btnaddrow', + 'Add Row', + 'vpn_pppoe_edit.php?id=' . $id . '&addrow=true' + ); + +$btnaddrow->removeClass('btn-primary')->addClass('btn-success btn-sm'); + +$section->addInput($btnaddrow); + +// Hidden fields +if(isset($id)) { + $section->addInput(new Form_Input( + 'id', + null, + 'hidden', + htmlspecialchars($id, ENT_QUOTES | ENT_HTML401) + )); +} + +if (isset($pconfig['pppoeid'])) { + $section->addInput(new Form_Input( + 'pppoeid', + null, + 'hidden', + $pconfig['pppoeid'] + )); +} + +$form->add($section); + +print($form); + +print_info_box(gettext('Don\'t forget to add a firewall rule to permit traffic from PPPoE clients')); +?> +<script> +//<![CDATA[ +events.push(function(){ + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // show/hide radius server controls + function hide_radius(hide) { + disableInput('radacct_enable', hide); + disableInput('radiusserver', hide); + disableInput('radiussecret', hide); + disableInput('radiusserverport', hide); + disableInput('radiusserveracctport', hide); + disableInput('radiusissueips', hide); + disableInput('radius_nasip', hide); + disableInput('radiusissueips', hide); + disableInput('radius_nasip', hide); + disableInput('radius_acct_update', hide); + disableInput('radiussecenable', hide); + hide_radius2(hide); + } + // show/hide radius server 2 controls + function hide_radius2(hide) { + disableInput('radiusserver2', hide); + disableInput('radiussecret2', hide); + disableInput('radiusserver2port', hide); + disableInput('radiusserver2acctport', hide); + } + + // When the RADIUS checkbox is clicked . . + $('#radiusenable').click(function () { + hide_radius(!$('#radiusenable').prop('checked')); + if(!$('#radiusenable').prop('checked')) + hide_radius2(true); + else + hide_radius2(!$('#radiussecenable').prop('checked')); + }); + + // When the 'Use backup RADIUS' checkbox is clicked . . + $('#radiussecenable').click(function () { + hide_radius2(!$('#radiussecenable').prop('checked')); + }); + + //I On initial page load + hide_radius2(!$('#radiussecenable').prop('checked')); + hide_radius(!$('#radiusenable').prop('checked')); +}); +//]]> +</script> +<?php +include("foot.inc");
\ No newline at end of file diff --git a/src/usr/local/www/vpn_pptp.php b/src/usr/local/www/vpn_pptp.php new file mode 100644 index 0000000..fc918b4 --- /dev/null +++ b/src/usr/local/www/vpn_pptp.php @@ -0,0 +1,509 @@ +<?php + +/* + WARNING: DEPRICATED! SHOULD NOT BE CONVERTED. SEE https://github.com/SjonHortensius/pfsense/issues/229 +*/ + +/* + vpn_pptp.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-vpnpptp +##|*NAME=VPN: VPN PPTP page +##|*DESCR=Allow access to the 'VPN: VPN PPTP' page. +##|*MATCH=vpn_pptp.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("vpn.inc"); + +if (!is_array($config['pptpd']['radius'])) { + $config['pptpd']['radius'] = array(); +} +$pptpcfg = &$config['pptpd']; + +$pconfig['remoteip'] = $pptpcfg['remoteip']; +$pconfig['localip'] = $pptpcfg['localip']; +$pconfig['redir'] = $pptpcfg['redir']; +$pconfig['mode'] = $pptpcfg['mode']; +$pconfig['wins'] = $pptpcfg['wins']; +$pconfig['req128'] = isset($pptpcfg['req128']); +$pconfig['n_pptp_units'] = $pptpcfg['n_pptp_units']; +$pconfig['pptp_dns1'] = $pptpcfg['dns1']; +$pconfig['pptp_dns2'] = $pptpcfg['dns2']; +$pconfig['radiusenable'] = isset($pptpcfg['radius']['server']['enable']); +$pconfig['radiusissueips'] = isset($pptpcfg['radius']['radiusissueips']); +$pconfig['radiussecenable'] = isset($pptpcfg['radius']['server2']['enable']); +$pconfig['radacct_enable'] = isset($pptpcfg['radius']['accounting']); +$pconfig['radiusserver'] = $pptpcfg['radius']['server']['ip']; +$pconfig['radiusserverport'] = $pptpcfg['radius']['server']['port']; +$pconfig['radiusserveracctport'] = $pptpcfg['radius']['server']['acctport']; +$pconfig['radiussecret'] = $pptpcfg['radius']['server']['secret']; +$pconfig['radiusserver2'] = $pptpcfg['radius']['server2']['ip']; +$pconfig['radiusserver2port'] = $pptpcfg['radius']['server2']['port']; +$pconfig['radiusserver2acctport'] = $pptpcfg['radius']['server2']['acctport']; +$pconfig['radiussecret2'] = $pptpcfg['radius']['server2']['secret2']; +$pconfig['radius_acct_update'] = $pptpcfg['radius']['acct_update']; +$pconfig['radius_nasip'] = $pptpcfg['radius']['nasip']; + +if ($_POST) { + + if (isset($input_errors)) + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if ($_POST['mode'] == "server") { + $reqdfields = explode(" ", "localip remoteip"); + $reqdfieldsn = array(gettext("Server address"),gettext("Remote start address")); + + if ($_POST['radiusenable']) { + $reqdfields = array_merge($reqdfields, explode(" ", "radiusserver radiussecret")); + $reqdfieldsn = array_merge($reqdfieldsn, + array(gettext("RADIUS server address"),gettext("RADIUS shared secret"))); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($_POST['localip'] && !is_ipaddr($_POST['localip']))) { + $input_errors[] = gettext("A valid server address must be specified."); + } + if (is_ipaddr_configured($_POST['localip'])) { + $input_errors[] = gettext("'Server address' parameter should NOT be set to any IP address currently in use on this firewall."); + } + if (!is_ipaddr($_POST['remoteip'])) { + $input_errors[] = gettext("A valid remote start address must be specified."); + } + if (($_POST['radiusserver'] && !is_ipaddr($_POST['radiusserver']))) { + $input_errors[] = gettext("A valid RADIUS server address must be specified."); + } + + if (!$input_errors) { + $subnet_start = ip2ulong($_POST['remoteip']); + $subnet_end = ip2ulong($_POST['remoteip']) + $_POST['n_pptp_units'] - 1; + + if ((ip2ulong($_POST['localip']) >= $subnet_start) && + (ip2ulong($_POST['localip']) <= $subnet_end)) { + $input_errors[] = gettext("The specified server address lies in the remote subnet."); + } + // TODO: Should this check be for any local IP address? + if ($_POST['localip'] == $config['interfaces']['lan']['ipaddr']) { + $input_errors[] = gettext("The specified server address is equal to the LAN interface address."); + } + } + } else if ($_POST['mode'] == "redir") { + $reqdfields = explode(" ", "redir"); + $reqdfieldsn = array(gettext("PPTP redirection target address")); + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (($_POST['redir'] && !is_ipaddr($_POST['redir']))) { + $input_errors[] = gettext("A valid target address must be specified."); + } + } else if (isset($config['pptpd']['mode'])) { + unset($config['pptpd']['mode']); + } + + if (!$input_errors) { + $pptpcfg['remoteip'] = $_POST['remoteip']; + $pptpcfg['redir'] = $_POST['redir']; + $pptpcfg['localip'] = $_POST['localip']; + $pptpcfg['mode'] = $_POST['mode']; + $pptpcfg['wins'] = $_POST['wins']; + $pptpcfg['n_pptp_units'] = $_POST['n_pptp_units']; + $pptpcfg['radius']['server']['ip'] = $_POST['radiusserver']; + $pptpcfg['radius']['server']['port'] = $_POST['radiusserverport']; + $pptpcfg['radius']['server']['acctport'] = $_POST['radiusserveracctport']; + $pptpcfg['radius']['server']['secret'] = $_POST['radiussecret']; + $pptpcfg['radius']['server2']['ip'] = $_POST['radiusserver2']; + $pptpcfg['radius']['server2']['port'] = $_POST['radiusserver2port']; + $pptpcfg['radius']['server2']['acctport'] = $_POST['radiusserver2acctport']; + $pptpcfg['radius']['server2']['secret2'] = $_POST['radiussecret2']; + $pptpcfg['radius']['nasip'] = $_POST['radius_nasip']; + $pptpcfg['radius']['acct_update'] = $_POST['radius_acct_update']; + + if ($_POST['pptp_dns1'] == "") { + if (isset($pptpcfg['dns1'])) + unset($pptpcfg['dns1']); + } else + $pptpcfg['dns1'] = $_POST['pptp_dns1']; + + if ($_POST['pptp_dns2'] == "") { + if (isset($pptpcfg['dns2'])) + unset($pptpcfg['dns2']); + } else + $pptpcfg['dns2'] = $_POST['pptp_dns2']; + + if($_POST['req128'] == "yes") + $pptpcfg['req128'] = true; + else if (isset($pptpcfg['req128'])) + unset($pptpcfg['req128']); + + if($_POST['radiusenable'] == "yes") + $pptpcfg['radius']['server']['enable'] = true; + else if (isset($pptpcfg['radius']['server']['enable'])) + unset($pptpcfg['radius']['server']['enable']); + + if($_POST['radiussecenable'] == "yes") + $pptpcfg['radius']['server2']['enable'] = true; + else if (isset($pptpcfg['radius']['server2']['enable'])) + unset($pptpcfg['radius']['server2']['enable']); + + if($_POST['radacct_enable'] == "yes") + $pptpcfg['radius']['accounting'] = true; + else if (isset($pptpcfg['radius']['accounting'])) + unset($pptpcfg['radius']['accounting']); + + if($_POST['radiusissueips'] == "yes") { + $pptpcfg['radius']['radiusissueips'] = true; + } else if (isset($pptpcfg['radius']['radiusissueips'])) + unset($pptpcfg['radius']['radiusissueips']); + + write_config(); + + $retval = 0; + $retval = vpn_pptpd_configure(); + $savemsg = get_std_save_message($retval); + + filter_configure(); + } +} + +$pgtitle = array(gettext("VPN"),gettext("VPN PPTP")); +$shortcut_section = "pptps"; +include("head.inc"); + +?> + +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<script type="text/javascript"> +//<![CDATA[ +function get_radio_value(obj) +{ + for (i = 0; i < obj.length; i++) { + if (obj[i].checked) + return obj[i].value; + } + return null; +} + +function enable_change(enable_over) { + if ((get_radio_value(document.iform.mode) == "server") || enable_over) { + document.iform.remoteip.disabled = 0; + document.iform.localip.disabled = 0; + document.iform.req128.disabled = 0; + document.iform.radiusenable.disabled = 0; + document.iform.radiusissueips.disabled = 0; + document.iform.wins.disabled = 0; + document.iform.n_pptp_units.disabled = 0; + document.iform.pptp_dns1.disabled = 0; + document.iform.pptp_dns2.disabled = 0; + + if (document.iform.radiusenable.checked || enable_over) { + document.iform.radiussecenable.disabled = 0; + document.iform.radacct_enable.disabled = 0; + document.iform.radiusserver.disabled = 0; + document.iform.radiusserverport.disabled = 0; + document.iform.radiusserveracctport.disabled = 0; + document.iform.radiussecret.disabled = 0; + document.iform.radius_nasip.disabled = 0; + document.iform.radius_acct_update.disabled = 0; + document.iform.radiusissueips.disabled = 0; + if (document.iform.radiussecenable.checked || enable_over) { + document.iform.radiusserver2.disabled = 0; + document.iform.radiussecret2.disabled = 0; + document.iform.radiusserver2port.disabled = 0; + document.iform.radiusserver2acctport.disabled = 0; + } else { + + document.iform.radiusserver2.disabled = 1; + document.iform.radiussecret2.disabled = 1; + document.iform.radiusserver2port.disabled = 1; + document.iform.radiusserver2acctport.disabled = 1; + } + } else { + document.iform.radacct_enable.disabled = 1; + document.iform.radiusserver.disabled = 1; + document.iform.radiusserverport.disabled = 1; + document.iform.radiusissueips.disabled = 1; + document.iform.radiusserveracctport.disabled = 1; + document.iform.radiussecret.disabled = 1; + document.iform.radius_nasip.disabled = 1; + document.iform.radius_acct_update.disabled = 1; + document.iform.radiusissueips.disabled = 1; + document.iform.radiusserver2.disabled = 1; + document.iform.radiussecret2.disabled = 1; + document.iform.radiusserver2port.disabled = 1; + document.iform.radiusserver2acctport.disabled = 1; + } + + } else { + document.iform.remoteip.disabled = 1; + document.iform.localip.disabled = 1; + document.iform.req128.disabled = 1; + document.iform.n_pptp_units.disabled = 1; + document.iform.pptp_dns1.disabled = 1; + document.iform.pptp_dns2.disabled = 1; + document.iform.radiusenable.disabled = 1; + document.iform.radacct_enable.disabled = 1; + document.iform.radiusserver.disabled = 1; + document.iform.radiusserverport.disabled = 1; + document.iform.radiusissueips.disabled = 1; + document.iform.radiusserveracctport.disabled = 1; + document.iform.radiussecret.disabled = 1; + document.iform.radius_nasip.disabled = 1; + document.iform.radius_acct_update.disabled = 1; + document.iform.radiussecenable.disabled = 1; + document.iform.radiusserver2.disabled = 1; + document.iform.radiusserver2port.disabled = 1; + document.iform.radiusserver2acctport.disabled = 1; + document.iform.radiussecret2.disabled = 1; + document.iform.wins.disabled = 1; + document.iform.radiusissueips.disabled = 1; + } + if ((get_radio_value(document.iform.mode) == "redir") || enable_over) { + document.iform.redir.disabled = 0; + } else { + document.iform.redir.disabled = 1; + } +} +//]]> +</script> +<form action="vpn_pptp.php" method="post" name="iform" id="iform"> +<?php if ($input_errors) print_input_errors($input_errors); ?> +<?php if ($savemsg) print_info_box($savemsg); ?> +<?php print_info_box(gettext("PPTP is no longer considered a secure VPN technology because it relies upon MS-CHAPv2 which has been compromised. If you continue to use PPTP be aware that intercepted traffic can be decrypted by a third party, so it should be considered unencrypted. We advise migrating to another VPN type such as OpenVPN or IPsec.<br /><br /><a href=\"https://isc.sans.edu/diary/End+of+Days+for+MS-CHAPv2/13807\">Read More</a>")); ?> +<table width="100%" border="0" cellpadding="0" cellspacing="0" summary="vpn pptp"> + <tr><td class="tabnavtbl"> +<?php + $tab_array = array(); + $tab_array[0] = array(gettext("Configuration"), true, "vpn_pptp.php"); + $tab_array[1] = array(gettext("Users"), false, "vpn_pptp_users.php"); + display_top_tabs($tab_array); +?> + </td></tr> + <tr> + <td> +<div id="mainarea"> + <table class="tabcont" width="100%" border="0" cellpadding="6" cellspacing="0" summary="main area"> + <tr> + <td width="22%" valign="top" class="vtable"> </td> + <td width="78%" class="vtable"> + <input name="mode" type="radio" onclick="enable_change(false)" value="off" + <?php if (($pconfig['mode'] != "server") && ($pconfig['mode'] != "redir")) echo "checked=\"checked\"";?> /> + <?=gettext("Off"); ?></td> + </tr> + <tr> + <td width="22%" valign="top" class="vtable"> </td> + <td width="78%" class="vtable"> + + <input type="radio" name="mode" value="redir" onclick="enable_change(false)" <?php if ($pconfig['mode'] == "redir") echo "checked=\"checked\"" ?> /> + <?=gettext("Redirect incoming PPTP connections to");?>:</td> + </tr> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("PPTP redirection");?></td> + <td width="78%" class="vtable"> + <?=$mandfldhtml;?><input name="redir" type="text" class="formfld unknown" id="redir" size="20" value="<?=htmlspecialchars($pconfig['redir']);?>" /> + <br /> + <?=gettext("Enter the IP address of a host which will accept incoming " . + "PPTP connections"); ?>.</td> + </tr> + <tr> + <td width="22%" valign="top" class="vtable"> </td> + <td width="78%" class="vtable"> + <input type="radio" name="mode" value="server" onclick="enable_change(false)" <?php if ($pconfig['mode'] == "server") echo "checked=\"checked\""; ?> /> + <?=gettext("Enable PPTP server"); ?></td> + </tr> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("No. PPTP users"); ?></td> + <td width="78%" class="vtable"> + <select id="n_pptp_units" name="n_pptp_units"> + <?php + $toselect = ($pconfig['n_pptp_units'] > 0) ? $pconfig['n_pptp_units'] : 16; + for($x=1; $x<255; $x++) { + if($x == $toselect) + $SELECTED = " selected=\"selected\""; + else + $SELECTED = ""; + echo "<option value=\"{$x}\"{$SELECTED}>{$x}</option>\n"; + } + ?> + </select> + <br /><?=gettext("Hint: 10 is ten PPTP clients"); ?> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Server address"); ?></td> + <td width="78%" class="vtable"> + <?=$mandfldhtml;?><input name="localip" type="text" class="formfld unknown" id="localip" size="20" value="<?=htmlspecialchars($pconfig['localip']);?>" /> + <br /> + <?=gettext("Enter the IP address the PPTP server should give to clients for use as their \"gateway\""); ?>. + <br /> + <?=gettext("Typically this is set to an unused IP just outside of the client range"); ?>. + <br /> + <br /> + <?=gettext("NOTE: This should NOT be set to any IP address currently in use on this firewall"); ?>.</td> + </tr> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Remote address " . + "range"); ?></td> + <td width="78%" class="vtable"> + <?=$mandfldhtml;?><input name="remoteip" type="text" class="formfld unknown" id="remoteip" size="20" value="<?=htmlspecialchars($pconfig['remoteip']);?>" /> + <br /> + <?=gettext("Specify the starting address for the client IP subnet"); ?>.<br /> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("PPTP DNS Servers"); ?></td> + <td width="78%" class="vtable"> + <?=$mandfldhtml;?><input name="pptp_dns1" type="text" class="formfld unknown" id="pptp_dns1" size="20" value="<?=htmlspecialchars($pconfig['pptp_dns1']);?>" /> + <br /> + <input name="pptp_dns2" type="text" class="formfld unknown" id="pptp_dns2" size="20" value="<?=htmlspecialchars($pconfig['pptp_dns2']);?>" /> + <br /> + <?=gettext("primary and secondary DNS servers assigned to PPTP clients"); ?><br /> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("WINS Server"); ?></td> + <td width="78%" valign="top" class="vtable"> + <input name="wins" class="formfld unknown" id="wins" size="20" value="<?=htmlspecialchars($pconfig['wins']);?>" /> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("RADIUS"); ?></td> + <td width="78%" class="vtable"> + <input name="radiusenable" type="checkbox" id="radiusenable" onclick="enable_change(false)" value="yes" <?php if ($pconfig['radiusenable']) echo "checked=\"checked\""; ?> /> + <strong><?=gettext("Use a RADIUS server for authentication"); ?></strong><br /> + <?=gettext("When set, all users will be authenticated using " . + "the RADIUS server specified below. The local user database " . + "will not be used"); ?>.<br /> + <br /> + <input name="radacct_enable" type="checkbox" id="radacct_enable" onclick="enable_change(false)" value="yes" <?php if ($pconfig['radacct_enable']) echo "checked=\"checked\""; ?> /> + <strong><?=gettext("Enable RADIUS accounting"); ?> <br /> + </strong><?=gettext("Sends accounting packets to the RADIUS server"); ?>.<br /> + <br /> + <input name="radiussecenable" type="checkbox" id="radiussecenable" onclick="enable_change(false)" value="yes" <?php if ($pconfig['radiussecenable']) echo "checked=\"checked\""; ?> /> + <strong><?=gettext("Secondary RADIUS server for failover authentication"); ?></strong><br /> + <?=gettext("When set, all requests will go to the secondary server when primary fails"); ?><br /> + <br /> + <input name="radiusissueips" value="yes" type="checkbox" class="formfld" id="radiusissueips"<?php if($pconfig['radiusissueips']) echo " checked=\"checked\""; ?> /> + <strong><?=gettext("RADIUS issued IPs"); ?></strong> + <br /><?=gettext("Issue IP addresses via RADIUS server"); ?>. + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("RADIUS NAS IP"); ?></td> + <td width="78%" valign="top" class="vtable"> + <input name="radius_nasip" class="formfld unknown" id="radius_nasip" size="20" value="<?=htmlspecialchars($pconfig['radius_nasip']);?>" /> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("RADIUS Accounting Update"); ?></td> + <td width="78%" valign="top" class="vtable"> + <input name="radius_acct_update" class="formfld unknown" id="radius_acct_update" size="20" value="<?=htmlspecialchars($pconfig['radius_acct_update']);?>" /> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("RADIUS Server"); ?> </td> + <td width="78%" class="vtable"> + <input name="radiusserver" type="text" class="formfld unknown" id="radiusserver" size="20" value="<?=htmlspecialchars($pconfig['radiusserver']);?>" /> + <input name="radiusserverport" type="text" class="formfld unknown" id="radiusserverport" size="4" value="<?=htmlspecialchars($pconfig['radiusserverport']);?>" /> + <input name="radiusserveracctport" type="text" class="formfld unknown" id="radiusserveracctport" size="4" value="<?=htmlspecialchars($pconfig['radiusserveracctport']);?>" /> + <br /> + <?=gettext("Enter the IP address, RADIUS port, and RADIUS accounting port of the RADIUS server"); ?>.</td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("RADIUS shared secret"); ?></td> + <td width="78%" valign="top" class="vtable"> + <input name="radiussecret" type="password" class="formfld pwd" id="radiussecret" size="20" value="<?=htmlspecialchars($pconfig['radiussecret']);?>" /> + <br /> + <?=gettext("Enter the shared secret that will be used to authenticate " . + "to the RADIUS server"); ?>.</td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Secondary RADIUS server"); ?> </td> + <td width="78%" class="vtable"> + <input name="radiusserver2" type="text" class="formfld unknown" id="radiusserver2" size="20" value="<?=htmlspecialchars($pconfig['radiusserver2']);?>" /> + <input name="radiusserver2port" type="text" class="formfld unknown" id="radiusserver2port" size="4" value="<?=htmlspecialchars($pconfig['radiusserver2port']);?>" /> + <input name="radiusserver2acctport" type="text" class="formfld unknown" id="radiusserver2acctport" size="4" value="<?=htmlspecialchars($pconfig['radiusserver2acctport']);?>" /> + <br /> + <?=gettext("Enter the IP address, RADIUS port, and RADIUS accounting port of the RADIUS server"); ?>.</td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("Secondary RADIUS shared secret"); ?></td> + <td width="78%" valign="top" class="vtable"> + <input name="radiussecret2" type="password" class="formfld pwd" id="radiussecret2" size="20" value="<?=htmlspecialchars($pconfig['radiussecret2']);?>" /> + <br /> + <?=gettext("Enter the shared secret that will be used to authenticate " . + "to the secondary RADIUS server"); ?>.</td> + </tr> + <tr> + <td height="16" colspan="2" valign="top"></td> + </tr> + <tr> + <td width="22%" valign="middle"> </td> + <td width="78%" class="vtable"> + <input name="req128" type="checkbox" id="req128" value="yes" <?php if ($pconfig['req128']) echo "checked=\"checked\""; ?> /> + <strong><?=gettext("Require 128-bit encryption"); ?></strong><br /> + <?=gettext("When set, only 128-bit encryption will be accepted. Otherwise " . + "40-bit and 56-bit encryption will be accepted as well. Note that " . + "encryption will always be forced on PPTP connections (i.e. " . + "unencrypted connections will not be accepted)"); ?>.</td> + </tr> + <tr> + <td width="22%" valign="top"> </td> + <td width="78%"> + <input name="Submit" type="submit" class="formbtn" value="<?=gettext("Save"); ?>" onclick="enable_change(true)" /> + </td> + </tr> + <tr> + <td width="22%" valign="top"> </td> + <td width="78%"><span class="vexpl"><span class="red"><strong><?=gettext("Note");?>:<br /> + </strong></span><?=gettext("don't forget to ");?><a href="firewall_rules.php?if=pptp"><?=gettext("add a firewall rule"); ?></a> <?=gettext("to permit ". + "traffic from PPTP clients");?>!</span></td> + </tr> + </table> +</div> + </td> + </tr> +</table> +</form> +<script type="text/javascript"> +//<![CDATA[ +enable_change(false); +//]]> +</script> +<?php include("fend.inc"); ?> +</body> +</html> diff --git a/src/usr/local/www/vpn_pptp_users.php b/src/usr/local/www/vpn_pptp_users.php new file mode 100644 index 0000000..da62e6a --- /dev/null +++ b/src/usr/local/www/vpn_pptp_users.php @@ -0,0 +1,147 @@ +<?php + +/* + WARNING: DEPRICATED! SHOULD NOT BE CONVERTED. SEE https://github.com/SjonHortensius/pfsense/issues/229 +*/ + +/* + vpn_pptp_users.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-vpnpptp-users +##|*NAME=VPN: VPN PPTP: Users page +##|*DESCR=Allow access to the 'VPN: VPN PPTP: Users' page. +##|*MATCH=vpn_pptp_users.php* +##|-PRIV + +require("guiconfig.inc"); +require_once("vpn.inc"); + +if (!is_array($config['pptpd']['user'])) { + $config['pptpd']['user'] = array(); +} +$a_secret = &$config['pptpd']['user']; + +if ($_POST) { + + $pconfig = $_POST; + + if ($_POST['apply']) { + $retval = 0; + $retval = vpn_setup(); + $savemsg = get_std_save_message($retval); + if ($retval == 0) { + if (is_subsystem_dirty('pptpusers')) + clear_subsystem_dirty('pptpusers'); + } + } +} + +if ($_GET['act'] == "del") { + if ($a_secret[$_GET['id']]) { + unset($a_secret[$_GET['id']]); + write_config(); + mark_subsystem_dirty('pptpusers'); + header("Location: vpn_pptp_users.php"); + exit; + } +} + +$pgtitle = array(gettext("VPN"),gettext("VPN PPTP"),gettext("Users")); +$shortcut_section = "pptps"; +include("head.inc"); + +?> + +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<form action="vpn_pptp_users.php" method="post"> +<?php if ($savemsg) print_info_box($savemsg); ?> +<?php if (isset($config['pptpd']['radius']['enable'])) + print_info_box(gettext("Warning: RADIUS is enabled. The local user database will not be used.")); ?> +<?php if (is_subsystem_dirty('pptpusers')): ?><br/> +<?php print_info_box_np(gettext("The PPTP user list has been modified").".<br />".gettext("You must apply the changes in order for them to take effect").".<br /></b><b>".gettext("Warning: this will terminate all current PPTP sessions")."!");?><br /> +<?php endif; ?> +<table width="100%" border="0" cellpadding="0" cellspacing="0" summary="vpn pptp users"> + <tr><td class="tabnavtbl"> +<?php + $tab_array = array(); + $tab_array[0] = array(gettext("Configuration"), false, "vpn_pptp.php"); + $tab_array[1] = array(gettext("Users"), true, "vpn_pptp_users.php"); + display_top_tabs($tab_array); +?> </td></tr> + <tr> + <td> + <div id="mainarea"> + <table class="tabcont" width="100%" border="0" cellpadding="6" cellspacing="0" summary="main area"> + <tr> + <td class="listhdrr"><?=gettext("Username");?></td> + <td class="listhdr"><?=gettext("IP address");?></td> + <td class="list"> + <table border="0" cellspacing="0" cellpadding="1" summary="add"> + <tr> + <td width="17"></td> + <td><a href="vpn_pptp_users_edit.php"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" title="<?=gettext("add user");?>" width="17" height="17" border="0" alt="add" /></a></td> + </tr> + </table> + </td> + </tr> + <?php $i = 0; foreach ($a_secret as $secretent): ?> + <tr> + <td class="listlr"> + <?=htmlspecialchars($secretent['name']);?> + </td> + <td class="listr"> + <?=htmlspecialchars($secretent['ip']);?> + </td> + <td class="list nowrap"><a href="vpn_pptp_users_edit.php?id=<?=$i;?>"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_e.gif" title="<?=gettext("edit user");?>" width="17" height="17" border="0" alt="edit" /></a> + <a href="vpn_pptp_users.php?act=del&id=<?=$i;?>" onclick="return confirm('<?=gettext("Do you really want to delete this user?");?>')"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_x.gif" title="<?=gettext("delete user");?>" width="17" height="17" border="0" alt="delete" /></a></td> + </tr> + <?php $i++; endforeach; ?> + <tr> + <td class="list" colspan="2"></td> + <td class="list"> + <table border="0" cellspacing="0" cellpadding="1" summary="add"> + <tr> + <td width="17"></td> + <td><a href="vpn_pptp_users_edit.php"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_plus.gif" title="<?=gettext("add user");?>" width="17" height="17" border="0" alt="add" /></a></td> + </tr> + </table> + </td> + </tr> + </table> +</div> + </td> + </tr> +</table> +</form> +<?php include("fend.inc"); ?> +</body> +</html> diff --git a/src/usr/local/www/vpn_pptp_users_edit.php b/src/usr/local/www/vpn_pptp_users_edit.php new file mode 100644 index 0000000..5c5d787 --- /dev/null +++ b/src/usr/local/www/vpn_pptp_users_edit.php @@ -0,0 +1,189 @@ +<?php + +/* + WARNING: DEPRICATED! SHOULD NOT BE CONVERTED. SEE https://github.com/SjonHortensius/pfsense/issues/229 +*/ + +/* + vpn_pptp_users_edit.php + part of m0n0wall (http://m0n0.ch/wall) + part of pfSense + + Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-vpn-vpnpptp-user-edit +##|*NAME=VPN: VPN PPTP: User: Edit page +##|*DESCR=Allow access to the 'VPN: VPN PPTP: User: Edit' page. +##|*MATCH=vpn_pptp_users_edit.php* +##|-PRIV + +function pptpusercmp($a, $b) { + return strcasecmp($a['name'], $b['name']); +} + +function pptpd_users_sort() { + global $config; + + if (!is_array($config['ppptpd']['user'])) + return; + + usort($config['pptpd']['user'], "pptpusercmp"); +} + +require("guiconfig.inc"); +require_once("vpn.inc"); + +if (!is_array($config['pptpd']['user'])) { + $config['pptpd']['user'] = array(); +} +$a_secret = &$config['pptpd']['user']; + +if (is_numericint($_GET['id'])) + $id = $_GET['id']; +if (isset($_POST['id']) && is_numericint($_POST['id'])) + $id = $_POST['id']; + +if (isset($id) && $a_secret[$id]) { + $pconfig['username'] = $a_secret[$id]['name']; + $pconfig['ip'] = $a_secret[$id]['ip']; +} + +if ($_POST) { + + unset($input_errors); + $pconfig = $_POST; + + /* input validation */ + if (isset($id) && ($a_secret[$id])) { + $reqdfields = explode(" ", "username"); + $reqdfieldsn = array(gettext("Username")); + } else { + $reqdfields = explode(" ", "username passwordfld1"); + $reqdfieldsn = array(gettext("Username"),gettext("Password")); + } + + do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + + if (preg_match("/[^a-zA-Z0-9\.\-_]/", $_POST['username'])) + $input_errors[] = gettext("The username contains invalid characters."); + + if (preg_match("/^!/", $_POST['passwordfld1'])) + $input_errors[] = gettext("The password cannot start with '!'."); + + if (!preg_match("/^[\x20-\x7E]*$/", $_POST['passwordfld1'])) + $input_errors[] = gettext("The password contains invalid characters."); + + if (($_POST['passwordfld1']) && ($_POST['passwordfld1'] != $_POST['passwordfld2'])) { + $input_errors[] = gettext("The passwords do not match."); + } + if (($_POST['ip'] && !is_ipaddr($_POST['ip']))) { + $input_errors[] = gettext("The IP address entered is not valid."); + } + + if (!$input_errors && !(isset($id) && $a_secret[$id])) { + /* make sure there are no dupes */ + foreach ($a_secret as $secretent) { + if ($secretent['name'] == $_POST['username']) { + $input_errors[] = gettext("Another entry with the same username already exists."); + break; + } + } + } + + if (!$input_errors) { + + if (isset($id) && $a_secret[$id]) + $secretent = $a_secret[$id]; + + $secretent['name'] = $_POST['username']; + $secretent['ip'] = $_POST['ip']; + + if ($_POST['passwordfld1']) + $secretent['password'] = $_POST['passwordfld1']; + + if (isset($id) && $a_secret[$id]) + $a_secret[$id] = $secretent; + else + $a_secret[] = $secretent; + pptpd_users_sort(); + + write_config(); + mark_subsystem_dirty('pptpusers'); + + header("Location: vpn_pptp_users.php"); + exit; + } +} + +$pgtitle = array(gettext("VPN"),gettext("VPN PPTP"),gettext("User"),gettext("Edit")); +$shortcut_section = "pptps"; +include("head.inc"); + +?> +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<?php if ($input_errors) print_input_errors($input_errors); ?> + <form action="vpn_pptp_users_edit.php" method="post" name="iform" id="iform"> + <div id="mainarea"> + <table width="100%" border="0" cellpadding="6" cellspacing="0" summary="vpn pptp users edit"> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Username");?></td> + <td width="78%" class="vtable"> + <?=$mandfldhtml;?><input name="username" type="text" class="formfld user" id="username" size="20" value="<?=htmlspecialchars($pconfig['username']);?>" /> + </td> + </tr> + <tr> + <td width="22%" valign="top" class="vncellreq"><?=gettext("Password");?></td> + <td width="78%" class="vtable"> + <?=$mandfldhtml;?><input name="passwordfld1" type="password" class="formfld pwd" id="passwordfld1" size="20" /> + <br /><?=$mandfldhtml;?><input name="passwordfld2" type="password" class="formfld pwd" id="passwordfld2" size="20" /> + (<?=gettext("confirmation");?>)<?php if (isset($id) && $a_secret[$id]): ?><br /> + <span class="vexpl"><?=gettext("If you want to change the users' password, ". + "enter it here twice.");?></span><?php endif; ?></td> + </tr> + <tr> + <td width="22%" valign="top" class="vncell"><?=gettext("IP address");?></td> + <td width="78%" class="vtable"> + <input name="ip" type="text" class="formfld unknown" id="ip" size="20" value="<?=htmlspecialchars($pconfig['ip']);?>" /> + <br /><span class="vexpl"><?=gettext("If you want the user to be assigned a specific IP address, enter it here.");?></span></td> + </tr> + <tr> + <td class="vncell" width="22%" valign="top"> </td> + <td class="vncell" width="78%"> + <input name="Submit" type="submit" class="formbtn" value="<?=gettext("Save");?>" /> + <?php if (isset($id) && $a_secret[$id]): ?> + <input name="id" type="hidden" value="<?=htmlspecialchars($id);?>" /> + <?php endif; ?> + </td> + </tr> + </table> + </div> +</form> +<?php include("fend.inc"); ?> +</body> +</html> diff --git a/src/usr/local/www/widgets/include/captiveportal.inc b/src/usr/local/www/widgets/include/captiveportal.inc new file mode 100644 index 0000000..3714209 --- /dev/null +++ b/src/usr/local/www/widgets/include/captiveportal.inc @@ -0,0 +1,4 @@ +<?php +$captive_portal_status_title = "Captive Portal Status"; +$captive_portal_status_title_link = "status_captiveportal.php"; +?> diff --git a/src/usr/local/www/widgets/include/carp_status.inc b/src/usr/local/www/widgets/include/carp_status.inc new file mode 100644 index 0000000..79d3c03 --- /dev/null +++ b/src/usr/local/www/widgets/include/carp_status.inc @@ -0,0 +1,7 @@ +<?php + +//set variable for custom title +$carp_status_title = "Carp Status"; +$carp_status_title_link = "carp_status.php"; + +?> diff --git a/src/usr/local/www/widgets/include/dyn_dns_status.inc b/src/usr/local/www/widgets/include/dyn_dns_status.inc new file mode 100644 index 0000000..8116fe7 --- /dev/null +++ b/src/usr/local/www/widgets/include/dyn_dns_status.inc @@ -0,0 +1,7 @@ +<?php + +//set variable for custom title +$dyn_dns_status_title = "Dyn DNS Status"; +$dyn_dns_status_title_link = "services_dyndns.php"; + +?> diff --git a/src/usr/local/www/widgets/include/gateways.inc b/src/usr/local/www/widgets/include/gateways.inc new file mode 100644 index 0000000..4666689 --- /dev/null +++ b/src/usr/local/www/widgets/include/gateways.inc @@ -0,0 +1,5 @@ +<?php +//set variable for custom title +$gateways_title = "Gateways"; +$gateways_title_link = "status_gateways.php"; +?> diff --git a/src/usr/local/www/widgets/include/gmirror_status.inc b/src/usr/local/www/widgets/include/gmirror_status.inc new file mode 100644 index 0000000..6547d4d --- /dev/null +++ b/src/usr/local/www/widgets/include/gmirror_status.inc @@ -0,0 +1,4 @@ +<?php +$gmirror_status_title = "GEOM Mirror Status"; +$gmirror_status_title_link = "diag_gmirror.php"; +?>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/include/installed_packages.inc b/src/usr/local/www/widgets/include/installed_packages.inc new file mode 100644 index 0000000..a978191 --- /dev/null +++ b/src/usr/local/www/widgets/include/installed_packages.inc @@ -0,0 +1,7 @@ +<?php + +//set variable for custom title +$installed_packages_title = "Installed Packages"; +$installed_packages_title_link = "pkg_mgr_installed.php"; + +?>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/include/interface_statistics.inc b/src/usr/local/www/widgets/include/interface_statistics.inc new file mode 100644 index 0000000..c789418 --- /dev/null +++ b/src/usr/local/www/widgets/include/interface_statistics.inc @@ -0,0 +1,5 @@ +<?php +//set variable for custom title +$interface_statistics_title = "Interface Statistics"; +$interface_statistics_title_link = "status_interfaces.php"; +?>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/include/interfaces.inc b/src/usr/local/www/widgets/include/interfaces.inc new file mode 100644 index 0000000..6c19a6b --- /dev/null +++ b/src/usr/local/www/widgets/include/interfaces.inc @@ -0,0 +1,6 @@ +<?php +//set variable for custom title +$interfaces_title = "Interfaces"; +$interfaces_title_link = "status_interfaces.php"; + +?>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/include/ipsec.inc b/src/usr/local/www/widgets/include/ipsec.inc new file mode 100644 index 0000000..6df0a7c --- /dev/null +++ b/src/usr/local/www/widgets/include/ipsec.inc @@ -0,0 +1,4 @@ +<?php +$ipsec_title = "IPsec"; +$ipsec_title_link = "diag_ipsec.php"; +?> diff --git a/src/usr/local/www/widgets/include/load_balancer.inc b/src/usr/local/www/widgets/include/load_balancer.inc new file mode 100644 index 0000000..367ea46 --- /dev/null +++ b/src/usr/local/www/widgets/include/load_balancer.inc @@ -0,0 +1,4 @@ +<?php +$load_balancer_status_title = "Load Balancer Status"; +$load_balancer_status_title_link = "status_lb_pool.php"; +?> diff --git a/src/usr/local/www/widgets/include/log.inc b/src/usr/local/www/widgets/include/log.inc new file mode 100644 index 0000000..7ad453a --- /dev/null +++ b/src/usr/local/www/widgets/include/log.inc @@ -0,0 +1,6 @@ +<?php +//set variable for custom title +$log_title = "Firewall Logs"; +$log_title_link = "diag_logs_filter.php"; + +?>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/include/ntp_status.inc b/src/usr/local/www/widgets/include/ntp_status.inc new file mode 100644 index 0000000..1115095 --- /dev/null +++ b/src/usr/local/www/widgets/include/ntp_status.inc @@ -0,0 +1,5 @@ +<?php +//set variable for custom title +$ntp_status_title = "NTP Status"; +$ntp_status_title_link = "status_ntpd.php"; +?> diff --git a/src/usr/local/www/widgets/include/openvpn.inc b/src/usr/local/www/widgets/include/openvpn.inc new file mode 100644 index 0000000..075d0e5 --- /dev/null +++ b/src/usr/local/www/widgets/include/openvpn.inc @@ -0,0 +1,4 @@ +<?php +$openvpn_title = "OpenVPN"; +$openvpn_title_link = "status_openvpn.php"; +?>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/include/services_status.inc b/src/usr/local/www/widgets/include/services_status.inc new file mode 100644 index 0000000..685aee4 --- /dev/null +++ b/src/usr/local/www/widgets/include/services_status.inc @@ -0,0 +1,7 @@ +<?php + +//set variable for custom title +$services_status_title = "Services Status"; +$services_status_title_link = "status_services.php"; + +?>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/include/smart_status.inc b/src/usr/local/www/widgets/include/smart_status.inc new file mode 100644 index 0000000..bbfa274 --- /dev/null +++ b/src/usr/local/www/widgets/include/smart_status.inc @@ -0,0 +1,5 @@ +<?php +//set variable for custom title +$smart_status_title = "SMART Status"; +$smart_status_title_link = "diag_smart.php"; +?> diff --git a/src/usr/local/www/widgets/include/thermal_sensors.inc b/src/usr/local/www/widgets/include/thermal_sensors.inc new file mode 100644 index 0000000..a6e727a --- /dev/null +++ b/src/usr/local/www/widgets/include/thermal_sensors.inc @@ -0,0 +1,27 @@ +<?php +/* + $Id: thermal_sensors.inc + File location: + \usr\local\www\widgets\include\ + + Used by: + \usr\local\www\widgets\widgets\thermal_sensors.widget.php + + +*/ + +//set variable for custom title +$thermal_sensors_widget_title = "Thermal Sensors"; +//$thermal_sensors_widget_link = "thermal_sensors.php"; + + +//returns core temp data (from coretemp.ko or amdtemp.ko driver) as "|"-delimited string. +//NOTE: depends on proper config in System >> Advanced >> Miscellaneous tab >> Thermal Sensors section. +function getThermalSensorsData() { + + $_gb = exec("/sbin/sysctl -a | grep temperature", $dfout); + $thermalSensorsData = join("|", $dfout); + return $thermalSensorsData; + +} +?> diff --git a/src/usr/local/www/widgets/include/traffic_graph.inc b/src/usr/local/www/widgets/include/traffic_graph.inc new file mode 100644 index 0000000..3901db6 --- /dev/null +++ b/src/usr/local/www/widgets/include/traffic_graph.inc @@ -0,0 +1,4 @@ +<?php +$traffic_graphs_title = "Traffic Graphs"; +$traffic_graphs_title_link = "status_graph.php"; +?> diff --git a/src/usr/local/www/widgets/include/wake_on_lan.inc b/src/usr/local/www/widgets/include/wake_on_lan.inc new file mode 100644 index 0000000..af3229c --- /dev/null +++ b/src/usr/local/www/widgets/include/wake_on_lan.inc @@ -0,0 +1,7 @@ +<?php + +//set variable for custom title +$wake_on_lan_title = "Wake On Lan"; +$wake_on_lan_title_link = "services_wol.php"; + +?>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/javascript/cpu_graphs.js b/src/usr/local/www/widgets/javascript/cpu_graphs.js new file mode 100644 index 0000000..1fc690a --- /dev/null +++ b/src/usr/local/www/widgets/javascript/cpu_graphs.js @@ -0,0 +1,245 @@ +/****************************************************************************** + $Id: graphlink.js,v 1.1 2006/12/21 17:10:25 dberlin Exp $ + + This file is part of the GraphLink software. + GraphLink is distributed under the MIT License. + Copyright (C) 2005-2006 Max Khitrov <max@mxsoft.org> + ******************************************************************************/ + +/***** Global data ************************************************************/ + +var gl_graphCount = 0; // Number of graphs on the current page + +/***** Constants **************************************************************/ + +var GL_START = 0; +var GL_END = 1; +var GL_STATIC = 0; +var GL_DYNAMIC = 1; + +/***** Public functions *******************************************************/ + +/** + * Creates a graph and returns the graph data structure which can later be + * manipulated using the other graph functions. + * + * element_id - DOM element id (should be a DIV) that will contain the graph. + * width - The width of the graph in pixels. + * height - Height of the graph in pixels. + * bar_width - Width of each bar on the graph. This number should divide width + * evenly, or else width will be adjusted to meet this requirement. + * General formula to keep in mind: + * Smaller bar width = more bars = higher CPU usage on client-side. + * + * Returns graph data structure on success, false on error. + */ +function GraphInitialize(element_id, width, height, bar_width) { + // Find the page element which will contain the graph + var owner; + if((owner = jQuery('#' + element_id)) == null) { + alert("GraphLink Error: Element ID '" + element_id + "' not found."); + return false; + } + + // Make sure width is divisible by bar_width + if(width / bar_width != Math.floor(width / bar_width)) + width = Math.floor(width / bar_width) * bar_width; + + var bar_count = width / bar_width; + + // Create the graph data structure + var graph = new Array(); + graph['id'] = gl_graphCount; // ID used to separate elements of one graph from those of another + graph['width'] = width; // Graph width + graph['height'] = height; // Graph height + graph['bar_count'] = bar_count; // Number of bars on the graph + graph['scale_type'] = GL_STATIC; // How the graph is scaled + graph['scale'] = 1; // Multiplier for the bar height + graph['max'] = 0; // Largest value currently on the graph + graph['vmax'] = height; // Virtual graph maximum + graph['spans'] = new Array(bar_count); // References to all the spans for each graph + graph['vals'] = new Array(bar_count); // The height of each bar on the graph, actually it's (graph height - bar height) + gl_graphCount++; + + // Build the graph (x)html + var graph_html = ''; + graph_html += '<div id="GraphLinkData' + graph['id'] + '" class="GraphLinkData">'; + + for(var i = 0; i < bar_count; i++) { + graph['vals'][i] = height; + graph_html += '<span id="GraphLinkBar' + graph['id'] + '_' + i + '" class="GraphLinkBar"></span>'; + } + + graph_html += '</div>'; + owner.html(graph_html); + graph['element_id'] = jQuery('#GraphLinkData' + graph['id']); + + for(i = 0; i < bar_count; i++) { + graph['spans'][i] = jQuery('#GraphLinkBar' + graph['id'] + '_' + i); + graph['spans'][i].css('width',bar_width + 'px'); + graph['spans'][i].css('margin-top',height + 'px'); + } + + return graph; +} + +/** + * Adds a new value to a graph. + * + * graph - Graph object to which to add the new value. + * value - Value to add. + * where - (optional) GL_START (0) or GL_END (1), depending on where you want + * the new value to appear. GL_START will add the value on the left + * of the graph, GL_END will add it on the right (default). + */ +function GraphValue(graph, value, where) { + if(typeof(where) == 'undefined') + where = GL_END; + + var rescale = false; + var lost = 0; + + if(value < 0) + value = 0; + + if(graph['scale_type'] == GL_DYNAMIC && value > graph['max']) + rescale = true; + + if(graph['scale_type'] == GL_STATIC) { + if(value > graph['vmax']) + value = graph['vmax']; + value = Math.round(value * graph['scale']); + } + + if(where == GL_START) { + graph['vals'].unshift(graph['height'] - value); + lost = graph['vals'].pop(); + } + else { + graph['vals'].push(graph['height'] - value); + lost = graph['vals'].shift(); + } + + if(graph['scale_type'] == GL_DYNAMIC && (graph['height'] - lost) == graph['max']) + rescale = true; + + if(rescale) + GraphAdjustScale(graph) + + GraphDraw(graph); +} + +/** + * Sets a virtual maximum for the graph allowing you to have non-scaled graphs + * that can show a value greater then the graph height. This function will + * automatically set the graph to a static scale mode, meaning that no values + * above the maximum will be permitted. If you need to have a graph with no + * pre-defined maximum, make it dynamic. Also note that if you set a vmax on a + * graph that has data larger than vmax, that data will be reduced. + * + * graph - Graph object for which to set virtual max. + * vmax - The virtual maximum value for the graph. + */ +function GraphSetVMax(graph, vmax) { + graph['scale_type'] = GL_STATIC; + graph['vmax'] = vmax; + + GraphAdjustScale(graph); + GraphDraw(graph); +} + +/** + * This function instructs the graph to be scaled according to what the maximum + * value is. That value is used as the graph maximum and is reevaluated whenever + * a new value is added, or the current maximum is removed. Dynamic scaling is a + * good way of showing data for which you don't know what the maximum will be, + * but it also is a bit more resource-intensive then statically scaled graphs. + * + * graph - Graph object for which to enable dynamic scaling. + */ +function GraphDynamicScale(graph) { + graph['scale_type'] = GL_DYNAMIC; + + GraphAdjustScale(graph); + GraphDraw(graph); +} + +/***** Private functions ******************************************************/ + +/** + * Checks if the current scale of the graph is still valid, or needs to be + * adjusted. + * + * graph - Graph object for which to check the scale. + */ +function GraphAdjustScale(graph) { + var limit = graph['bar_count']; + var new_max = 0; + var new_scale = 0; + var val = 0; + + if(graph['scale_type'] == GL_STATIC) { + new_max = graph['vmax']; + new_scale = graph['height'] / new_max; + + if(new_scale == graph['scale']) + return; + } + + for(var i = 0; i < limit; i++) { + if(graph['scale_type'] == GL_STATIC) { + val = (graph['height'] - graph['vals'][i]) * graph['scale']; + val = val * new_scale; + + if(val > new_max) + val = new_max; + + graph['vals'][i] = graph['height'] - Math.round(val * new_scale); + + } + else if((graph['height'] - graph['vals'][i]) > new_max) { + new_max = graph['height'] - graph['vals'][i]; + } + } + + + if(graph['scale_type'] == GL_STATIC) { + graph['scale'] = new_scale; + } + else { + if(new_max == 0) + graph['scale'] = 1; + else + graph['scale'] = graph['height'] / new_max; + + graph['max'] = new_max; + } +} + +/** + * Redraws the graph on the screen. + * + * graph - Graph object which needs to be re-drawn. + */ +function GraphDraw(graph) { + var count = graph['bar_count']; + + if(graph['scale_type'] == GL_STATIC) + var getMargin = function(i) { + return graph['vals'][i] + 'px'; + }; + else + var getMargin = function(i) { + var h = graph['height']; + var s = graph['scale']; + var v = graph['vals'][i]; + return (h - Math.round((h - v) * s)) + 'px'; + }; + + graph['spans'][count - 1].css("display", "none"); + + for(var i = 0; i < count; i++) + graph['spans'][i].css("marginTop", getMargin(i)); + +// jQuery('#' + graph['spans'][count - 1]).fadeIn(500); +} diff --git a/src/usr/local/www/widgets/javascript/ipsec.js b/src/usr/local/www/widgets/javascript/ipsec.js new file mode 100644 index 0000000..d38f6cd --- /dev/null +++ b/src/usr/local/www/widgets/javascript/ipsec.js @@ -0,0 +1,9 @@ +function updateIpsec() { + selectIntLink = "ipsecDetailed"; + ipsecsettings = "ipsecDetail="; + ipsecsettings += d.getElementById(selectIntLink).checked; + + selectIntLink = "ipsec-config"; + textlink = d.getElementById(selectIntLink); + textlink.value = ipsecsettings; +}
\ No newline at end of file diff --git a/src/usr/local/www/widgets/javascript/thermal_sensors.js b/src/usr/local/www/widgets/javascript/thermal_sensors.js new file mode 100644 index 0000000..7415b01 --- /dev/null +++ b/src/usr/local/www/widgets/javascript/thermal_sensors.js @@ -0,0 +1,296 @@ +/* + $Id: thermal_sensors.js + Description: + Javascript functions to get and show thermal sensors data in thermal_sensors.widget.php. + NOTE: depends on proper config in System >> Advanced >> Miscellaneous tab >> Thermal Sensors section. + File location: + \usr\local\www\widgets\javascript\ + Used by: + \usr\local\www\widgets\widgets\thermal_sensors.widget.php + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + + + +//should be called from "thermal_sensors.widget.php" +function showThermalSensorsData() { + + //get data from thermal_sensors.widget.php + url = "/widgets/widgets/thermal_sensors.widget.php?getThermalSensorsData=1" + //IE fix to disable cache when using http:// , just append timespan + + new Date().getTime(); + + jQuery.ajax(url, { + type: 'get', + success: function(data) { + var thermalSensorsData = data || ""; + buildThermalSensorsData(thermalSensorsData); + }, + error: function(jqXHR, status, error) { + buildThermalSensorsDataRaw( + "Error getting data from [thermal_sensors.widget.php] - |" + + "status: [" + (status || "") + "]|" + + "error: [" + (error || "") + "]"); + } + }); + + //call itself in 11 seconds + window.setTimeout(showThermalSensorsData, 11000); +} + +function buildThermalSensorsData(thermalSensorsData) { + //NOTE: variable thermal_sensors_widget_showRawOutput is declared/set in "thermal_sensors.widget.php" + if (thermal_sensors_widget_showRawOutput) { + buildThermalSensorsDataRaw(thermalSensorsData); + } else { + buildThermalSensorsDataGraph(thermalSensorsData); + } +} + +function buildThermalSensorsDataRaw(thermalSensorsData) { + + var thermalSensorsContent = ""; + + if (thermalSensorsData && thermalSensorsData != "") { + thermalSensorsContent = thermalSensorsData.replace(/\|/g, "<br />"); + //rawData = thermalSensorsData.split("|").join("<br />"); + } + + loadThermalSensorsContainer(thermalSensorsContent); +} + +function loadThermalSensorsContainer (thermalSensorsContent) { + + if (thermalSensorsContent && thermalSensorsContent != "") { + //load generated graph (or raw data) into thermalSensorsContainer (thermalSensorsContainer DIV defined in "thermal_sensors.widget.php") + jQuery('#thermalSensorsContainer').html(thermalSensorsContent); + } else { + jQuery('#thermalSensorsContainer').html("No Thermal Sensors data available.<br /><br />"); + jQuery('<div/>').html( + "<span>* You can configure a proper Thermal Sensor / Module under <br />" + + " <a href='system_advanced_misc.php'>System > Advanced > Miscellaneous : Thermal Sensors section</a>.</span>" + ).appendTo('#thermalSensorsContainer'); + } +} + +function buildThermalSensorsDataGraph(thermalSensorsData) { + + //local constants + var normalColor = "LimeGreen"; + var normalColorShadowTop = "Lime"; + var normalColorShadowBottom = "Green"; + + var warningColor = "Orange"; + var warningColorShadowBottom = "Chocolate"; + + var criticalColor = "Red"; + var criticalColorShadowBottom = "DarkRed"; + + //local variables + var barBgColor = normalColor; //green/normal as default + var barBgColorShadowTop = normalColorShadowTop; //green/normal as default + var barBgColorShadowBottom = normalColorShadowBottom; //green/normal as default + + var thermalSensorsArray = new Array(); + + if (thermalSensorsData && thermalSensorsData != "") { + thermalSensorsArray = thermalSensorsData.split("|"); + } + + var thermalSensorsHTMLContent = ""; + var itemsToPulsate = new Array(); + + //generate graph for each temperature sensor and append to thermalSensorsHTMLContent string + for (var i = 0; i < thermalSensorsArray.length; i++) { + + var sensorDataArray = thermalSensorsArray[i].split(":"); + var sensorName = sensorDataArray[0].trim(); + var thermalSensorValue = getThermalSensorValue(sensorDataArray[1]); + + var pulsateTimes = 0; + var pulsateDuration = 0; + + var warningTempThresholdPosition = 0; + var criticalTempThresholdPosition = 0; + + //NOTE: the following variables are declared/set in "thermal_sensors.widget.php": + // thermal_sensors_widget_coreWarningTempThreshold, thermal_sensors_widget_coreCriticalTempThreshold, + // thermal_sensors_widget_zoneWarningTempThreshold, thermal_sensors_widget_zoneCriticalTempThreshold + // thermal_sensors_widget_pulsateWarning, thermal_sensors_widget_pulsateCritical + + //set graph color and pulsate parameters + if (sensorName.indexOf("cpu") > -1) { //check CPU Threshold config settings + + warningTempThresholdPosition = thermal_sensors_widget_coreWarningTempThreshold; + criticalTempThresholdPosition = thermal_sensors_widget_coreCriticalTempThreshold; + + if (thermalSensorValue < thermal_sensors_widget_coreWarningTempThreshold) { + barBgColor = normalColor; + barBgColorShadowTop = normalColorShadowTop; + barBgColorShadowBottom = normalColorShadowBottom; + pulsateTimes = 0; + pulsateDuration = 0; + } else if (thermalSensorValue >= thermal_sensors_widget_coreWarningTempThreshold && thermalSensorValue < thermal_sensors_widget_coreCriticalTempThreshold) { + barBgColor = warningColor; + barBgColorShadowTop = warningColor; + barBgColorShadowBottom = warningColorShadowBottom; + pulsateTimes = thermal_sensors_widget_pulsateWarning ? 4 : 0; + pulsateDuration = thermal_sensors_widget_pulsateWarning ? 900 : 0; + } else { // thermalSensorValue > thermal_sensors_widget_coreCriticalTempThreshold + barBgColor = criticalColor; + barBgColorShadowTop = criticalColor; + barBgColorShadowBottom = criticalColorShadowBottom; + pulsateTimes = thermal_sensors_widget_pulsateCritical ? 7 : 0; + pulsateDuration = thermal_sensors_widget_pulsateCritical ? 900 : 0; + } + } else { //assuming sensor is for a zone, check Zone Threshold config settings + + warningTempThresholdPosition = thermal_sensors_widget_zoneWarningTempThreshold; + criticalTempThresholdPosition = thermal_sensors_widget_zoneCriticalTempThreshold; + + if (thermalSensorValue < thermal_sensors_widget_zoneWarningTempThreshold) { + + barBgColor = normalColor; + barBgColorShadowTop = normalColorShadowTop; + barBgColorShadowBottom = normalColorShadowBottom; + pulsateTimes = 0; + pulsateDuration = 0; + + } else if (thermalSensorValue >= thermal_sensors_widget_zoneWarningTempThreshold && + thermalSensorValue < thermal_sensors_widget_zoneCriticalTempThreshold) { + + barBgColor = warningColor; + barBgColorShadowTop = warningColor; + barBgColorShadowBottom = warningColorShadowBottom; + pulsateTimes = thermal_sensors_widget_pulsateWarning ? 4 : 0; + pulsateDuration = thermal_sensors_widget_pulsateWarning ? 900 : 0; + + } else { // thermalSensorValue > thermal_sensors_widget_zoneCriticalTempThreshold + + barBgColor = criticalColor; + barBgColorShadowTop = criticalColor; + barBgColorShadowBottom = criticalColorShadowBottom; + pulsateTimes = thermal_sensors_widget_pulsateCritical ? 7 : 0; + pulsateDuration = thermal_sensors_widget_pulsateCritical ? 900 : 0; + } + } + + //NOTE: variable thermal_sensors_widget_showFullSensorName is declared/set in "thermal_sensors.widget.php" + if (!thermal_sensors_widget_showFullSensorName) { + sensorName = getSensorFriendlyName(sensorName); + } + + //build temperature item/row for a sensor + //NOTE: additional styles are set in 'thermal_sensors.widget.php' + var thermalSensorRow = "<div class='thermalSensorRow' id='thermalSensorRow" + i + "' >" + + //sensor name and temperature value + " <div class='thermalSensorTextShell'><div class='thermalSensorText' id='thermalSensorText" + i + "'>" + sensorName + ": </div><div class='thermalSensorValue' id='thermalSensorValue" + i + "'>" + thermalSensorValue + " °C</div></div>" + + //temperature bar + " <div class='thermalSensorBarShell' id='thermalSensorBarShell" + i + "' >" + + " <div class='thermalSensorBar' id='thermalSensorBar" + i + "' style='background-color: " + barBgColor + "; border-top-color: " + barBgColorShadowTop + "; border-bottom-color: " + barBgColorShadowBottom + "; width:" + thermalSensorValue + "%;' ></div>" + + //threshold targets (warning and critical) + " <div class='thermalSensorWarnThresh' id='thermalSensorWarnThresh" + i + "' style='left:" + warningTempThresholdPosition + "%;' ></div>" + + " <div class='thermalSensorCritThresh' id='thermalSensorCritThresh" + i + "' style='left:" + criticalTempThresholdPosition + "%;' ></div>" + + //temperature scale (max 100 C) + " <div class='thermal_sensors_widget_scale000'></div>" + + " <div class='thermal_sensors_widget_scale010'></div>" + + " <div class='thermal_sensors_widget_scale020'></div>" + + " <div class='thermal_sensors_widget_scale030'></div>" + + " <div class='thermal_sensors_widget_scale040'></div>" + + " <div class='thermal_sensors_widget_scale050'></div>" + + " <div class='thermal_sensors_widget_scale060'></div>" + + " <div class='thermal_sensors_widget_scale070'></div>" + + " <div class='thermal_sensors_widget_scale080'></div>" + + " <div class='thermal_sensors_widget_scale090'></div>" + + " <div class='thermal_sensors_widget_scale100'></div>" + + " <div class='thermal_sensors_widget_mark100'>100°</div>" + + " </div>" + + "</div>"; + + //collect parameters for warning/critical items we need to pulsate + if (pulsateTimes > 0) { + var params = i + "|" + barBgColor + "|" + pulsateTimes + "|" + pulsateDuration; + itemsToPulsate.push(params); + } + + //append HTML item + thermalSensorsHTMLContent = thermalSensorsHTMLContent + thermalSensorRow; + } + + //load generated graph into thermalSensorsContainer (DIV defined in "thermal_sensors.widget.php") + loadThermalSensorsContainer(thermalSensorsHTMLContent); + + if (itemsToPulsate.length > 0) { + //pulsate/flash warning/critical items we collected + pulsateThermalSensorsItems(itemsToPulsate); + } +} + +function pulsateThermalSensorsItems(itemsToPulsate) { + + //pulsate/flash warning/critical items we collected + for (var i = 0; i < itemsToPulsate.length; i++) { + + var pulsateParams = itemsToPulsate[i].split("|"); + var rowNum = parseInt(pulsateParams[0]); + //var textColor = pulsateParams[1]; + var pulsateTimes = parseInt(pulsateParams[2]); + var pulsateDuration = parseInt(pulsateParams[3]); + + //pulsate temp Value + var divThermalSensorValue = jQuery("#thermalSensorValue" + rowNum); //get temp value by id + divThermalSensorValue.effect("pulsate", { + times: pulsateTimes, + easing: 'linear' //'easeInExpo' + }, pulsateDuration); + ////set Temp Value color + //divThermalSensorValue.css({ color: textColor }); + + //pulsate temp Bar + var divThermalSensorBar = jQuery("#thermalSensorBar" + rowNum); //get temp bar by id + divThermalSensorBar.effect("pulsate", { + times: pulsateTimes, + easing: 'linear' //'easeInExpo' + }, pulsateDuration); + + } +} + +function getSensorFriendlyName(sensorFullName) { + var rzone = /^hw\.acpi\.thermal\.tz([0-9]+)\.temperature$/; + var rcore = /^dev\.cpu\.([0-9]+)\.temperature$/; + + if (rzone.test(sensorFullName)) { + return "Zone " + rzone.exec(sensorFullName)[1]; + } + + if (rcore.test(sensorFullName)) { + return "Core " + rcore.exec(sensorFullName)[1]; + } + + return sensorFullName; +} + +function getThermalSensorValue(stringValue) { + return (+parseFloat(stringValue) || 0).toFixed(1); +} diff --git a/src/usr/local/www/widgets/javascript/traffic_graph.js b/src/usr/local/www/widgets/javascript/traffic_graph.js new file mode 100644 index 0000000..383a549 --- /dev/null +++ b/src/usr/local/www/widgets/javascript/traffic_graph.js @@ -0,0 +1,41 @@ +function trafficshowDiv(incDiv,ifDescription,refreshIntervalSec,swapButtons) { + // put the graph object HTML in the element and make it appear + selectedDiv = incDiv + "graphdiv"; + jQuery('#' + selectedDiv).html( + '<object data="graph.php?ifnum=' + incDiv + '&ifname=' + ifDescription + '&timeint=' + refreshIntervalSec + '&initdelay=0" height="100%" width="100%">' + + '<param name="id" value="graph" />' + + '<param name="type" value="image/svg+xml" />' + + '<param name="pluginspage" value="http://www.adobe.com/svg/viewer/install/auto" />' + + '</object>'); + jQuery('#' + selectedDiv).effect('blind',{mode:'show'},1000); + d = document; + if (swapButtons) { + selectIntLink = selectedDiv + "-min"; + textlink = d.getElementById(selectIntLink); + textlink.style.display = "inline"; + + selectIntLink = selectedDiv + "-open"; + textlink = d.getElementById(selectIntLink); + textlink.style.display = "none"; + } + document.traffic_graphs_widget_iform["shown[" + incDiv + "]"].value = "show"; +} + +function trafficminimizeDiv(incDiv,swapButtons) { + // remove the graph object HTML from the element (so it does not keep using CPU) and fade + selectedDiv = incDiv + "graphdiv"; + jQuery('#' + selectedDiv).html(''); + jQuery('#' + selectedDiv).effect('blind',{mode:'hide'},1000); + d = document; + if (swapButtons) { + selectIntLink = selectedDiv + "-open"; + textlink = d.getElementById(selectIntLink); + textlink.style.display = "inline"; + + selectIntLink = selectedDiv + "-min"; + textlink = d.getElementById(selectIntLink); + textlink.style.display = "none"; + } + document.traffic_graphs_widget_iform["shown[" + incDiv + "]"].value = "hide"; +} + diff --git a/src/usr/local/www/widgets/widgets/captive_portal_status.widget.php b/src/usr/local/www/widgets/widgets/captive_portal_status.widget.php new file mode 100644 index 0000000..4f067a0 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/captive_portal_status.widget.php @@ -0,0 +1,140 @@ +<?php +/* + captive_portal_status.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Copyright (C) 2007 Sam Wenham + All rights reserved. + + status_captiveportal.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("globals.inc"); +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); +require_once("captiveportal.inc"); + +?> + +<?php + +if (!is_array($config['captiveportal'])) { + $config['captiveportal'] = array(); +} +$a_cp =& $config['captiveportal']; + +$cpzone = $_GET['zone']; +if (isset($_POST['zone'])) { + $cpzone = $_POST['zone']; +} + +if (isset($cpzone) && !empty($cpzone) && isset($a_cp[$cpzone]['zoneid'])) { + $cpzoneid = $a_cp[$cpzone]['zoneid']; +} + +if (($_GET['act'] == "del") && !empty($cpzone) && isset($cpzoneid)) { + captiveportal_disconnect_client($_GET['id']); +} +unset($cpzone); + +flush(); + +function clientcmp($a, $b) { + global $order; + return strcmp($a[$order], $b[$order]); +} + +$cpdb_all = array(); + +$showact = isset($_GET['showact']) ? 1 : 0; + +foreach ($a_cp as $cpzone => $cp) { + $cpdb = captiveportal_read_db(); + foreach ($cpdb as $cpent) { + $cpent[10] = $cpzone; + if ($showact == 1) { + $cpent[11] = captiveportal_get_last_activity($cpent[2], $cpentry[3]); + } + $cpdb_all[] = $cpent; + } +} + +if ($_GET['order']) { + if ($_GET['order'] == "ip") { + $order = 2; + } else if ($_GET['order'] == "mac") { + $order = 3; + } else if ($_GET['order'] == "user") { + $order = 4; + } else if ($_GET['order'] == "lastact") { + $order = 5; + } else if ($_GET['order'] == "zone") { + $order = 10; + } else { + $order = 0; + } + usort($cpdb_all, "clientcmp"); +} +?> +<table class="table"> + <thead> + <tr> + <th><a href="?order=ip&showact=<?=$showact;?>">IP address</a></td> + <th><a href="?order=mac&showact=<?=$showact;?>">MAC address</a></td> + <th><a href="?order=user&showact=<?=$showact;?>"><?=gettext("Username");?></a></td> +<?php if ($showact == 1): ?> + <th><a href="?order=start&showact=<?=$showact;?>"><?=gettext("Session start");?></a></td> + <th><a href="?order=start&showact=<?=$showact;?>"><?=gettext("Last activity");?></a></td> +<?php endif; ?> + </tr> + </thead> + <tbody> +<?php foreach ($cpdb_all as $cpent): ?> + <tr> + <td><?=$cpent[2];?></td> + <td><?=$cpent[3];?></td> + <td><?=$cpent[4];?></td> +<?php if ($showact == 1): ?> + <td><?=date("m/d/Y H:i:s", $cpent[0]);?></td> + <td><?php if ($cpent[11] && ($cpent[11] > 0)) echo date("m/d/Y H:i:s", $cpent[11]);?></td> +<?php endif; ?> + <td> + <a href="?order=<?=htmlspecialchars($_GET['order']);?>&showact=<?=$showact;?>&act=del&zone=<?=$cpent[10];?>&id=<?=$cpent[5];?>" class="btn btn-xs btn-danger"> + delete + </a> + </td> + </tr> +<?php +endforeach; +?> + </tbody> +</table>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/widgets/carp_status.widget.php b/src/usr/local/www/widgets/widgets/carp_status.widget.php new file mode 100644 index 0000000..a44ddd8 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/carp_status.widget.php @@ -0,0 +1,86 @@ +<?php +/* + $Id$ + carp_status.widget.php + Copyright (C) 2007 Sam Wenham + All rights reserved. + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); +require_once("/usr/local/www/widgets/include/carp_status.inc"); + +$carp_enabled = get_carp_status(); + +?> +<table> +<?php + if (is_array($config['virtualip']['vip'])) { + $carpint=0; + foreach ($config['virtualip']['vip'] as $carp) { + if ($carp['mode'] != "carp") { + continue; + } + $ipaddress = $carp['subnet']; + $password = $carp['password']; + $netmask = $carp['subnet_bits']; + $vhid = $carp['vhid']; + $advskew = $carp['advskew']; + $status = get_carp_interface_status("_vip{$carp['uniqid']}"); +?> +<tr> + <td> + <i class="icon icon-inbox"></i> + <a href="/system_hasync.php"> + <?=htmlspecialchars(convert_friendly_interface_to_friendly_descr($carp['interface']) . "@{$vhid}");?> + </a> + </td> + <td> +<?php + if ($carp_enabled == false) { + $status = "DISABLED"; + echo "<img src='/themes/".$g['theme']."/images/icons/icon_block.gif' title=\"$status\" alt=\"$status\" />"; + } else { + if($status == "MASTER") { + echo "<img src='/themes/".$g['theme']."/images/icons/icon_pass.gif' title=\"$status\" alt=\"$status\" />"; + } else if($status == "BACKUP") { + echo "<img src='/themes/".$g['theme']."/images/icons/icon_pass_d.gif' title=\"$status\" alt=\"$status\" />"; + } else if($status == "INIT") { + echo "<img src='/themes/".$g['theme']."/images/icons/icon_log.gif' title=\"$status\" alt=\"$status\" />"; + } + } + if ($ipaddress){ ?> + <?=htmlspecialchars($status);?> + <?=htmlspecialchars($ipaddress);}?> +</td></tr><?php } + } else { ?> + <tr><td>No CARP Interfaces Defined. Click <a href="carp_status.php">here</a> to configure CARP.</td></tr> +<?php } ?> +</table> diff --git a/src/usr/local/www/widgets/widgets/deactivated/cpu_graphs.widget.php b/src/usr/local/www/widgets/widgets/deactivated/cpu_graphs.widget.php new file mode 100644 index 0000000..09723d7 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/deactivated/cpu_graphs.widget.php @@ -0,0 +1,76 @@ +<?php +/* + $Id$ + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Copyright 2007 Scott Dale + Part of pfSense widgets (https://www.pfsense.org) + originally based on m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2004-2005 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net> + and Jonathan Watt <jwatt@jwatt.org>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); +?> +<link href="/themes/<?=$g['theme'];?>/graphlink.css" rel="stylesheet" type="text/css" /> +<script src="/widgets/javascript/cpu_graphs.js" type="text/javascript"></script> +<script type="text/javascript"> + /* initialize the graph */ + // --- Global Data --- // + var graphs; // An array that stores all created graphs + var graph_dir; // The direction in which each graph moves + var last_val; // An array of values for each graph + var last_val_span; // References to Last Value span tags for each graph + var pause; // Controls execution + + var ajaxStarted = false; + + /** + * Launches the GraphLink demo. It initializes the graph along with the ajax + * engine and starts the main execution loop. + */ + graph = new Array(); + graph_dir = new Array(); + last_val = new Array(); + last_val_span = new Array(); +</script> +<div style='display: block; margin-left: auto; margin-right: auto' class="GraphLink" id="GraphOutput"></div> +<script type="text/javascript"> + + // Graph 1 + graph[0] = GraphInitialize('GraphOutput', 200, 50, 4); + graph_dir[0] = GL_END; + last_val[0] = Math.floor(Math.random() * 50); + last_val_span[0] = document.getElementById('LastValue0'); + + GraphSetVMax(graph[0], 100); + GraphDynamicScale(graph[0]); + +</script> diff --git a/src/usr/local/www/widgets/widgets/dyn_dns_status.widget.php b/src/usr/local/www/widgets/widgets/dyn_dns_status.widget.php new file mode 100644 index 0000000..7839e6e --- /dev/null +++ b/src/usr/local/www/widgets/widgets/dyn_dns_status.widget.php @@ -0,0 +1,174 @@ +<?php +/* + Original status page code from: services_dyndns.php + Copyright (C) 2008 Ermal Luçi + Edits to convert it to a widget: dyn_dns_status.widget.php + Copyright (C) 2013 Stanley P. Miller \ stan-qaz + All rights reserved. + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INClUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/bin/host + pfSense_MODULE: dyndns +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); +require_once("/usr/local/www/widgets/include/dyn_dns_status.inc"); + +if (!is_array($config['dyndnses']['dyndns'])) { + $config['dyndnses']['dyndns'] = array(); +} + +$a_dyndns = &$config['dyndnses']['dyndns']; + +if($_REQUEST['getdyndnsstatus']) { + $first_entry = true; + foreach ($a_dyndns as $dyndns) { + if ($first_entry) { + $first_entry = false; + } else { + // Put a vertical bar delimiter between the echoed HTML for each entry processed. + echo "|"; + } + + $filename = "{$g['conf_path']}/dyndns_{$dyndns['interface']}{$dyndns['type']}" . escapeshellarg($dyndns['host']) . "{$dyndns['id']}.cache"; + if (file_exists($filename)) { + $ipaddr = dyndnsCheckIP($dyndns['interface']); + $cached_ip_s = explode(':', file_get_contents($filename)); + $cached_ip = $cached_ip_s[0]; + if ($ipaddr <> $cached_ip) { + echo "<font color='red'>"; + } else { + echo "<font color='green'>"; + } + echo htmlspecialchars($cached_ip); + echo "</font>"; + } else { + echo "N/A " . date("H:i:s"); + } + } + exit; +} + +?> + +<table> + <tr> + <td width="5%" class="listhdrr"><?=gettext("Int.");?></td> + <td width="15%" class="listhdrr"><?=gettext("Service");?></td> + <td width="20%" class="listhdrr"><?=gettext("Hostname");?></td> + <td width="20%" class="listhdrr"><?=gettext("Cached IP");?></td> + </tr> + <?php $i = 0; foreach ($a_dyndns as $dyndns): ?> + <tr ondblclick="document.location='services_dyndns_edit.php?id=<?=$i;?>'"> + <td class="listlr"> + <?php $iflist = get_configured_interface_with_descr(); + foreach ($iflist as $if => $ifdesc) { + if ($dyndns['interface'] == $if) { + if (!isset($dyndns['enable'])) { + echo "<span class=\"gray\">{$ifdesc}</span>"; + } else { + echo "{$ifdesc}"; + } + break; + } + } + $groupslist = return_gateway_groups_array(); + foreach ($groupslist as $if => $group) { + if ($dyndns['interface'] == $if) { + if (!isset($dyndns['enable'])) { + echo "<span class=\"gray\">{$if}</span>"; + } else { + echo "{$if}"; + } + break; + } + } + ?> + </td> + <td class="listr"> + <?php + $types = explode(",", DYNDNS_PROVIDER_DESCRIPTIONS); + $vals = explode(" ", DYNDNS_PROVIDER_VALUES); + for ($j = 0; $j < count($vals); $j++) { + if ($vals[$j] == $dyndns['type']) { + if (!isset($dyndns['enable'])) { + echo "<span class=\"gray\">".htmlspecialchars($types[$j])."</span>"; + } else { + echo htmlspecialchars($types[$j]); + } + break; + } + } + ?> + </td> + <td class="listr"> + <?php + if (!isset($dyndns['enable'])) { + echo "<span class=\"gray\">".htmlspecialchars($dyndns['host'])."</span>"; + } else { + echo htmlspecialchars($dyndns['host']); + } + ?> + </td> + <td class="listr"> + <div id='dyndnsstatus<?= $i;?>'><?= gettext("Checking ...");?></div> + </td> + </tr> + <?php $i++; endforeach;?> +</table> +<script type="text/javascript"> +//<![CDATA[ + function dyndns_getstatus() { + scroll(0,0); + var url = "/widgets/widgets/dyn_dns_status.widget.php"; + var pars = 'getdyndnsstatus=yes'; + jQuery.ajax( + url, + { + type: 'get', + data: pars, + complete: dyndnscallback + }); + // Refresh the status every 5 minutes + setTimeout('dyndns_getstatus()', 5*60*1000); + } + function dyndnscallback(transport) { + // The server returns a string of statuses separated by vertical bars + var responseStrings = transport.responseText.split("|"); + for (var count=0; count<responseStrings.length; count++) { + var divlabel = '#dyndnsstatus' + count; + jQuery(divlabel).prop('innerHTML',responseStrings[count]); + } + } + // Do the first status check 2 seconds after the dashboard opens + setTimeout('dyndns_getstatus()', 2000); +//]]> +</script> diff --git a/src/usr/local/www/widgets/widgets/gateways.widget.php b/src/usr/local/www/widgets/widgets/gateways.widget.php new file mode 100644 index 0000000..7087cf2 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/gateways.widget.php @@ -0,0 +1,122 @@ +<?php +/* + gateways.widget.php + Copyright 2008 Seth Mos + Part of pfSense widgets (https://www.pfsense.org) + originally based on m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); +require_once("/usr/local/www/widgets/include/gateways.inc"); + +if ($_POST) { + if (!is_array($config["widgets"]["gateways_widget"])) { + $config["widgets"]["gateways_widget"] = array(); + } + if (isset($_POST["display_type"])) { + $config["widgets"]["gateways_widget"]["display_type"] = $_POST["display_type"]; + } + write_config("Updated gateways widget settings via dashboard."); + header("Location: /"); + exit(0); +} + +if (isset($config["widgets"]["gateways_widget"]["display_type"])) { + $display_type = $config["widgets"]["gateways_widget"]["display_type"]; +} else { + $display_type = "gw_ip"; +} + +$a_gateways = return_gateways_array(); +$gateways_status = array(); +$gateways_status = return_gateways_status(true); + +?> +<table class="table table-striped table-hover"> +<thead> +<tr> + <th>Name</td> + <th>RTT</td> + <th>Loss</td> + <th>Status</td> +</tr> +</thead> +<tbody> +<?php foreach ($a_gateways as $gname => $gateway): ?> + <tr> + <td> +<?php + $if_gw = ''; + if (is_ipaddr($gateway['gateway'])) + $if_gw = $gateway['gateway']; + else { + if($gateway['ipprotocol'] == "inet") + $if_gw = get_interface_gateway($gateway['friendlyiface']); + if($gateway['ipprotocol'] == "inet6") + $if_gw = get_interface_gateway_v6($gateway['friendlyiface']); + } +?> + <?=htmlspecialchars($gateway['name'])?><br /> + <i><?=($if_gw == '' ? '~' : htmlspecialchars($if_gw));?></i> + </td> +<?php + if ($gateways_status[$gname]) { + if (stristr($gateways_status[$gname]['status'], "force_down")) { + $online = "Offline (forced)"; + $bgcolor = "#F08080"; // lightcoral + } elseif (stristr($gateways_status[$gname]['status'], "down")) { + $online = "Offline"; + $bgcolor = "#F08080"; // lightcoral + } elseif (stristr($gateways_status[$gname]['status'], "loss")) { + $online = "Packetloss"; + $bgcolor = "#F0E68C"; // khaki + } elseif (stristr($gateways_status[$gname]['status'], "delay")) { + $online = "Latency"; + $bgcolor = "#F0E68C"; // khaki + } elseif ($gateways_status[$gname]['status'] == "none") { + $online = "Online"; + $bgcolor = "#90EE90"; // lightgreen + } elseif ($gateways_status[$gname]['status'] == "") { + $online = "Pending"; + $bgcolor = "#D3D3D3"; // lightgray + } + } else { + $online = gettext("Unknown"); + $bgcolor = "#ADD8E6"; // lightblue + } +?> + <td><?=($gateways_status[$gname] ? htmlspecialchars($gateways_status[$gname]['delay']) : gettext("Pending"))?></td> + <td><?=($gateways_status[$gname] ? htmlspecialchars($gateways_status[$gname]['loss']) : gettext("Pending"))?></td> + <td style="background-color: <?=$bgcolor?>"><?=$online?></td> + </tr> +<?php endforeach; ?> +</tbody> +</table>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/widgets/gmirror_status.widget.php b/src/usr/local/www/widgets/widgets/gmirror_status.widget.php new file mode 100644 index 0000000..c8dad84 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/gmirror_status.widget.php @@ -0,0 +1,59 @@ +<?php +/* + gmirror_status.widget.php + Copyright (C) 2009-2010 Jim Pingle + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INClUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("gmirror.inc"); + +?> +<div id="gmirror_status"> + <?=gmirror_html_status()?> +</div> + +<script> +function gmirrorStatusUpdateFromServer(){ + $.ajax({ + type: 'get', + url: '/widgets/widgets/gmirror_status.widget.php', + dataType: 'html', + dataFilter: function(raw){ + // We reload the entire widget, strip this block of javascript from it + return raw.replace(/<script>([\s\S]*)<\/script>/gi, ''); + }, + success: function(data){ + $('#gmirror_status').html(data); + } + }); +} + +events.push(function(){ + setInterval('gmirrorStatusUpdateFromServer()', 60*1000); +}); +</script>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/widgets/installed_packages.widget.php b/src/usr/local/www/widgets/widgets/installed_packages.widget.php new file mode 100644 index 0000000..b6e01bc --- /dev/null +++ b/src/usr/local/www/widgets/widgets/installed_packages.widget.php @@ -0,0 +1,107 @@ +<?php +/* + installed_packages.widget.php + + Copyright (C) 2013-2014 Electric Sheep Fencing, LP + Copyright 2007 Scott Dale + Part of pfSense widgets (https://www.pfsense.org) + originally based on m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2004-2005 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net> + and Jonathan Watt <jwatt@jwatt.org>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); +require_once("/usr/local/www/widgets/include/installed_packages.inc"); +require_once("pkg-utils.inc"); + +if(is_array($config['installedpackages']['package'])) { + $instpkgs = array(); + foreach ($config['installedpackages']['package'] as $instpkg) + $instpkgs[ $instpkg['name'] ] = $instpkg; + ksort($instpkgs); + $currentvers = get_pkg_info(array_keys($instpkgs), array('version', 'xmlver')); +} +?> + +<?php if (empty($config['installedpackages']['package'])): ?> + <div class="alert alert-warning" role="alert"> + <strong>No packages installed.</strong> + You can install packages <a href="pkg_mgr.php" class="alert-link">here</a>. + </div> +<?php else: ?> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th>Package Name</th> + <th>Category</th> + <th>Package Version</th> + </tr> + </thead> + <tbody> +<?php +foreach ($instpkgs as $pkgname => $pkg): + if (empty($pkgname)) + continue; + + $latest_package = $currentvers[$pkg['name']]['version']; + if ($latest_package) { + // we're running a newer version of the package + if(strcmp($pkg['version'], $latest_package) > 0) { + $status = 'Newer then available ('. $latest_package .')'; + $statusicon = 'exclamation'; + } + // we're running an older version of the package + if(strcmp($pkg['version'], $latest_package) < 0) { + $status = 'Upgrade available to '.$latest_package; + $statusicon = 'plus'; + } + // we're running the current version + if(!strcmp($pkg['version'], $latest_package)) { + $status = 'Up-to-date'; + $statusicon = 'ok'; + } + } else { + // unknown available package version + $status = 'Unknown'; + $statusicon = 'question'; + } +?> + <tr> + <td><?=$pkg['name']?></td> + <td><?=$pkg['category']?></td> + <td> + <i title="<?=$status?>" class="icon icon-<?=$statusicon?>-sign"></i> + <?=$pkg['version']?> + </td> + </tr> +<?php endforeach; ?> + </tbody> + </table> +<?php endif; ?>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/widgets/interface_statistics.widget.php b/src/usr/local/www/widgets/widgets/interface_statistics.widget.php new file mode 100644 index 0000000..166aa80 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/interface_statistics.widget.php @@ -0,0 +1,82 @@ +<?php +/* + $Id: interface_statistics.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Copyright 2007 Scott Dale + Part of pfSense widgets (https://www.pfsense.org) + originally based on m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2004-2005 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net> + and Jonathan Watt <jwatt@jwatt.org>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); +require_once("/usr/local/www/widgets/include/interface_statistics.inc"); + +$rows = array( + 'inpkts' => 'Packets In', + 'outpkts' => 'Packets Out', + 'inbytes' => 'Bytes In', + 'outbytes' => 'Bytes Out', + 'inerrs' => 'Errors In', + 'outerrs' => 'Errors Out', + 'collisions' => 'Collisions', +); +$ifdescrs = get_configured_interface_with_descr(); + +?> +<table class="table table-striped table-hover"> +<thead> + <tr> + <td></td> +<?php foreach ($ifdescrs as $ifname): ?> + <th><?=$ifname?></th> +<?php endforeach; ?> + </tr> +</thead> +<tbody> +<?php foreach ($rows as $key => $name): ?> + <tr> + <th><?=$name?></th> +<?php foreach ($ifdescrs as $ifdescr => $ifname): + $ifinfo = get_interface_info($ifdescr); + + if ($ifinfo['status'] == "down") + continue; + + $ifinfo['inbytes'] = format_bytes($ifinfo['inbytes']); + $ifinfo['outbytes'] = format_bytes($ifinfo['outbytes']); + ?> + <td><?=(isset($ifinfo[$key]) ? htmlspecialchars($ifinfo[$key]) : 'n/a')?></td> +<?php endforeach; ?> + </tr> +<?php endforeach; ?> + </tbody> +</table>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/widgets/interfaces.widget.php b/src/usr/local/www/widgets/widgets/interfaces.widget.php new file mode 100644 index 0000000..85cfda1 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/interfaces.widget.php @@ -0,0 +1,110 @@ +<?php +/* + interfaces.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Copyright 2007 Scott Dale + Part of pfSense widgets (https://www.pfsense.org) + originally based on m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2004-2005 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net> + and Jonathan Watt <jwatt@jwatt.org>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); +require_once("/usr/local/www/widgets/include/interfaces.inc"); + +$ifdescrs = get_configured_interface_with_descr(); +?> + +<table class="table table-striped table-hover"> +<?php +foreach ($ifdescrs as $ifdescr => $ifname): + $ifinfo = get_interface_info($ifdescr); + + if ($ifinfo['ppplink']) { + $icon = 'headphones'; + } else if (is_interface_wireless($ifdescr)) { + if ($ifinfo['status'] == "associated") { + $icon = 'wlan'; + } else { + $icon = 'wlan_d'; + } + } else { + $icon = 'cablenic'; + } + + if ($ifinfo['status'] == "up" || $ifinfo['status'] == "associated") { + $known_status = true; + $up_display = "inline"; + $down_display = "none"; + $block_display = "none"; + } elseif ($ifinfo['status'] == "no carrier") { + $known_status = true; + $up_display = "none"; + $down_display = "inline"; + $block_display = "none"; + } elseif ($ifinfo['status'] == "down") { + $known_status = true; + $up_display = "none"; + $down_display = "none"; + $block_display = "inline"; + } else { + $known_status = false; + } +?> + <tr> + <td title="<?=htmlspecialchars($ifinfo['macaddr'])?>"> + <i class="icon icon-<?=$icon?>"></i> + <a href="/interfaces.php?if=<?=$ifdescr?>"> + <?=htmlspecialchars($ifname);?> + </a> + </td> + <td> + <?php if (isset($status)):?> + <i class="icon icon-<?=status?>-circle" alt="<?=htmlspecialchars($ifinfo['status'])?>"></i> + <?php else: ?> + <?=htmlspecialchars($ifinfo['status'])?> + <?php endif; ?> + </td> + <td> + <?=htmlspecialchars($ifinfo['media']);?> + </td> + <td<?=($ifinfo['dhcplink'] ? ' title="via dhcp"':'')?>> + <?php if (empty($addresses)): ?> + n/a + <?php else: ?> + <?= implode('<br />', $addresses)?> + <?php endif; ?> + </td> + </tr> +<?php +endforeach; +?> +</table> diff --git a/src/usr/local/www/widgets/widgets/ipsec.widget.php b/src/usr/local/www/widgets/widgets/ipsec.widget.php new file mode 100644 index 0000000..76ed282 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/ipsec.widget.php @@ -0,0 +1,215 @@ +<?php +/* + ipsec.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Copyright 2007 Scott Dale + Part of pfSense widgets (https://www.pfsense.org) + originally based on m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2004-2005 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net> + and Jonathan Watt <jwatt@jwatt.org>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("functions.inc"); +require_once("ipsec.inc"); + +if (isset($config['ipsec']['phase1'])) { + $tab_array = array(); + $tab_array[0] = array("Overview", true, "ipsec-Overview"); + $tab_array[1] = array("Tunnels", false, "ipsec-tunnel"); + $tab_array[2] = array("Mobile", false, "ipsec-mobile"); + + display_widget_tabs($tab_array); + + $spd = ipsec_dump_spd(); + $sad = ipsec_dump_sad(); + $mobile = ipsec_dump_mobile(); + $ipsec_status = ipsec_smp_dump_status(); + + $activecounter = 0; + $inactivecounter = 0; + + if (!is_array($ipsec_status['query'])) { + $ipsec_status['query'] = array(); + $ipsec_status['query']['ikesalist'] = array(); + $ipsec_status['query']['ikesalist']['ikesa'] = array(); + } else if (!is_array($ipsec_status['query']['ikesalist'])) { + $ipsec_status['query']['ikesalist'] = array(); + $ipsec_status['query']['ikesalist']['ikesa'] = array(); + } else if (!is_array($ipsec_status['query']['ikesalist']['ikesa'])) { + $ipsec_status['query']['ikesalist']['ikesa'] = array(); + } + + $ipsec_detail_array = array(); + $ikenum = array(); + if (isset($config['ipsec']['phase2'])) { + foreach ($config['ipsec']['phase2'] as $ph2ent) { + if (!ipsec_lookup_phase1($ph2ent,$ph1ent)) { + continue; + } + + if ($ph2ent['remoteid']['type'] == "mobile" || isset($ph1ent['mobile'])) { + continue; + } + if (isset($ph1ent['disabled']) || isset($ph2ent['disabled'])) { + continue; + } + + if (empty($ph1ent['iketype']) || $ph1ent['iketype'] == 'ikev1') { + if (!isset($ikenum[$ph1ent['ikeid']])) { + $ikenum[$ph1ent['ikeid']] = 0; + } else { + $ikenum[$ph1ent['ikeid']]++; + } + $ikeid = "con{$ph1ent['ikeid']}00" . $ikenum[$ph1ent['ikeid']]; + } else { + if (isset($ikenum[$ph1ent['ikeid']])) { + continue; + } + $ikeid = "con{$ph1ent['ikeid']}"; + $ikenum[$ph1ent['ikeid']] = true; + } + + $found = false; + foreach ($ipsec_status['query']['ikesalist']['ikesa'] as $ikesa) { + if (isset($ikesa['childsalist']) && isset($ikesa['childsalist']['childsa'])) { + foreach ($ikesa['childsalist']['childsa'] as $childsa) { + if ($ikeid == $childsa['childconfig']) { + $found = true; + break; + } + } + } else if ($ikeid == $ikesa['peerconfig']) { + $found = true; + } + + if ($found === true) { + if ($ikesa['status'] == 'established') { + /* tunnel is up */ + $iconfn = "true"; + $activecounter++; + } else { + /* tunnel is down */ + $iconfn = "false"; + $inactivecounter++; + } + break; + } + } + + if ($found === false) { + /* tunnel is down */ + $iconfn = "false"; + $inactivecounter++; + } + + $ipsec_detail_array[] = array('src' => convert_friendly_interface_to_friendly_descr($ph1ent['interface']), + 'dest' => $ph1ent['remote-gateway'], + 'remote-subnet' => ipsec_idinfo_to_text($ph2ent['remoteid']), + 'descr' => $ph2ent['descr'], + 'status' => $iconfn); + } + } + unset($ikenum); +} + +if (isset($config['ipsec']['phase2'])): ?> + <table class="table"> + <thead> + <tr> + <th>Active Tunnels</td> + <th>Inactive Tunnels</td> + <th>Mobile Users</td> + </tr> + </thead> + <tbody> + <tr> + <td><?=$activecounter; ?></td> + <td><?=$inactivecounter; ?></td> + <td><?=(is_array($mobile['pool']) ? htmlspecialchars($mobile['pool'][0]['usage']) : '0'); ?></td> + </tr> + </tbody> + </table> + + <table class="table table-striped table-hover"> + <thead> + <th>Source</th> + <th>Destination</th> + <th>Description</th> + <th>Status</th> + </thead> + <tbody> + <?php foreach ($ipsec_detail_array as $ipsec) : ?> + <tr> + <td><?php echo htmlspecialchars($ipsec['src']);?></td> + <td><?php echo $ipsec['remote-subnet'];?><br />(<?php echo htmlspecialchars($ipsec['dest']);?>)</td> + <td><?php echo htmlspecialchars($ipsec['descr']);?></td> + <td> + <?php if ($ipsec['status'] == "true"): ?> + <i class="icon icon-chevron-up"></i> + <?php else: ?> + <i class="icon icon-chevron-down"></i> + <?php endif; ?> + </td> + </tr> + <?php endforeach; ?> + </tbody> + </table> + + <?php if (is_array($mobile['pool'])): ?> + <table class="table table-striped table-hover"> + <thead> + <th>User</th> + <th>IP</th> + <th>Status</th> + </thead> + <tbody> + + <?php foreach ($mobile['pool'] as $pool): + if (!is_array($pool['lease'])) + continue; + + foreach ($pool['lease'] as $muser) : ?> + <tr> + <td><?php echo htmlspecialchars($muser['id']);?></td> + <td><?php echo htmlspecialchars($muser['host']);?></td> + <td><?php echo htmlspecialchars($muser['status']);?></td> + </tr> + <?php + endforeach; + endforeach; ?> + </tbody> + </table> + <?php endif;?> +<?php else: ?> + <div class="alert alert-warning"> + <h3>There are no configured IPsec Tunnels</h3> + <p>You can configure your IPsec <a href="vpn_ipsec.php">here</a>.</p> + </div> +<?php endif; ?>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/widgets/load_balancer_status.widget.php b/src/usr/local/www/widgets/widgets/load_balancer_status.widget.php new file mode 100644 index 0000000..0613713 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/load_balancer_status.widget.php @@ -0,0 +1,156 @@ +<?php +/* + load_balancer_status.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Copyright 2010 Jim Pingle + Portions copied from status_lb_pool.php, status_lb_vs.php, and vslb.inc: + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl>. + Copyright (C) 2005-2008 Bill Marquette + + Part of pfSense widgets (https://www.pfsense.org) + originally based on m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2004-2005 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net> + and Jonathan Watt <jwatt@jwatt.org>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); +require_once("vslb.inc"); + +$now = time(); +$year = date("Y"); + +if (!is_array($config['load_balancer']['lbpool'])) { + $config['load_balancer']['lbpool'] = array(); +} +if (!is_array($config['load_balancer']['virtual_server'])) { + $config['load_balancer']['virtual_server'] = array(); +} +$a_vs = &$config['load_balancer']['virtual_server']; +$a_pool = &$config['load_balancer']['lbpool']; +$rdr_a = get_lb_redirects(); +$relay_hosts = get_lb_summary(); + +$lb_logfile = "{$g['varlog_path']}/relayd.log"; +$nentries = $config['syslog']['nentries']; +if (!$nentries) { + $nentries = 50; +} + +?> + +<table class="table"> +<thead> + <tr> + <th>Server</th> + <th>Pool</th> + <th>Description</th> + </tr> +</thead> +<tbody> + <?php foreach ($a_vs as $vsent): ?> + <tr> + <?php + switch (trim($rdr_a[$vsent['name']]['status'])) { + case 'active': + $bgcolor = "#90EE90"; // lightgreen + $rdr_a[$vsent['name']]['status'] = "Active"; + break; + case 'down': + $bgcolor = "#F08080"; // lightcoral + $rdr_a[$vsent['name']]['status'] = "Down"; + break; + default: + $bgcolor = "#D3D3D3"; // lightgray + $rdr_a[$vsent['name']]['status'] = 'Unknown - relayd not running?'; + } + ?> + <td> + <?=$vsent['name'];?><br /> + <span style="background-color: <?=$bgcolor?>; display: block"><i><?=$rdr_a[$vsent['name']]['status']?></i></span> + <?=$vsent['ipaddr'].":".$vsent['port'];?><br /> + </td> + <td> + <table> + <?php + foreach ($a_pool as $pool) { + if ($pool['name'] == $vsent['poolname']) { + $pool_hosts=array(); + foreach ((array) $pool['servers'] as $server) { + $svr['ip']['addr']=$server; + $svr['ip']['state']=$relay_hosts[$pool['name'].":".$pool['port']][$server]['state']; + $svr['ip']['avail']=$relay_hosts[$pool['name'].":".$pool['port']][$server]['avail']; + $pool_hosts[]=$svr; + } + foreach ((array) $pool['serversdisabled'] as $server) { + $svr['ip']['addr']="$server"; + $svr['ip']['state']='disabled'; + $svr['ip']['avail']='disabled'; + $pool_hosts[]=$svr; + } + asort($pool_hosts); + foreach ((array) $pool_hosts as $server) { + if(empty($server['ip']['addr'])) + continue; + + switch ($server['ip']['state']) { + case 'up': + $bgcolor = "#90EE90"; // lightgreen + $checked = "checked"; + break; + case 'disabled': + $bgcolor = "#FFFFFF"; // white + $checked = ""; + break; + default: + $bgcolor = "#F08080"; // lightcoral + $checked = "checked"; + } +?> + <tr style="background-color: <?=$bgcolor?>"> + <td><?=$server['ip']['addr']?>:<?=$pool['port']?></td> + <td> + <?php if($server['ip']['avail']): ?> + ({$server['ip']['avail']}) + <?php endif; ?> + </td> + </tr> +<?php + } + } + } +?> + </table> + </td> + <td><?=$vsent['descr'];?></td> + </tr> + <?php endforeach; ?> +</tbody> +</table>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/widgets/log.widget.php b/src/usr/local/www/widgets/widgets/log.widget.php new file mode 100644 index 0000000..00b668a --- /dev/null +++ b/src/usr/local/www/widgets/widgets/log.widget.php @@ -0,0 +1,234 @@ +<?php +/* + log.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Copyright 2007 Scott Dale + Part of pfSense widgets (https://www.pfsense.org) + originally based on m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2004-2005 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net> + and Jonathan Watt <jwatt@jwatt.org>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); + +/* In an effort to reduce duplicate code, many shared functions have been moved here. */ +require_once("filter_log.inc"); + +if (is_numeric($_POST['filterlogentries'])) { + $config['widgets']['filterlogentries'] = $_POST['filterlogentries']; + + $acts = array(); + if ($_POST['actpass']) { + $acts[] = "Pass"; + } + if ($_POST['actblock']) { + $acts[] = "Block"; + } + if ($_POST['actreject']) { + $acts[] = "Reject"; + } + + if (!empty($acts)) { + $config['widgets']['filterlogentriesacts'] = implode(" ", $acts); + } else { + unset($config['widgets']['filterlogentriesacts']); + } + unset($acts); + + if (($_POST['filterlogentriesinterfaces']) and ($_POST['filterlogentriesinterfaces'] != "All")) { + $config['widgets']['filterlogentriesinterfaces'] = trim($_POST['filterlogentriesinterfaces']); + } else { + unset($config['widgets']['filterlogentriesinterfaces']); + } + + write_config("Saved Filter Log Entries via Dashboard"); + Header("Location: /"); + exit(0); +} + +$nentries = isset($config['widgets']['filterlogentries']) ? $config['widgets']['filterlogentries'] : 5; + +//set variables for log +$nentriesacts = isset($config['widgets']['filterlogentriesacts']) ? $config['widgets']['filterlogentriesacts'] : 'All'; +$nentriesinterfaces = isset($config['widgets']['filterlogentriesinterfaces']) ? $config['widgets']['filterlogentriesinterfaces'] : 'All'; + +$filterfieldsarray = array( + "act" => $nentriesacts, + "interface" => $nentriesinterfaces +); + +$filter_logfile = "{$g['varlog_path']}/filter.log"; + +/* AJAX related routines */ +if (isset($_POST['lastsawtime'])) { + $filterlog = conv_log_filter($filter_logfile, $nentries, $nentries + 20); + + foreach ($filterlog as $idx => $row) { + if (strtotime($log_row['time']) <= $_POST['lastsawtime']) + unset($filterlog[$idx]); + } +} +else + $filterlog = conv_log_filter($filter_logfile, $nentries, 50, $filterfieldsarray); +?> +<script> + var logWidgetLastRefresh = <?=time()?>; +</script> + +<table class="table table-striped table-hover"> + <thead> + <tr> + <th><?=gettext("Act");?></th> + <th><?=gettext("Time");?></th> + <th><?=gettext("IF");?></th> + <th><?=gettext("Source");?></th> + <th><?=gettext("Destination");?></th> + </tr> + </thead> + <tbody> +<?php + foreach ($filterlog as $filterent): + if ($filterent['version'] == '6') { + $srcIP = "[" . htmlspecialchars($filterent['srcip']) . "]"; + $dstIP = "[" . htmlspecialchars($filterent['dstip']) . "]"; + } else { + $srcIP = htmlspecialchars($filterent['srcip']); + $dstIP = htmlspecialchars($filterent['dstip']); + } + + if ($filterent['act'] == "block") + $iconfn = "remove"; + else if ($filterent['act'] == "reject") + $iconfn = "fire"; + else if ($filterent['act'] == "match") + $iconfn = "filter"; + else + $iconfn = "ok"; + + $rule = find_rule_by_number($filterent['rulenum'], $filterent['tracker'], $filterent['act']); +?> + <tr> + <td><a role="button" data-toggle="popover" data-trigger="hover" + data-title="Rule that triggered this action" + data-content="<?=htmlspecialchars($rule)?>"> <i + class="icon icon-<?=$iconfn?>"></i> + </a></td> + <td title="<?=htmlspecialchars($filterent['time'])?>"><?=substr(htmlspecialchars($filterent['time']),0,-3)?></td> + <td><?=htmlspecialchars($filterent['interface']);?></td> + <td><a href="diag_dns.php?host=<?=$filterent['srcip']?>" + title="<?=gettext("Reverse Resolve with DNS")?>"><?=$srcIP?></a>:<?=htmlspecialchars($filterent['srcport'])?> + </td> + <td><a href="diag_dns.php?host=<?=$filterent['dstip']?>" + title="<?=gettext("Reverse Resolve with DNS");?>"><?=$dstIP?></a>:<?=htmlspecialchars($filterent['dstport'])?> + </td> + </tr> + <?php + endforeach; + ?> + </tbody> +</table> + +<?php + +/* for AJAX response, we only need the panel-body */ +if (isset($_GET['lastsawtime'])) + exit; +?> + +<script> +function logWidgetUpdateFromServer(){ + $.ajax({ + type: 'get', + url: '/widgets/widgets/log.widget.php', + data: 'lastsawtime='+logWidgetLastRefresh, + dataFilter: function(raw){ + // We reload the entire widget, strip this block of javascript from it + return raw.replace(/<script>([\s\S]*)<\/script>/gi, ''); + }, + dataType: 'html', + success: function(data){ + $('#widget-log .panel-body').html(data); + } + }); +} + +events.push(function(){ + setInterval('logWidgetUpdateFromServer()', 60*1000); +}); +</script> + +<!-- close the body we're wrapped in and add a configuration-panel --> +</div> +<div class="panel-footer collapse"> + + <form action="/widgets/widgets/log.widget.php" method="post" + class="form-horizontal"> + <div class="form-group"> + <label for="filterlogentries" class="col-sm-4 control-label">Number + of entries</label> + <div class="col-sm-6"> + <input type="number" name="filterlogentries" value="<?=$nentries?>" + min="1" max="20" class="form-control" /> + </div> + </div> + + <div class="form-group"> + <label class="col-sm-4 control-label">Filter actions</label> + <div class="col-sm-6 checkbox"> + <?php $include_acts = explode(" ", strtolower($nentriesacts)); ?> + <label><input name="actpass" type="checkbox" value="Pass" + <?=(in_array('pass', $include_acts) ? 'checked="checked"':'')?> />Pass</label> + <label><input name="actblock" type="checkbox" value="Block" + <?=(in_array('block', $include_acts) ? 'checked="checked"':'')?> />Block</label> + <label><input name="actreject" type="checkbox" value="Reject" + <?=(in_array('reject', $include_acts) ? 'checked="checked"':'')?> />Reject</label> + </div> + </div> + + <div class="form-group"> + <label for="filterlogentriesinterfaces" + class="col-sm-4 control-label">Filter interface</label> + <div class="col-sm-6 checkbox"> + <select name="filterlogentriesinterfaces" class="form-control"> + <?php foreach (array("All" => "ALL") + get_configured_interface_with_descr() as $iface => $ifacename):?> + <option value="<?=$iface?>" + <?=($nentriesinterfaces==$iface?'selected="selected"':'')?>><?=htmlspecialchars($ifacename)?></option> + <?php endforeach;?> + </select> + </div> + </div> + + <div class="form-group"> + <div class="col-sm-offset-4 col-sm-6"> + <button type="submit" class="btn btn-default">Save</button> + </div> + </div> + </form> diff --git a/src/usr/local/www/widgets/widgets/ntp_status.widget.php b/src/usr/local/www/widgets/widgets/ntp_status.widget.php new file mode 100644 index 0000000..48d3755 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/ntp_status.widget.php @@ -0,0 +1,499 @@ +<?php +/* + ntp_status.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP +X + Copyright 2007 Scott Dale + Part of pfSense widgets (https://www.pfsense.org) + originally based on m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2004-2005 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net> + and Jonathan Watt <jwatt@jwatt.org>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); + +require_once("/usr/local/www/widgets/include/ntp_status.inc"); + +if ($_REQUEST['updateme']) { +//this block displays only on ajax refresh + if (isset($config['system']['ipv6allow'])) { + $inet_version = ""; + } else { + $inet_version = " -4"; + } + + exec("/usr/local/sbin/ntpq -pn $inet_version | /usr/bin/tail +3", $ntpq_output); + $ntpq_counter = 0; + foreach ($ntpq_output as $line) { + if (substr($line, 0, 1) == "*") { + //Active NTP Peer + $line = substr($line, 1); + $peerinfo = preg_split("/[\s\t]+/", $line); + if ($peerinfo[2] == "1") { + $syncsource = $peerinfo[0] . " (stratum " . $peerinfo[2] . ", " . $peerinfo[1] . ")"; + } else { + $syncsource = $peerinfo[0] . " (stratum " . $peerinfo[2] . ")"; + } + $ntpq_counter++; + } elseif (substr($line, 0, 1) == "o") { + //Local PPS Peer + $line = substr($line, 1); + $peerinfo = preg_split("/[\s\t]+/", $line); + $syncsource = $peerinfo[1] . " (stratum " . $peerinfo[2] . ", PPS)"; + $ntpq_counter++; + } + } + + exec("/usr/local/sbin/ntpq -c clockvar $inet_version", $ntpq_clockvar_output); + foreach ($ntpq_clockvar_output as $line) { + if (substr($line, 0, 9) == "timecode=") { + $tmp = explode('"', $line); + $tmp = $tmp[1]; + if (substr($tmp, 0, 6) == '$GPRMC') { + $gps_vars = explode(",", $tmp); + $gps_ok = ($gps_vars[2] == "A"); + $gps_lat_deg = substr($gps_vars[3], 0, 2); + $gps_lat_min = substr($gps_vars[3], 2) / 60.0; + $gps_lon_deg = substr($gps_vars[5], 0, 3); + $gps_lon_min = substr($gps_vars[5], 3) / 60.0; + $gps_lat = $gps_lat_deg + $gps_lat_min; + $gps_lat = $gps_lat * (($gps_vars[4] == "N") ? 1 : -1); + $gps_lon = $gps_lon_deg + $gps_lon_min; + $gps_lon = $gps_lon * (($gps_vars[6] == "E") ? 1 : -1); + $gps_la = $gps_vars[4]; + $gps_lo = $gps_vars[6]; + }elseif (substr($tmp, 0, 6) == '$GPGGA') { + $gps_vars = explode(",", $tmp); + $gps_ok = $gps_vars[6]; + $gps_lat_deg = substr($gps_vars[2], 0, 2); + $gps_lat_min = substr($gps_vars[2], 2) / 60.0; + $gps_lon_deg = substr($gps_vars[4], 0, 3); + $gps_lon_min = substr($gps_vars[4], 3) / 60.0; + $gps_lat = $gps_lat_deg + $gps_lat_min; + $gps_lat = $gps_lat * (($gps_vars[3] == "N") ? 1 : -1); + $gps_lon = $gps_lon_deg + $gps_lon_min; + $gps_lon = $gps_lon * (($gps_vars[5] == "E") ? 1 : -1); + $gps_alt = $gps_vars[9]; + $gps_alt_unit = $gps_vars[10]; + $gps_sat = $gps_vars[7]; + $gps_la = $gps_vars[3]; + $gps_lo = $gps_vars[5]; + }elseif (substr($tmp, 0, 6) == '$GPGLL') { + $gps_vars = explode(",", $tmp); + $gps_ok = ($gps_vars[6] == "A"); + $gps_lat_deg = substr($gps_vars[1], 0, 2); + $gps_lat_min = substr($gps_vars[1], 2) / 60.0; + $gps_lon_deg = substr($gps_vars[3], 0, 3); + $gps_lon_min = substr($gps_vars[3], 3) / 60.0; + $gps_lat = $gps_lat_deg + $gps_lat_min; + $gps_lat = $gps_lat * (($gps_vars[2] == "N") ? 1 : -1); + $gps_lon = $gps_lon_deg + $gps_lon_min; + $gps_lon = $gps_lon * (($gps_vars[4] == "E") ? 1 : -1); + $gps_la = $gps_vars[2]; + $gps_lo = $gps_vars[4]; + } + } + } + + if (isset($config['ntpd']['gps']['type']) && ($config['ntpd']['gps']['type'] == 'SureGPS') && (isset($gps_ok))) { + //GSV message is only enabled by init commands in services_ntpd_gps.php for SureGPS board + $gpsport = fopen("/dev/gps0", "r+"); + while($gpsport){ + $buffer = fgets($gpsport); + if(substr($buffer, 0, 6)=='$GPGSV'){ + //echo $buffer."\n"; + $gpgsv = explode(',',$buffer); + $gps_satview = $gpgsv[3]; + break; + } + } + } +?> + +<table class="table" id="ntp_status_widget"> + <tr> + <th>Server Time</th> + <td id="ntpStatusClock"> + <script>var ntpServerTime = new Date('<?=date_format(date_create(), 'c')?>');</script> + <!-- display initial value before javascript takes over --> + <?=gmdate('D j Y H:i:s \G\M\T O (T)');?> + </td> + </tr> + <tr> + <th>Sync Source</th> + <td> + <?php if ($ntpq_counter == 0): ?> + <i>No active peers available</i> + <?php else: ?> + <?php echo $syncsource; ?> + <?php endif; ?> + </td> + </tr> + <?php if (($gps_ok) && ($gps_lat) && ($gps_lon)): ?> + <tr> + <th>Clock location</th> + <td> + <a target="_gmaps" href="http://maps.google.com/?q=<?php echo $gps_lat; ?>,<?php echo $gps_lon; ?>"> + <?php + echo sprintf("%.5f", $gps_lat) . " " . $gps_la . ", " . sprintf("%.5f", $gps_lon) . " " . $gps_lo; ?> + </a> + <?php if (isset($gps_alt)) {echo " (" . $gps_alt . " " . $gps_alt_unit . " alt.)";} ?> + </td> + </tr> + <?php if (isset($gps_sat) || isset($gps_satview)): ?> + <tr> + <th>Satellites</th> + <td> + <?php + if (isset($gps_satview)) {echo 'in view ' . intval($gps_satview);} + if (isset($gps_sat) && isset($gps_satview)) {echo ', ';} + if (isset($gps_sat)) {echo 'in use ' . $gps_sat;} + ?> + </td> + </tr> + <?php endif; ?> + <?php endif; ?> +</table> + +<?php + exit; +} +?> +<script> +function ntpWidgetUpdateFromServer(){ + $.ajax({ + type: 'get', + url: '/widgets/widgets/ntp_status.widget.php', + dataFilter: function(raw){ + // We reload the entire widget, strip this block of javascript from it + return raw.replace(/<script>([\s\S]*)<\/script>/gi, ''); + }, + dataType: 'html', + success: function(data){ + console.log(data); + $('#ntp_status_widget').html(data); + } + }); +} + +function ntpWidgetUpdateDisplay(){ + // Javascript handles overflowing + ntpServerTime.setSeconds(ntpServerTime.getSeconds()+1); + + $('#ntpStatusClock').html(ntpServerTime.toString()); +} + +<script type="text/javascript"> +//<![CDATA[ +/* set up variables used to init clock in BODY's onLoad handler; + should be done as early as possible */ +var clockLocalStartTime = new Date(); +var clockServerStartTime = new Date(<?php echo(getServerDateItems($gDate))?>); + +/* stub functions for older browsers; + will be overridden by next JavaScript1.2 block */ +function clockInit() { +} +//]]> +</script> + + +<script type="text/javascript"> +//<![CDATA[ +/*** simpleFindObj, by Andrew Shearer + +Efficiently finds an object by name/id, using whichever of the IE, +classic Netscape, or Netscape 6/W3C DOM methods is available. +The optional inLayer argument helps Netscape 4 find objects in +the named layer or floating DIV. */ +function simpleFindObj(name, inLayer) { + return document[name] || (document.all && document.all[name]) + || (document.getElementById && document.getElementById(name)) + || (document.layers && inLayer && document.layers[inLayer].document[name]); +} + +/*** Beginning of Clock 2.1.2, by Andrew Shearer +See: http://www.shearersoftware.com/software/web-tools/clock/ +Redistribution is permitted with the above notice intact. + +Client-side clock, based on computed time differential between browser & +server. The server time is inserted by server-side JavaScript, and local +time is subtracted from it by client-side JavaScript while the page is +loading. + +Cookies: The local and remote times are saved in cookies named +localClock and remoteClock, so that when the page is loaded from local +cache (e.g. by the Back button) the clock will know that the embedded +server time is stale compared to the local time, since it already +matches its cookie. It can then base the calculations on both cookies, +without reloading the page from the server. (IE 4 & 5 for Windows didn't +respect Response.Expires = 0, so if cookies weren't used, the clock +would be wrong after going to another page then clicking Back. Netscape +& Mac IE were OK.) + +Every so often (by default, one hour) the clock will reload the page, to +make sure the clock is in sync (as well as to update the rest of the +page content). + +Compatibility: IE 4.x and 5.0, Netscape 4.x and 6.0, Mozilla 1.0. Mac & Windows. + +History: 1.0 2000-05-09 GIF-image digits + 2.0 2000-06-29 Uses text DIV layers (so 4.0 browsers req'd), & + cookies to work around Win IE stale-time bug + 2.1 2002-10-12 Noted Mozilla 1.0 compatibility; released PHP version. + 2.1.1 2002-10-20 Fixed octal bug in the PHP translation; the number of + minutes & seconds were misinterpreted when less than 10 + 2.1.2 2003-08-07 The previous fix had introduced a bug when the + minutes or seconds were exactly 0. Thanks to Man Bui + for reporting the bug. +*/ +var clockIncrementMillis = 1000; +var localTime; +var clockOffset; +var clockExpirationLocal; +var clockShowsSeconds = true; +var clockTimerID = null; + +function clockInit(localDateObject, serverDateObject) +{ + var origRemoteClock = parseInt(clockGetCookieData("remoteClock")); + var origLocalClock = parseInt(clockGetCookieData("localClock")); + var newRemoteClock = serverDateObject.getTime(); + // May be stale (WinIE); will check against cookie later + // Can't use the millisec. ctor here because of client inconsistencies. + var newLocalClock = localDateObject.getTime(); + var maxClockAge = 60 * 60 * 1000; // get new time from server every 1hr + + if (newRemoteClock != origRemoteClock) { + // new clocks are up-to-date (newer than any cookies) + document.cookie = "remoteClock=" + newRemoteClock; + document.cookie = "localClock=" + newLocalClock; + clockOffset = newRemoteClock - newLocalClock; + clockExpirationLocal = newLocalClock + maxClockAge; + localTime = newLocalClock; // to keep clockUpdate() happy + } else if (origLocalClock != origLocalClock) { + // error; localClock cookie is invalid (parsed as NaN) + clockOffset = null; + clockExpirationLocal = null; + } else { + // fall back to clocks in cookies + clockOffset = origRemoteClock - origLocalClock; + clockExpirationLocal = origLocalClock + maxClockAge; + localTime = origLocalClock; + // so clockUpdate() will reload if newLocalClock + // is earlier (clock was reset) + } + /* Reload page at server midnight to display the new date, + by expiring the clock then */ + var nextDayLocal = (new Date(serverDateObject.getFullYear(), + serverDateObject.getMonth(), + serverDateObject.getDate() + 1)).getTime() - clockOffset; + if (nextDayLocal < clockExpirationLocal) { + clockExpirationLocal = nextDayLocal; + } +} + +function clockOnLoad() +{ + clockUpdate(); +} + +function clockOnUnload() { + clockClearTimeout(); +} + +function clockClearTimeout() { + if (clockTimerID) { + clearTimeout(clockTimerID); + clockTimerID = null; + } +} + +function clockToggleSeconds() +{ + clockClearTimeout(); + if (clockShowsSeconds) { + clockShowsSeconds = false; + clockIncrementMillis = 60000; + } else { + clockShowsSeconds = true; + clockIncrementMillis = 1000; + } + clockUpdate(); +} + +function clockTimeString(inHours, inMinutes, inSeconds) { + return inHours + + (inMinutes < 10 ? ":0" : ":") + inMinutes + + (inSeconds < 10 ? ":0" : ":") + inSeconds; +} + +function clockDisplayTime(inHours, inMinutes, inSeconds) { + clockWriteToDiv("ClockTime", clockTimeString(inHours, inMinutes, inSeconds)); +} + +function clockWriteToDiv(divName, newValue) // APS 6/29/00 +{ + var divObject = simpleFindObj(divName); + newValue = '<b>' + newValue + '<' + '/b>'; + if (divObject && divObject.innerHTML) { + divObject.innerHTML = newValue; + } else if (divObject && divObject.document) { + divObject.document.writeln(newValue); + divObject.document.close(); + } + // else divObject wasn't found; it's only a clock, so don't bother complaining +} + +function clockGetCookieData(label) { + /* find the value of the specified cookie in the document's + semicolon-delimited collection. For IE Win98 compatibility, search + from the end of the string (to find most specific host/path) and + don't require "=" between cookie name & empty cookie values. Returns + null if cookie not found. One remaining problem: Under IE 5 [Win98], + setting a cookie with no equals sign creates a cookie with no name, + just data, which is indistinguishable from a cookie with that name + but no data but can't be overwritten by any cookie with an equals + sign. */ + var c = document.cookie; + if (c) { + var labelLen = label.length, cEnd = c.length; + while (cEnd > 0) { + var cStart = c.lastIndexOf(';',cEnd-1) + 1; + /* bug fix to Danny Goodman's code: calculate cEnd, to + prevent walking the string char-by-char & finding cookie + labels that contained the desired label as suffixes */ + // skip leading spaces + while (cStart < cEnd && c.charAt(cStart)==" ") { + cStart++; + } + if (cStart + labelLen <= cEnd && c.substr(cStart,labelLen) == label) { + if (cStart + labelLen == cEnd) { + return ""; // empty cookie value, no "=" + } else if (c.charAt(cStart+labelLen) == "=") { + // has "=" after label + return unescape(c.substring(cStart + labelLen + 1,cEnd)); + } + } + cEnd = cStart - 1; // skip semicolon + } + } + return null; +} + +/* Called regularly to update the clock display as well as onLoad (user + may have clicked the Back button to arrive here, so the clock would need + an immediate update) */ +function clockUpdate() +{ + var lastLocalTime = localTime; + localTime = (new Date()).getTime(); + + /* Sanity-check the diff. in local time between successive calls; + reload if user has reset system clock */ + if (clockOffset == null) { + clockDisplayTime(null, null, null); + } else if (localTime < lastLocalTime || clockExpirationLocal < localTime) { + /* Clock expired, or time appeared to go backward (user reset + the clock). Reset cookies to prevent infinite reload loop if + server doesn't give a new time. */ + document.cookie = 'remoteClock=-'; + document.cookie = 'localClock=-'; + location.reload(); // will refresh time values in cookies + } else { + // Compute what time would be on server + var serverTime = new Date(localTime + clockOffset); + clockDisplayTime(serverTime.getHours(), serverTime.getMinutes(), + serverTime.getSeconds()); + + // Reschedule this func to run on next even clockIncrementMillis boundary + clockTimerID = setTimeout("clockUpdate()", + clockIncrementMillis - (serverTime.getTime() % clockIncrementMillis)); + } +} + +/*** End of Clock ***/ +window.onload=clockInit(clockLocalStartTime, clockServerStartTime);clockOnLoad(); +window.onunload=clockOnUnload() +clockUpdate(); +//]]> +</script> + + +<table width="100%" border="0" cellspacing="0" cellpadding="0" summary="clock"> + <tbody> + <tr> + <td width="40%" class="vncellt">Server Time</td> + <td width="60%" class="listr"> + <div id="ClockTime"> + <b><?php echo(clockTimeString($gDate,$gClockShowsSeconds));?></b> + </div> + </td> + </tr> + </tbody> +</table> + +<div id='ntpstatus'> +<table width="100%" border="0" cellspacing="0" cellpadding="0" summary="clock"> + <tbody> + <tr> + <td width="100%" class="listr"> + Updating... + </td> + </tr> + </tbody> +</table> +</div> + +<script type="text/javascript"> +//<![CDATA[ + function ntp_getstatus() { + scroll(0,0); + var url = "/widgets/widgets/ntp_status.widget.php"; + var pars = 'updateme=yes'; + jQuery.ajax( + url, + { + type: 'get', + data: pars, + complete: ntpstatuscallback + }); + // Refresh the status every 1 minute + setTimeout('ntp_getstatus()', 1*60*1000); + } + function ntpstatuscallback(transport) { + // The server returns formatted html code + var responseStringNtp = transport.responseText + jQuery('#ntpstatus').prop('innerHTML',responseStringNtp); + } + // Do the first status check 1 second after the dashboard opens + setTimeout('ntp_getstatus()', 1000); +//]]> +</script> diff --git a/src/usr/local/www/widgets/widgets/openvpn.widget.php b/src/usr/local/www/widgets/widgets/openvpn.widget.php new file mode 100644 index 0000000..4dc386c --- /dev/null +++ b/src/usr/local/www/widgets/widgets/openvpn.widget.php @@ -0,0 +1,289 @@ +<?php + +/* + openvpn.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Part of pfSense widgets (https://www.pfsense.org) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("openvpn.inc"); + +/* Handle AJAX */ +if ($_GET['action']) { + if ($_GET['action'] == "kill") { + $port = $_GET['port']; + $remipp = $_GET['remipp']; + if (!empty($port) and !empty($remipp)) { + $retval = kill_client($port, $remipp); + echo htmlentities("|{$port}|{$remipp}|{$retval}|"); + } else { + echo gettext("invalid input"); + } + exit; + } +} + + +function kill_client($port, $remipp) { + global $g; + + //$tcpsrv = "tcp://127.0.0.1:{$port}"; + $tcpsrv = "unix://{$g['varetc_path']}/openvpn/{$port}.sock"; + $errval = null; + $errstr = null; + + /* open a tcp connection to the management port of each server */ + $fp = @stream_socket_client($tcpsrv, $errval, $errstr, 1); + $killed = -1; + if ($fp) { + stream_set_timeout($fp, 1); + fputs($fp, "kill {$remipp}\n"); + while (!feof($fp)) { + $line = fgets($fp, 1024); + + $info = stream_get_meta_data($fp); + if ($info['timed_out']) { + break; + } + + /* parse header list line */ + if (strpos($line, "INFO:") !== false) { + continue; + } + if (strpos($line, "SUCCESS") !== false) { + $killed = 0; + } + break; + } + fclose($fp); + } + return $killed; +} + +$servers = openvpn_get_active_servers(); +$sk_servers = openvpn_get_active_servers("p2p"); +$clients = openvpn_get_active_clients(); +?> + +<br /> +<script type="text/javascript"> +//<![CDATA[ + function killClient(mport, remipp) { + var busy = function(index,icon) { + jQuery(icon).bind("onclick",""); + jQuery(icon).attr('src',jQuery(icon).attr('src').replace("\.gif", "_d.gif")); + jQuery(icon).css("cursor","wait"); + } + + jQuery('img[name="i:' + mport + ":" + remipp + '"]').each(busy); + + jQuery.ajax( + "<?=$_SERVER['SCRIPT_NAME'];?>" + + "?action=kill&port=" + mport + "&remipp=" + remipp, + { type: "get", complete: killComplete } + ); + } + + function killComplete(req) { + var values = req.responseText.split("|"); + if (values[3] != "0") { + alert('<?=gettext("An error occurred.");?>' + ' (' + values[3] + ')'); + return; + } + + jQuery('tr[name="r:' + values[1] + ":" + values[2] + '"]').each( + function(index,row) { jQuery(row).fadeOut(1000); } + ); + } +//]]> +</script> + +<?php foreach ($servers as $server): ?> + +<table> + <tr> + <td colspan="6" class="listtopic"> + <?=$server['name'];?> Client connections + </td> + </tr> + <tr> + <td> + <table> + <tr> + <th>Name/Time</td> + <th>Real/Virtual IP</td> + </tr> + <?php $rowIndex = 0; + foreach ($server['conns'] as $conn): + $evenRowClass = $rowIndex % 2 ? " listMReven" : " listMRodd"; + $rowIndex++; + ?> + <tr name='<?php echo "r:{$server['mgmt']}:{$conn['remote_host']}"; ?>' class="<?=$evenRowClass?>"> + <td class="listMRlr"> + <?=$conn['common_name'];?> + </td> + <td class="listMRr"> + <?=$conn['remote_host'];?> + </td> + <td class='listMR' rowspan="2"> + <img src='/themes/<?php echo $g['theme']; ?>/images/icons/icon_x.gif' height='17' width='17' border='0' + onclick="killClient('<?php echo $server['mgmt']; ?>', '<?php echo $conn['remote_host']; ?>');" style='cursor:pointer;' + name='<?php echo "i:{$server['mgmt']}:{$conn['remote_host']}"; ?>' + title='Kill client connection from <?php echo $conn['remote_host']; ?>' alt='' /> + </td> + </tr> + <tr name='<?php echo "r:{$server['mgmt']}:{$conn['remote_host']}"; ?>' class="<?=$evenRowClass?>"> + <td class="listMRlr"> + <?=$conn['connect_time'];?> + </td> + <td class="listMRr"> + <?=$conn['virtual_addr'];?> + </td> + </tr> + + <?php endforeach; ?> + <tfoot> + <tr> + <td colspan="6" class="list" height="12"></td> + </tr> + </tfoot> + </table> + </td> + </tr> +</table> + +<?php endforeach; ?> +<?php if (!empty($sk_servers)) { ?> +<table> + <tr> + <td colspan="6" class="listtopic"> + Peer to Peer Server Instance Statistics + </td> + </tr> + <tr> + <table> + <tr> + <th>Name/Time</td> + <th>Remote/Virtual IP</td> + </tr> + +<?php foreach ($sk_servers as $sk_server): ?> + <tr name='<?php echo "r:{$sk_server['port']}:{$sk_server['remote_host']}"; ?>'> + <td class="listlr"> + <?=$sk_server['name'];?> + </td> + <td class="listr"> + <?=$sk_server['remote_host'];?> + </td> + <td rowspan="2" align="center"> + <?php + if ($sk_server['status'] == "up") { + /* tunnel is up */ + $iconfn = "interface_up"; + } else { + /* tunnel is down */ + $iconfn = "interface_down"; + } + echo "<img src ='/themes/{$g['theme']}/images/icons/icon_{$iconfn}.gif' alt='' />"; + ?> + </td> + </tr> + <tr name='<?php echo "r:{$sk_server['port']}:{$sk_server['remote_host']}"; ?>'> + <td class="listlr"> + <?=$sk_server['connect_time'];?> + </td> + <td class="listr"> + <?=$sk_server['virtual_addr'];?> + </td> + </tr> +<?php endforeach; ?> + </table> + </tr> +</table> + +<?php +} ?> +<?php if (!empty($clients)) { ?> +<table> + <tr> + <td colspan="6" class="listtopic"> + Client Instance Statistics + </td> + </tr> + <tr> + <table> + <tr> + <th>Name/Time</td> + <th>Remote/Virtual IP</td> + </tr> + + <?php foreach ($clients as $client): ?> + <tr name='<?php echo "r:{$client['port']}:{$client['remote_host']}"; ?>'> + <td class="listlr"> + <?=$client['name'];?> + </td> + <td class="listr"> + <?=$client['remote_host'];?> + </td> + <td rowspan="2" align="center"> + <?php + if ($client['status'] == "up") { + /* tunnel is up */ + $iconfn = "interface_up"; + } else { + /* tunnel is down */ + $iconfn = "interface_down"; + } + echo "<img src ='/themes/{$g['theme']}/images/icons/icon_{$iconfn}.gif' alt='' />"; + ?> + </td> + </tr> + <tr name='<?php echo "r:{$client['port']}:{$client['remote_host']}"; ?>'> + <td class="listlr"> + <?=$client['connect_time'];?> + </td> + <td class="listr"> + <?=$client['virtual_addr'];?> + </td> + </tr> + <?php endforeach; ?> + </table> + </tr> +</table> + +<?php +} + +if ($DisplayNote) { + echo "<br /><b>NOTE:</b> You need to bind each OpenVPN client to enable its management daemon: use 'Local port' setting in the OpenVPN client screen"; +} + +if ((empty($clients)) && (empty($servers)) && (empty($sk_servers))) { + echo "No OpenVPN instances defined"; +} +?> diff --git a/src/usr/local/www/widgets/widgets/picture.widget.php b/src/usr/local/www/widgets/widgets/picture.widget.php new file mode 100644 index 0000000..6b19ab6 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/picture.widget.php @@ -0,0 +1,84 @@ +<?php +/* + picture.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright 2009 Scott Ullrich + Part of pfSense widgets (https://www.pfsense.org) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); + +if ($_GET['getpic']=="true") { + $pic_type_s = explode(".", $config['widgets']['picturewidget_filename']); + $pic_type = $pic_type_s[1]; + if ($config['widgets']['picturewidget']) { + $data = base64_decode($config['widgets']['picturewidget']); + } + header("Content-Disposition: inline; filename=\"{$config['widgets']['picturewidget_filename']}\""); + header("Content-Type: image/{$pic_type}"); + header("Content-Length: " . strlen($data)); + echo $data; + exit; +} + +if ($_POST) { + if (is_uploaded_file($_FILES['pictfile']['tmp_name'])) { + /* read the file contents */ + $fd_pic = fopen($_FILES['pictfile']['tmp_name'], "rb"); + while (($buf=fread($fd_pic, 8192)) != '') { + // Here, $buf is guaranteed to contain data + $data .= $buf; + } + fclose($fd_pic); + if (!$data) { + log_error("Warning, could not read file " . $_FILES['pictfile']['tmp_name']); + die("Could not read temporary file"); + } else { + $picname = basename($_FILES['uploadedfile']['name']); + $config['widgets']['picturewidget'] = base64_encode($data); + $config['widgets']['picturewidget_filename'] = $_FILES['pictfile']['name']; + write_config("Picture widget saved via Dashboard."); + header("Location: /index.php"); + exit; + } + } +} + +?> +<a href="/widgets/widgets/picture.widget.php?getpic=true" target="_blank"> + <img width="100%" height="100%" src="/widgets/widgets/picture.widget.php?getpic=true" alt="picture" /> +</a> + +<!-- close the body we're wrapped in and add a configuration-panel --> +</div><div class="panel-footer collapse"> + +<form action="/widgets/widgets/picture.widget.php" method="post" enctype="multipart/form-data" class="form-inline"> + <label for="pictfile">New picture: </label> + <input name="pictfile" type="file" class="form-control" /> + <button type="submit" class="btn btn-default">Upload</button> +</form> diff --git a/src/usr/local/www/widgets/widgets/rss.widget.php b/src/usr/local/www/widgets/widgets/rss.widget.php new file mode 100644 index 0000000..e84bae2 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/rss.widget.php @@ -0,0 +1,167 @@ +<?php +/* + rss.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Copyright 2009 Scott Ullrich + Part of pfSense widgets (https://www.pfsense.org) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); + +if ($_POST['rssfeed']) { + $config['widgets']['rssfeed'] = str_replace("\n", ",", htmlspecialchars($_POST['rssfeed'], ENT_QUOTES | ENT_HTML401)); + $config['widgets']['rssmaxitems'] = str_replace("\n", ",", htmlspecialchars($_POST['rssmaxitems'], ENT_QUOTES | ENT_HTML401)); + $config['widgets']['rsswidgetheight'] = htmlspecialchars($_POST['rsswidgetheight'], ENT_QUOTES | ENT_HTML401); + $config['widgets']['rsswidgettextlength'] = htmlspecialchars($_POST['rsswidgettextlength'], ENT_QUOTES | ENT_HTML401); + write_config("Saved RSS Widget feed via Dashboard"); + header("Location: /"); +} + +// Use saved feed and max items +if ($config['widgets']['rssfeed']) { + $rss_feed_s = explode(",", $config['widgets']['rssfeed']); +} + +if ($config['widgets']['rssmaxitems']) { + $max_items = $config['widgets']['rssmaxitems']; +} + +if (is_numeric($config['widgets']['rsswidgetheight'])) { + $rsswidgetheight = $config['widgets']['rsswidgetheight']; +} + +if (is_numeric($config['widgets']['rsswidgettextlength'])) { + $rsswidgettextlength = $config['widgets']['rsswidgettextlength']; +} + +// Set a default feed if none exists +if (!$rss_feed_s) { + $rss_feed_s = "https://blog.pfsense.org"; + $config['widgets']['rssfeed'] = "https://blog.pfsense.org"; +} + +if (!$max_items) { + $max_items = 10; +} + +if (!$rsswidgetheight) { + $rsswidgetheight = 300; +} + +if (!$rsswidgettextlength) { + $rsswidgettextlength = 140; // oh twitter, how do we love thee? +} + +if ($config['widgets']['rssfeed']) { + $textarea_txt = str_replace(",", "\n", $config['widgets']['rssfeed']); +} else { + $textarea_txt = ""; +} + +?> +<div class="list-group" style="height: <?=$rsswidgetheight?>px; overflow:scroll;"> +<?php + if (!is_dir("/tmp/simplepie")) { + mkdir("/tmp/simplepie"); + mkdir("/tmp/simplepie/cache"); + } + exec("chmod a+rw /tmp/simplepie/."); + exec("chmod a+rw /tmp/simplepie/cache/."); + require_once("simplepie/simplepie.inc"); + function textLimit($string, $length, $replacer = '...') { + if(strlen($string) > $length) + return (preg_match('/^(.*)\W.*$/', substr($string, 0, $length+1), $matches) ? $matches[1] : substr($string, 0, $length)) . $replacer; + return $string; + } + $feed = new SimplePie(); + $feed->set_cache_location("/tmp/simplepie/"); + $feed->set_feed_url($rss_feed_s); + $feed->init(); + $feed->handle_content_type(); + $counter = 1; + foreach($feed->get_items(0, $max_items) as $item) { + $feed = $item->get_feed(); + $feed->strip_htmltags(); + $content = $item->get_content(); + $content = strip_tags($content); +?> + <a href="<?=$item->get_permalink()?>" target="_blank" class="list-group-item"> + <h4 class="list-group-item-heading"> + <img src="<?=$feed->get_favicon()?>" title="Source: <?=$feed->get_title()?>" width="16" height="16" /> + <?=$item->get_title()?> + </h4> + <p class="list-group-item-text"> + <?=textLimit($content, $rsswidgettextlength)?> + <br /> + </p> + </a> +<?php + } +?> + +</div> + +<!-- close the body we're wrapped in and add a configuration-panel --> +</div><div class="panel-footer collapse"> + +<form action="/widgets/widgets/rss.widget.php" method="post" class="form-horizontal"> + <div class="form-group"> + <label for="rssfeed" class="col-sm-3 control-label">Feeds</label> + <div class="col-sm-6"> + <textarea name="rssfeed" class="form-control"><?=$textarea_txt;?></textarea> + </div> + </div> + + <div class="form-group"> + <label for="rssmaxitems" class="col-sm-3 control-label"># Stories</label> + <div class="col-sm-6"> + <input type="number" name="rssmaxitems" value="<?=$max_items?>" min="1" max="100" class="form-control" /> + </div> + </div> + + <div class="form-group"> + <label for="rsswidgetheight" class="col-sm-3 control-label">Widget height</label> + <div class="col-sm-6"> + <input type="number" name="rsswidgetheight" value="<?=$rsswidgetheight?>" min="100" max="2500" step="100" class="form-control" /> + </div> + </div> + + <div class="form-group"> + <label for="rsswidgettextlength" class="col-sm-3 control-label">Content limit</label> + <div class="col-sm-6"> + <input type="number" name="rsswidgettextlength" value="<?=$rsswidgettextlength?>" min="100" max="5000" step="100" class="form-control" /> + </div> + </div> + + <div class="form-group"> + <div class="col-sm-offset-3 col-sm-6"> + <button type="submit" class="btn btn-default">Save</button> + </div> + </div> +</form>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/widgets/services_status.widget.php b/src/usr/local/www/widgets/widgets/services_status.widget.php new file mode 100644 index 0000000..a0f7e54 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/services_status.widget.php @@ -0,0 +1,114 @@ +<?php +/* + services_status.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Copyright (C) 2004, 2005 Scott Ullrich + All rights reserved. + + services_status.widget.php + Copyright (C) 2007 Sam Wenham + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INClUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("captiveportal.inc"); +require_once("service-utils.inc"); +require_once("ipsec.inc"); +require_once("vpn.inc"); +require_once("/usr/local/www/widgets/include/services_status.inc"); + +$services = get_services(); + +if(isset($_POST['servicestatusfilter'])) { + $validNames = array(); + foreach ($services as $service) + array_push($validNames, $service['name']); + + $config['widgets']['servicestatusfilter'] = implode(',', array_intersect($validNames, $_POST['servicestatusfilter'])); + write_config("Saved Service Status Filter via Dashboard"); + header("Location: /"); +} +?> +<table class="table table-striped table-hover"> +<thead> + <tr> + <th></th> + <th>Service</td> + <th>Description</td> + <th>Action</td> + </tr> +</thead> +<tbody> +<?php +$skipservices = explode(",", $config['widgets']['servicestatusfilter']); + +if (count($services) > 0) { + uasort($services, "service_name_compare"); + foreach ($services as $service) { + if ((!$service['name']) || (in_array($service['name'], $skipservices)) || (!is_service_enabled($service['name']))) { + continue; + } + if (empty($service['description'])) { + $service['description'] = get_pkg_descr($service['name']); + } + $service_desc = explode(".",$service['description']); +?> + <tr> + <td><i class="icon icon-<?=get_service_status($service)? 'ok' : 'remove'?>-sign"></i></td> + <td><?=$service['name']?></td> + <td><?=$service_desc[0]?></td> + <td><?=get_service_control_links($service)?></td> + </tr> +<?php + } +} else { + echo "<tr><td colspan=\"3\" align=\"center\">" . gettext("No services found") . " . </td></tr>\n"; +} +?> +</tbody> +</table> + +<!-- close the body we're wrapped in and add a configuration-panel --> +</div><div class="panel-footer collapse"> + +<form action="/widgets/widgets/services_status.widget.php" method="post" class="form-horizontal"> + <div class="form-group"> + <label for="inputPassword3" class="col-sm-3 control-label">Hidden services</label> + <div class="col-sm-6"> + <select multiple="multiple" name="servicestatusfilter" class="form-control" height="5"> + <?php foreach ($services as $service): ?> + <option <?=(in_array($service['name'], $skipservices)?'selected="selected"':'')?>><?=$service['name']?></option> + <?php endforeach; ?> + </select> + </div> + </div> + + <div class="form-group"> + <div class="col-sm-offset-3 col-sm-6"> + <button type="submit" class="btn btn-default">Save</button> + </div> + </div> +</form>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/widgets/smart_status.widget.php b/src/usr/local/www/widgets/widgets/smart_status.widget.php new file mode 100644 index 0000000..175a185 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/smart_status.widget.php @@ -0,0 +1,82 @@ +<?php +/* + smart_status.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright 2012 mkirbst @ pfSense Forum + Part of pfSense widgets (https://www.pfsense.org) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); +require_once("/usr/local/www/widgets/include/smart_status.inc"); +?> + +<table class="table table-striped table-hover"> + <thead> + <tr> + <th></th> + <th><?=gettext("Drive")?></th> + <th><?=gettext("Ident")?></th> + <th><?=gettext("SMART Status")?></th> + </tr> + </thead> + <tbody> +<?php +$devs = array(); +## Get all adX, daX, and adaX (IDE, SCSI, and AHCI) devices currently installed +$devs = get_smart_drive_list(); + +if (count($devs) > 0) { + foreach ($devs as $dev) { ## for each found drive do + $dev_ident = exec("diskinfo -v /dev/$dev | grep ident | awk '{print $1}'"); ## get identifier from drive + $dev_state = trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} +/^SMART Health Status/ {print $2;exit}'")); ## get SMART state from drive + switch ($dev_state) { + case "PASSED": + case "OK": + $color = "#90EE90"; + break; + case "": + $dev_state = "Unknown"; + $color = "#C0B788"; + break; + default: + $color = "#F08080"; + break; + } +?> + <tr> + <td><i class="icon icon-<?=$icon?>-sign"></i></td> + <td><?=$dev?></td> + <td><?=$dev_ident?></td> + <td><?=ucfirst($dev_state)?></td> + </tr> +<?php + } +} +?> + </tbody> +</table> diff --git a/src/usr/local/www/widgets/widgets/system_information.widget.php b/src/usr/local/www/widgets/widgets/system_information.widget.php new file mode 100644 index 0000000..c9aab04 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/system_information.widget.php @@ -0,0 +1,311 @@ +<?php +/* + system_information.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright 2007 Scott Dale + Part of pfSense widgets (https://www.pfsense.org) + originally based on m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2004-2005 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net> + and Jonathan Watt <jwatt@jwatt.org>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +require_once("functions.inc"); +require_once("guiconfig.inc"); +require_once('notices.inc'); +include_once("includes/functions.inc.php"); + +if ($_REQUEST['getupdatestatus']) { + if (isset($config['system']['firmware']['disablecheck'])) { + exit; + } + if (isset($config['system']['firmware']['alturl']['enable'])) { + $updater_url = "{$config['system']['firmware']['alturl']['firmwareurl']}"; + } else { + $updater_url = $g['update_url']; + } + + $nanosize = ""; + if ($g['platform'] == "nanobsd") { + if (file_exists("/etc/nano_use_vga.txt")) { + $nanosize = "-nanobsd-vga-"; + } else { + $nanosize = "-nanobsd-"; + } + $nanosize .= strtolower(trim(file_get_contents("/etc/nanosize.txt"))); + } + + @unlink("/tmp/{$g['product_name']}_version"); + if (download_file_with_progress_bar("{$updater_url}/version{$nanosize}", "/tmp/{$g['product_name']}_version", 'read_body', 5, 5) === true) { + $remote_version = trim(@file_get_contents("/tmp/{$g['product_name']}_version")); + } + + if(empty($remote_version)) + echo "<i>Unable to check for updates</i>"; + else { + $current_installed_buildtime = trim(file_get_contents("/etc/version.buildtime")); + $current_installed_version = trim(file_get_contents("/etc/version")); + + if(!$remote_version) { + echo "<i>Unable to check for updates</i>"; + } + else { + $needs_system_upgrade = false; + if (pfs_version_compare($current_installed_buildtime, $current_installed_version, $remote_version) == -1) { +?> +<div class="alert alert-warning" role="alert"> + Version <?=$remote_version?> is available. <a href="/system_firmware_check.php" class="alert-link">Click Here to view.</a> +</div> +<?php + } else + echo "You are on the latest version."; + } + } + exit; +} + +$curcfg = $config['system']['firmware']; + +$filesystems = get_mounted_filesystems(); +?> + +<table class="table table-striped table-hover"> + <tbody> + <tr> + <th><?=gettext("Name");?></td> + <td><?php echo $config['system']['hostname'] . "." . $config['system']['domain']; ?></td> + </tr> + <tr> + <th><?=gettext("Version");?></th> + <td> + <strong><?php readfile("/etc/version"); ?></strong> + (<?php echo php_uname("m"); ?>) + <br /> + built on <?php readfile("/etc/version.buildtime"); ?> + <?php if(!$g['hideuname']): ?> + <br /> + <span title="<?php echo php_uname("a"); ?>"><?php echo php_uname("s") . " " . php_uname("r"); ?></span> + <?php endif; ?> + <br/><br/> + <?php if(!isset($config['system']['firmware']['disablecheck'])): ?> + <div id='updatestatus'><?php echo gettext("Obtaining update status"); ?> ...</div> + <?php endif; ?> + </td> + </tr> + <?php if (!$g['hideplatform']): ?> + <tr> + <th><?=gettext("Platform");?></td> + <td> + <?=htmlspecialchars($g['platform']);?> + <?php if (($g['platform'] == "nanobsd") && (file_exists("/etc/nanosize.txt"))) { + echo " (" . htmlspecialchars(trim(file_get_contents("/etc/nanosize.txt"))) . ")"; + } ?> + </td> + </tr> + <?php endif; ?> + <?php if ($g['platform'] == "nanobsd"): ?> + <? + global $SLICE, $OLDSLICE, $TOFLASH, $COMPLETE_PATH, $COMPLETE_BOOT_PATH; + global $GLABEL_SLICE, $UFS_ID, $OLD_UFS_ID, $BOOTFLASH; + global $BOOT_DEVICE, $REAL_BOOT_DEVICE, $BOOT_DRIVE, $ACTIVE_SLICE; + nanobsd_detect_slice_info(); + $rw = is_writable("/") ? "(rw)" : "(ro)"; + ?> + <tr> + <th><?=gettext("NanoBSD Boot Slice");?></td> + <td> + <?=htmlspecialchars(nanobsd_friendly_slice_name($BOOT_DEVICE));?> / <?=htmlspecialchars($BOOTFLASH);?><?php echo $rw; ?> + <?php if ($BOOTFLASH != $ACTIVE_SLICE): ?> + <br /><br />Next Boot:<br /> + <?=htmlspecialchars(nanobsd_friendly_slice_name($GLABEL_SLICE));?> / <?=htmlspecialchars($ACTIVE_SLICE);?> + <?php endif; ?> + </td> + </tr> + <?php endif; ?> + <tr> + <th><?=gettext("CPU Type");?></td> + <td><?=htmlspecialchars(get_single_sysctl("hw.model"))?> + <div id="cpufreq"><?= get_cpufreq(); ?></div> + <?php + $cpucount = get_cpu_count(); + if ($cpucount > 1): ?> + <div id="cpucount"> + <?= htmlspecialchars($cpucount) ?> CPUs: <?= htmlspecialchars(get_cpu_count(true)); ?></div> + <?php endif; ?> + </td> + </tr> + <?php if ($hwcrypto): ?> + <tr> + <th><?=gettext("Hardware crypto");?></td> + <td><?=htmlspecialchars($hwcrypto);?></td> + </tr> + <?php endif; ?> + <tr> + <th><?=gettext("Uptime");?></td> + <td id="uptime"><?= htmlspecialchars(get_uptime()); ?></td> + </tr> + <tr> + <th><?=gettext("Current date/time");?></td> + <td><div id="datetime"><?= date("D M j G:i:s T Y"); ?></div></td> + </tr> + <tr> + <th><?=gettext("DNS server(s)");?></td> + <td> + <ul> + <?php + $dns_servers = get_dns_servers(); + foreach($dns_servers as $dns) { + echo "<li>{$dns}</li>"; + } + ?> + </ul> + </td> + </tr> + <?php if ($config['revision']): ?> + <tr> + <th><?=gettext("Last config change");?></td> + <td><?= htmlspecialchars(date("D M j G:i:s T Y", intval($config['revision']['time'])));?></td> + </tr> + <?php endif; ?> + <tr> + <th><?=gettext("State table size");?></td> + <td> + <?php $pfstatetext = get_pfstate(); + $pfstateusage = get_pfstate(true); + ?> + <div class="progress"> + <div class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="<?=$pfstateusage?>" aria-valuemin="0" aria-valuemax="100" style="width: <?=$pfstateusage?>%"> + <span><?=$pfstateusage?>% (<?= htmlspecialchars($pfstatetext)?>)</span> + </div> + </div> + <a href="diag_dump_states.php"><?=gettext("Show states");?></a> + </td> + </tr> + <tr> + <th><?=gettext("MBUF Usage");?></td> + <td> + <?php + $mbufstext = get_mbuf(); + $mbufusage = get_mbuf(true); + ?> + <div class="progress"> + <div class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="<?=$mbufusage?>" aria-valuemin="0" aria-valuemax="100" style="width: <?=$mbufusage?>%"> + <span><?=$mbufusage?>% (<?= htmlspecialchars($mbufstext)?>)</span> + </div> + </div> + </td> + </tr> + <?php if (get_temp() != ""): ?> + <tr> + <th><?=gettext("Temperature");?></td> + <td> + <?php $TempMeter = $temp = get_temp(); ?> + <div id="tempPB"></div> + <span id="tempmeter"><?= $temp."°C"; ?></span> + </td> + </tr> + <?php endif; ?> + <tr> + <th><?=gettext("Load average");?></td> + <td> + <div id="load_average" title="Last 1, 5 and 15 minutes"><?= get_load_average(); ?></div> + </td> + </tr> + <tr> + <th><?=gettext("CPU usage");?></td> + <td> + <div id="cpuPB"></div> + <span id="cpumeter">(Updating in 10 seconds)</span> + </td> + </tr> + <tr> + <th><?=gettext("Memory usage");?></td> + <td> + <?php $memUsage = mem_usage(); ?> + <div class="progress"> + <div class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="<?=$memUsage?>" aria-valuemin="0" aria-valuemax="100" style="width: <?=$memUsage?>%"> + <span><?=$memUsage?>% of <?= sprintf("%.0f", get_single_sysctl('hw.physmem') / (1024*1024)) ?> MB</span> + </div> + </div> + </td> + </tr> + <?php if ($showswap == true): ?> + <tr> + <th><?=gettext("SWAP usage");?></td> + <td> + <?php $swapusage = swap_usage(); ?> + <div class="progress"> + <div class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="<?=$swapusage?>" aria-valuemin="0" aria-valuemax="100" style="width: <?=$swapusage?>%"> + <span><?=$swapusage?>% of <?= sprintf("%.0f", `/usr/sbin/swapinfo -m | /usr/bin/grep -v Device | /usr/bin/awk '{ print $2;}'`) ?> MB</span> + </div> + </div> + </td> + </tr> + <?php endif; ?> + <tr> + <th><?=gettext("Disk usage");?></td> + <td> + <table class="table"> +<?PHP foreach ($filesystems as $fs): ?> + <tr> + <th><?=$fs['mountpoint']?></th> + <td><?=$fs['type'] . ("md" == substr(basename($fs['device']), 0, 2) ? " in RAM" : "")?></td> + <td><?=$fs['total_size']?></td> + <td> + <div class="progress"> + <div class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="<?=$fs['percent_used']?>" aria-valuemin="0" aria-valuemax="100" style="width: <?=$fs['percent_used']?>%"> + <span><?=$fs['percent_used']?>%</span> + </div> + </div> + </td> + </tr> +<?PHP endforeach; ?> + </table> + </td> + </tr> + </tbody> +</table> + +<script> +function systemStatusGetUpdateStatus() { + $.ajax({ + type: 'get', + url: '/widgets/widgets/system_information.widget.php', + data: 'getupdatestatus=1', + dataFilter: function(raw){ + // We reload the entire widget, strip this block of javascript from it + return raw.replace(/<script>([\s\S]*)<\/script>/gi, ''); + }, + dataType: 'html', + success: function(data){ + $('#widget-system_information #updatestatus').html(data); + } + }); +} + +events.push(function(){ + setTimeout('systemStatusGetUpdateStatus()', 4000); +}); +</script>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/widgets/thermal_sensors.widget.php b/src/usr/local/www/widgets/widgets/thermal_sensors.widget.php new file mode 100644 index 0000000..875ccb2 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/thermal_sensors.widget.php @@ -0,0 +1,279 @@ +<?php +/* + $Id: thermal_sensors.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Description: Thermal Sensors Widget. + NOTE: depends on proper config in System >> Advanced >> Miscellaneous tab >> Thermal Sensors section. + + File location: + \usr\local\www\widgets\widgets\ + Depends on: + \usr\local\www\widgets\javascript\thermal_sensors.js + \usr\local\www\widgets\include\thermal_sensors.inc + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +require_once("guiconfig.inc"); +require_once("/usr/local/www/widgets/include/thermal_sensors.inc"); + +//========================================================================= +//called by showThermalSensorsData() (jQuery Ajax call) in thermal_sensors.js +if (isset($_GET["getThermalSensorsData"])) { + //get Thermal Sensors data and return + echo getThermalSensorsData(); + return; +} +//========================================================================= + + +const WIDGETS_CONFIG_SECTION_KEY = "widgets"; +const THERMAL_SENSORS_WIDGET_SUBSECTION_KEY = "thermal_sensors_widget"; + +//default constants +const DEFAULT_WARNING_THRESHOLD = 60; //60 C +const DEFAULT_CRITICAL_THRESHOLD = 70; //70 C +const MIN_THRESHOLD_VALUE = 1; //deg C +const MAX_THRESHOLD_VALUE = 100; //deg C + +//NOTE: keys used in $_POST and $config should match text and checkbox inputs' IDs/names in HTML code section +//========================================================================= +//save widget config settings on POST +if ($_POST) { + saveThresholdSettings($config, $_POST, "thermal_sensors_widget_zone_warning_threshold", "thermal_sensors_widget_zone_critical_threshold"); + saveThresholdSettings($config, $_POST, "thermal_sensors_widget_core_warning_threshold", "thermal_sensors_widget_core_critical_threshold"); + + //handle checkboxes separately + saveGraphDisplaySettings($config, $_POST, "thermal_sensors_widget_show_raw_output"); + saveGraphDisplaySettings($config, $_POST, "thermal_sensors_widget_show_full_sensor_name"); + saveGraphDisplaySettings($config, $_POST, "thermal_sensors_widget_pulsate_warning"); + saveGraphDisplaySettings($config, $_POST, "thermal_sensors_widget_pulsate_critical"); + + //write settings to config file + write_config("Saved thermal_sensors_widget settings via Dashboard."); + header("Location: ../../index.php"); +} + +function saveThresholdSettings(&$configArray, &$postArray, $warningValueKey, $criticalValueKey) { + $warningValue = 0; + $criticalValue = 0; + + if (isset($postArray[$warningValueKey])) { + $warningValue = (int) $postArray[$warningValueKey]; + } + + if (isset($postArray[$criticalValueKey])) { + $criticalValue = (int) $postArray[$criticalValueKey]; + } + + if (($warningValue >= MIN_THRESHOLD_VALUE && $warningValue <= MAX_THRESHOLD_VALUE) && + ($criticalValue >= MIN_THRESHOLD_VALUE && $criticalValue <= MAX_THRESHOLD_VALUE) && + ($warningValue < $criticalValue)) { + //all validated ok, save to config array + $configArray[WIDGETS_CONFIG_SECTION_KEY][THERMAL_SENSORS_WIDGET_SUBSECTION_KEY][$warningValueKey] = $warningValue; + $configArray[WIDGETS_CONFIG_SECTION_KEY][THERMAL_SENSORS_WIDGET_SUBSECTION_KEY][$criticalValueKey] = $criticalValue; + } +} + +function saveGraphDisplaySettings(&$configArray, &$postArray, $valueKey) { + $configArray[WIDGETS_CONFIG_SECTION_KEY][THERMAL_SENSORS_WIDGET_SUBSECTION_KEY][$valueKey] = isset($postArray[$valueKey]) ? 1 : 0; +} + +//========================================================================= +//get Threshold settings from config (apply defaults if missing) +$thermal_sensors_widget_zoneWarningTempThreshold = getThresholdValueFromConfig($config, "thermal_sensors_widget_zone_warning_threshold", DEFAULT_WARNING_THRESHOLD); +$thermal_sensors_widget_zoneCriticalTempThreshold = getThresholdValueFromConfig($config, "thermal_sensors_widget_zone_critical_threshold", DEFAULT_CRITICAL_THRESHOLD); +$thermal_sensors_widget_coreWarningTempThreshold = getThresholdValueFromConfig($config, "thermal_sensors_widget_core_warning_threshold", DEFAULT_WARNING_THRESHOLD); +$thermal_sensors_widget_coreCriticalTempThreshold = getThresholdValueFromConfig($config, "thermal_sensors_widget_core_critical_threshold", DEFAULT_CRITICAL_THRESHOLD); + +//get display settings from config (apply defaults if missing) +$thermal_sensors_widget_showRawOutput = getBoolValueFromConfig($config, "thermal_sensors_widget_show_raw_output", false); +$thermal_sensors_widget_showFullSensorName = getBoolValueFromConfig($config, "thermal_sensors_widget_show_full_sensor_name", false); +$thermal_sensors_widget_pulsateWarning = getBoolValueFromConfig($config, "thermal_sensors_widget_pulsate_warning", true); +$thermal_sensors_widget_pulsateCritical = getBoolValueFromConfig($config, "thermal_sensors_widget_pulsate_critical", true); + +function getThresholdValueFromConfig(&$configArray, $valueKey, $defaultValue) { + + $thresholdValue = $defaultValue; + + if (isset($configArray[WIDGETS_CONFIG_SECTION_KEY][THERMAL_SENSORS_WIDGET_SUBSECTION_KEY][$valueKey])) { + $thresholdValue = (int) $configArray[WIDGETS_CONFIG_SECTION_KEY][THERMAL_SENSORS_WIDGET_SUBSECTION_KEY][$valueKey]; + } + + if ($thresholdValue < MIN_THRESHOLD_VALUE || $thresholdValue > MAX_THRESHOLD_VALUE) { + //set to default if not in allowed range + $thresholdValue = $defaultValue; + } + return $thresholdValue; +} + +function getBoolValueFromConfig(&$configArray, $valueKey, $defaultValue) { + + $boolValue = false; + + if (isset($configArray[WIDGETS_CONFIG_SECTION_KEY][THERMAL_SENSORS_WIDGET_SUBSECTION_KEY][$valueKey])) { + $boolValue = (bool) $configArray[WIDGETS_CONFIG_SECTION_KEY][THERMAL_SENSORS_WIDGET_SUBSECTION_KEY][$valueKey]; + } else { + //set to default if not in allowed range + $boolValue = $defaultValue; + } + return $boolValue; +} + +//========================================================================= +?> + +<script type="text/javascript"> +//<![CDATA[ + //set Thresholds, to be used in thermal_sensors.js + var thermal_sensors_widget_zoneWarningTempThreshold = <?= $thermal_sensors_widget_zoneWarningTempThreshold; ?>; + var thermal_sensors_widget_zoneCriticalTempThreshold = <?= $thermal_sensors_widget_zoneCriticalTempThreshold; ?>; + var thermal_sensors_widget_coreWarningTempThreshold = <?= $thermal_sensors_widget_coreWarningTempThreshold; ?>; + var thermal_sensors_widget_coreCriticalTempThreshold = <?= $thermal_sensors_widget_coreCriticalTempThreshold; ?>; + + //set Graph display settings, to be used in thermal_sensors.js + var thermal_sensors_widget_showRawOutput = <?= $thermal_sensors_widget_showRawOutput ? "true" : "false"; ?>; + var thermal_sensors_widget_showFullSensorName = <?= $thermal_sensors_widget_showFullSensorName ? "true" : "false"; ?>; + var thermal_sensors_widget_pulsateWarning = <?= $thermal_sensors_widget_pulsateWarning ? "true" : "false"; ?>; + var thermal_sensors_widget_pulsateCritical = <?= $thermal_sensors_widget_pulsateCritical ? "true" : "false"; ?>; + + //start showing temp data + //NOTE: the refresh interval will be reset to a proper value in showThermalSensorsData() (thermal_sensors.js). + jQuery(document).ready(function() { + showThermalSensorsData(); + }); +//]]> +</script> + +<input type="hidden" id="thermal_sensors-config" name="thermal_sensors-config" value="" /> +<div id="thermal_sensors-settings" class="widgetconfigdiv" style="display:none;"> + <form action="/widgets/widgets/thermal_sensors.widget.php" method="post" id="iform_thermal_sensors_settings" name="iform_thermal_sensors_settings"> + <table> + <tr> + <td align="left" colspan="2"> + <span style="font-weight: bold" >Thresholds in °C (1 to 100):</span> + </td> + <td align="right" colspan="1"> + <span style="font-weight: bold" >Display settings:</span> + </td> + </tr> + <tr> + <td align="right"> + Zone Warning: + </td> + <td> + <input type="text" maxlength="3" size="3" class="formfld unknown" + name="thermal_sensors_widget_zone_warning_threshold" + id="thermal_sensors_widget_zone_warning_threshold" + value="<?= $thermal_sensors_widget_zoneWarningTempThreshold; ?>" /> + </td> + <td align="right"> + <label for="thermal_sensors_widget_show_raw_output">Show raw output (no graph): </label> + <input type="checkbox" + id="thermal_sensors_widget_show_raw_output" + name="thermal_sensors_widget_show_raw_output" + value="<?= $thermal_sensors_widget_showRawOutput; ?>" <?= ($thermal_sensors_widget_showRawOutput) ? " checked='checked'" : ""; ?> /> + </td> + </tr> + <tr> + <td align="right"> + Zone Critical: + </td> + <td> + <input type="text" maxlength="3" size="3" class="formfld unknown" + name="thermal_sensors_widget_zone_critical_threshold" + id="thermal_sensors_widget_zone_critical_threshold" + value="<?= $thermal_sensors_widget_zoneCriticalTempThreshold; ?>" /> + </td> + <td align="right"> + <label for="thermal_sensors_widget_show_full_sensor_name">Show full sensor name: </label> + <input type="checkbox" + id="thermal_sensors_widget_show_full_sensor_name" + name="thermal_sensors_widget_show_full_sensor_name" + value="<?= $thermal_sensors_widget_showFullSensorName; ?>" <?= ($thermal_sensors_widget_showFullSensorName) ? " checked='checked'" : ""; ?> /> + </td> + </tr> + <tr> + <td align="right"> + Core Warning: + </td> + <td> + <input type="text" maxlength="3" size="3" class="formfld unknown" + name="thermal_sensors_widget_core_warning_threshold" + id="thermal_sensors_widget_core_warning_threshold" + value="<?= $thermal_sensors_widget_coreWarningTempThreshold ?>" /> + </td> + <td align="right"> + <label for="thermal_sensors_widget_pulsate_warning">Pulsate Warning: </label> + <input type="checkbox" + id="thermal_sensors_widget_pulsate_warning" + name="thermal_sensors_widget_pulsate_warning" + value="<?= $thermal_sensors_widget_pulsateWarning; ?>" <?= ($thermal_sensors_widget_pulsateWarning) ? " checked='checked'" : ""; ?> /> + </td> + </tr> + <tr> + <td align="right"> + Core Critical: + </td> + <td> + <input type="text" maxlength="3" size="3" class="formfld unknown" + name="thermal_sensors_widget_core_critical_threshold" + id="thermal_sensors_widget_core_critical_threshold" + value="<?= $thermal_sensors_widget_coreCriticalTempThreshold ?>" /> + </td> + <td align="right"> + <label for="thermal_sensors_widget_pulsate_critical">Pulsate Critical: </label> + <input type="checkbox" + id="thermal_sensors_widget_pulsate_critical" + name="thermal_sensors_widget_pulsate_critical" + value="<?= $thermal_sensors_widget_pulsateCritical; ?>" <?= ($thermal_sensors_widget_pulsateCritical) ? " checked='checked'" : ""; ?> /> + </td> + </tr> + <tr> + <td align="right" colspan="3"> + <input type="submit" id="thermal_sensors_widget_submit" name="thermal_sensors_widget_submit" class="formbtn" value="Save" /> + </td> + </tr> + <tr> + <td align="left" colspan="3"> + <span>* You can configure a proper Thermal Sensor / Module under <br /> + <a href="system_advanced_misc.php">System > Advanced > Miscellaneous : Thermal Sensors section</a>.</span> + </td> + </tr> + </table> + </form> +</div> + +<div style="padding: 5px"> + <div id="thermalSensorsContainer" class="listr"> + (Updating...)<br /><br /> + </div> +</div> + +<!-- needed to display the widget settings menu --> +<script type="text/javascript"> +//<![CDATA[ + textlink = jQuery("#thermal_sensors-configure"); + textlink.css({display: "inline"}); +//]]> +</script> diff --git a/src/usr/local/www/widgets/widgets/traffic_graphs.widget.php b/src/usr/local/www/widgets/widgets/traffic_graphs.widget.php new file mode 100644 index 0000000..2689e32 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/traffic_graphs.widget.php @@ -0,0 +1,161 @@ +<?php +/* + traffic_graphs.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Copyright 2007 Scott Dale + Part of pfSense widgets (https://www.pfsense.org) + originally based on m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2004-2005 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net> + and Jonathan Watt <jwatt@jwatt.org>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("pfsense-utils.inc"); +require_once("functions.inc"); + +$first_time = false; +if (!is_array($config["widgets"]["trafficgraphs"])) { + $first_time = true; + $config["widgets"]["trafficgraphs"] = array(); +} +$a_config = &$config["widgets"]["trafficgraphs"]; + +if (!is_array($a_config["shown"])) { + $a_config["shown"] = array(); +} +if (!is_array($a_config["shown"]["item"])) { + $a_config["shown"]["item"] = array(); +} + +$ifdescrs = get_configured_interface_with_descr(); +if (isset($config['ipsec']['enable'])) { + $ifdescrs['enc0'] = "IPsec"; +} + +if ($_POST) { + if (isset($_POST["refreshinterval"])) { + $a_config["refreshinterval"] = $_POST["refreshinterval"]; + } + if (isset($_POST["scale_type"])) { + $a_config["scale_type"] = $_POST["scale_type"]; + } + $a_config["shown"]["item"] = array(); + foreach ($ifdescrs as $ifname => $ifdescr) { + $state = $_POST["shown"][$ifname]; + if ($state === "show") { + $a_config["shown"]["item"][] = $ifname; + } + } + write_config("Updated traffic graph settings via dashboard."); + header("Location: /"); + exit(0); +} + +$shown = array(); +foreach ($a_config["shown"]["item"] as $if) { + $shown[$if] = true; +} +if ($first_time) { + $keys = array_keys($ifdescrs); + $shown[$keys[0]] = true; +} + +if (isset($a_config["refreshinterval"])) { + $refreshinterval = $a_config["refreshinterval"]; +} else { + $refreshinterval = 10; +} + +if (isset($a_config["scale_type"])) { + $scale_type = $a_config["scale_type"]; +} else { + $scale_type = "up"; +} + +$graphcounter = 0; +foreach ($ifdescrs as $ifname => $ifdescr): + $ifinfo = get_interface_info($ifname); + if ($shown[$ifname]) { + $mingraphbutton = "inline"; + $showgraphbutton = "none"; + $graphdisplay = "inline"; + $interfacevalue = "show"; + $graphcounter++; + } else { + $mingraphbutton = "none"; + $showgraphbutton = "inline"; + $graphdisplay = "none"; + $interfacevalue = "hide"; + } + if ($ifinfo['status'] != "down"): +?> + <div style="display:<?=$graphdisplay?>"> + <object data="graph.php?ifnum=<?=$ifname?>&ifname=<?=rawurlencode($ifdescr)?>&timeint=<?=$refreshinterval?>&initdelay=<?=$graphcounter * 2?>"> + <param name="id" value="graph" /> + <param name="type" value="image/svg+xml" /> + <param name="pluginspage" value="http://www.adobe.com/svg/viewer/install/auto" /> + </object> + </div> +<?php endif; ?> +<?php endforeach; ?> + +<!-- close the body we're wrapped in and add a configuration-panel --> +</div><div class="panel-footer collapse"> + +<form action="/widgets/widgets/traffic_graphs.widget.php" method="post" class="form-horizontal"> + <?php foreach ($ifdescrs as $ifname => $ifdescr): ?> + <input type="hidden" name="shown[<?= $ifname?>]" value="<?= $shown[$ifname] ? "show" : "hide"?>" /> + <?php endforeach; ?> + <div class="form-group"> + <label for="scale_type_up" class="col-sm-3 control-label">Default Autoscale</label> + <div class="col-sm-6 checkbox"> + <label> + <input name="scale_type" type="radio" id="scale_type_up" value="up" <?=($config["widgets"]["trafficgraphs"]["scale_type"]=="follow" ? '' : 'checked="checked"')?> /> + up + </label> + <label> + <input name="scale_type" type="radio" id="scale_type_follow" value="up" <?=($config["widgets"]["trafficgraphs"]["scale_type"]=="follow" ? 'checked="checked"' : '')?> /> + follow + </label> + </div> + </div> + + <div class="form-group"> + <label for="refreshinterval" class="col-sm-3 control-label">Refresh Interval</label> + <div class="col-sm-6"> + <input type="number" name="refreshinterval" value="<?=$refreshinterval?>" min="1" max="30" class="form-control" /> + </div> + </div> + + <div class="form-group"> + <div class="col-sm-offset-3 col-sm-6"> + <button type="submit" class="btn btn-default">Save</button> + </div> + </div> +</form>
\ No newline at end of file diff --git a/src/usr/local/www/widgets/widgets/wake_on_lan.widget.php b/src/usr/local/www/widgets/widgets/wake_on_lan.widget.php new file mode 100644 index 0000000..5715044 --- /dev/null +++ b/src/usr/local/www/widgets/widgets/wake_on_lan.widget.php @@ -0,0 +1,80 @@ +<?php +/* + wake_on_lan.widget.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Copyright (C) 2010 Yehuda Katz + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INClUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$nocsrf = true; + +require_once("guiconfig.inc"); +require_once("/usr/local/www/widgets/include/wake_on_lan.inc"); + +if (is_array($config['wol']['wolentry'])) { + $wolcomputers = $config['wol']['wolentry']; +} else { + $wolcomputers = array(); +} + +?> +<table> + <tr> + <?php + echo '<td class="widgetsubheader" align="center">' . gettext("Computer / Device") . '</td>'; + echo '<td class="widgetsubheader" align="center">' . gettext("Interface") . '</td>'; + echo '<td class="widgetsubheader" align="center">' . gettext("Status") . '</td>'; + ?> + <td class="widgetsubheader"> </td> + </tr> +<?php + +if (count($wolcomputers) > 0) { + foreach($wolcomputers as $wolent) { + echo '<tr><td class="listlr">' . $wolent['descr'] . '<br />' . $wolent['mac'] . '</td>' . "\n"; + echo '<td class="listr">' . convert_friendly_interface_to_friendly_descr($wolent['interface']) . '</td>' . "\n"; + + $is_active = exec("/usr/sbin/arp -an |/usr/bin/grep {$wolent['mac']}| /usr/bin/wc -l|/usr/bin/awk '{print $1;}'"); + if($is_active == 1) { + echo '<td class="listr" align="center">' . "\n"; + echo "<img src=\"/themes/" . $g["theme"] . "/images/icons/icon_pass.gif\" alt=\"pass\" /> " . gettext("Online") . "</td>\n"; + } else { + echo '<td class="listbg" align="center">' . "\n"; + echo "<img src=\"/themes/" . $g["theme"] . "/images/icons/icon_block.gif\" alt=\"block\" /> <font color=\"white\">" . gettext("Offline") . "</font></td>\n"; + } + echo '<td valign="middle" class="list nowrap">'; + /*if($is_active) { */ + /* Will always show wake-up button even if pfsense thinks it is awake */ + /* } else { */ + echo "<a href='services_wol.php?mac={$wolent['mac']}&if={$wolent['interface']}'> "; + echo "<img title='" . gettext("Wake Up") . "' border='0' src='./themes/".$g['theme']."/images/icons/icon_wol_all.gif' alt='wol' /></a>\n"; + /* } */ + echo "</td></tr>\n"; + } +} else { + echo "<tr><td colspan=\"4\" align=\"center\">" . gettext("No saved WoL addresses") . ".</td></tr>\n"; +} +?> +</table> +<center><a href="status_dhcp_leases.php" class="navlink">DHCP Leases Status</a></center> diff --git a/src/usr/local/www/wizard.php b/src/usr/local/www/wizard.php new file mode 100644 index 0000000..adab493 --- /dev/null +++ b/src/usr/local/www/wizard.php @@ -0,0 +1,1061 @@ +<?php +/* $Id$ */ +/* + wizard.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2010 Ermal Luçi + Copyright (C) 2004 Scott Ullrich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-pfsensewizardsubsystem +##|*NAME=pfSense wizard subsystem page +##|*DESCR=Allow access to the 'pfSense wizard subsystem' page. +##|*MATCH=wizard.php* +##|-PRIV + + +require("globals.inc"); +require("guiconfig.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("shaper.inc"); +require_once("rrd.inc"); + +function gentitle_pkg($pgname) { + global $config; + return $config['system']['hostname'] . "." . $config['system']['domain'] . " - " . $pgname; +} + +global $g; + +$stepid = htmlspecialchars($_GET['stepid']); +if (isset($_POST['stepid'])) { + $stepid = htmlspecialchars($_POST['stepid']); +} +if (!$stepid) { + $stepid = "0"; +} + +$xml = htmlspecialchars($_GET['xml']); +if ($_POST['xml']) { + $xml = htmlspecialchars($_POST['xml']); +} + +if (empty($xml)) { + $xml = "not_defined"; + print_info_box_np(sprintf(gettext("ERROR: Could not open %s."), $xml)); + die; +} else { + if (file_exists("{$g['www_path']}/wizards/{$xml}")) { + $pkg = parse_xml_config_pkg("{$g['www_path']}/wizards/" . $xml, "pfsensewizard"); + } else { + print_info_box_np(sprintf(gettext("ERROR: Could not open %s."), $xml)); + die; + } +} + +if (!is_array($pkg)) { + print_info_box_np(sprintf(gettext("ERROR: Could not parse %s/wizards/%s file."), $g['www_path'], $xml)); + die; +} + +$title = preg_replace("/pfSense/i", $g['product_name'], $pkg['step'][$stepid]['title']); +$description = preg_replace("/pfSense/i", $g['product_name'], $pkg['step'][$stepid]['description']); +$totalsteps = $pkg['totalsteps']; + +if ($pkg['includefile']) { + require_once($pkg['includefile']); +} + +if ($pkg['step'][$stepid]['includefile']) { + require_once($pkg['step'][$stepid]['includefile']); +} + +if ($pkg['step'][$stepid]['stepsubmitbeforesave']) { + eval($pkg['step'][$stepid]['stepsubmitbeforesave']); +} + +if ($_POST && !$input_errors) { + foreach ($pkg['step'][$stepid]['fields']['field'] as $field) { + if (!empty($field['bindstofield']) and $field['type'] <> "submit") { + $fieldname = $field['name']; + $fieldname = str_replace(" ", "", $fieldname); + $fieldname = strtolower($fieldname); + // update field with posted values. + if ($field['unsetfield'] <> "") { + $unset_fields = "yes"; + } else { + $unset_fields = ""; + } + if ($field['arraynum'] <> "") { + $arraynum = $field['arraynum']; + } else { + $arraynum = ""; + } + + update_config_field($field['bindstofield'], $_POST[$fieldname], $unset_fields, $arraynum, $field['type']); + } + + } + // run custom php code embedded in xml config. + if ($pkg['step'][$stepid]['stepsubmitphpaction'] <> "") { + eval($pkg['step'][$stepid]['stepsubmitphpaction']); + } + if (!$input_errors) { + write_config(); + } + $stepid++; + if ($stepid > $totalsteps) { + $stepid = $totalsteps; + } +} + +function update_config_field($field, $updatetext, $unset, $arraynum, $field_type) { + global $config; + $field_split = explode("->", $field); + foreach ($field_split as $f) { + $field_conv .= "['" . $f . "']"; + } + if ($field_conv == "") { + return; + } + if ($arraynum <> "") { + $field_conv .= "[" . $arraynum . "]"; + } + if (($field_type == "checkbox" and $updatetext <> "on") || $updatetext == "") { + /* + * item is a checkbox, it should have the value "on" + * if it was checked + */ + $var = "\$config{$field_conv}"; + $text = "if (isset({$var})) unset({$var});"; + eval($text); + return; + } + + if ($field_type == "interfaces_selection") { + $var = "\$config{$field_conv}"; + $text = "if (isset({$var})) unset({$var});"; + $text .= "\$config" . $field_conv . " = \"" . $updatetext . "\";"; + eval($text); + return; + } + + if ($unset == "yes") { + $text = "unset(\$config" . $field_conv . ");"; + eval($text); + } + $text = "\$config" . $field_conv . " = \"" . addslashes($updatetext) . "\";"; + eval($text); +} + +$title = preg_replace("/pfSense/i", $g['product_name'], $pkg['step'][$stepid]['title']); +$description = preg_replace("/pfSense/i", $g['product_name'], $pkg['step'][$stepid]['description']); + +// handle before form display event. +do { + $oldstepid = $stepid; + if ($pkg['step'][$stepid]['stepbeforeformdisplay'] <> "") { + eval($pkg['step'][$stepid]['stepbeforeformdisplay']); + } +} while ($oldstepid != $stepid); + +$closehead = false; +$pgtitle = array($title); +include("head.inc"); + +if (file_exists("/usr/local/www/themes/{$g['theme']}/wizard.css")) { + echo "<link type=\"text/css\" rel=\"stylesheet\" href=\"/themes/{$g['theme']}/wizard.css\" media=\"all\" />\n"; +} else { + echo "<link type=\"text/css\" rel=\"stylesheet\" href=\"/themes/{$g['theme']}/all.css\" media=\"all\" />"; +} +?> +</head> +<body link="#0000CC" vlink="#0000CC" alink="#0000CC" > + +<?php if ($pkg['step'][$stepid]['fields']['field'] <> "") { ?> +<script type="text/javascript"> +//<![CDATA[ + +function FieldValidate(userinput, regexp, message) { + if (!userinput.match(regexp)) { + alert(message); + } +} + +function enablechange() { +<?php + foreach ($pkg['step'][$stepid]['fields']['field'] as $field) { + if (isset($field['enablefields']) or isset($field['checkenablefields'])) { + print "\t" . 'if (document.iform.' . strtolower($field['name']) . '.checked) {' . "\n"; + if (isset($field['enablefields'])) { + $enablefields = explode(',', $field['enablefields']); + foreach ($enablefields as $enablefield) { + $enablefield = strtolower($enablefield); + print "\t\t" . 'document.iform.' . $enablefield . '.disabled = 0;' . "\n"; + } + } + if (isset($field['checkenablefields'])) { + $checkenablefields = explode(',', $field['checkenablefields']); + foreach ($checkenablefields as $checkenablefield) { + $checkenablefield = strtolower($checkenablefield); + print "\t\t" . 'document.iform.' . $checkenablefield . '.checked = 0;' . "\n"; + } + } + print "\t" . '} else {' . "\n"; + if (isset($field['enablefields'])) { + $enablefields = explode(',', $field['enablefields']); + foreach ($enablefields as $enablefield) { + $enablefield = strtolower($enablefield); + print "\t\t" . 'document.iform.' . $enablefield . '.disabled = 1;' . "\n"; + } + } + if (isset($field['checkenablefields'])) { + $checkenablefields = explode(',', $field['checkenablefields']); + foreach ($checkenablefields as $checkenablefield) { + $checkenablefield = strtolower($checkenablefield); + print "\t\t" . 'document.iform.' . $checkenablefield . '.checked = 1;' . "\n"; + } + } + print "\t" . '}' . "\n"; + } + } +?> +} + +function disablechange() { +<?php + foreach ($pkg['step'][$stepid]['fields']['field'] as $field) { + if (isset($field['disablefields']) or isset($field['checkdisablefields'])) { + print "\t" . 'if (document.iform.' . strtolower($field['name']) . '.checked) {' . "\n"; + if (isset($field['disablefields'])) { + $enablefields = explode(',', $field['disablefields']); + foreach ($enablefields as $enablefield) { + $enablefield = strtolower($enablefield); + print "\t\t" . 'document.iform.' . $enablefield . '.disabled = 1;' . "\n"; + } + } + if (isset($field['checkdisablefields'])) { + $checkenablefields = explode(',', $field['checkdisablefields']); + foreach ($checkenablefields as $checkenablefield) { + $checkenablefield = strtolower($checkenablefield); + print "\t\t" . 'document.iform.' . $checkenablefield . '.checked = 1;' . "\n"; + } + } + print "\t" . '} else {' . "\n"; + if (isset($field['disablefields'])) { + $enablefields = explode(',', $field['disablefields']); + foreach ($enablefields as $enablefield) { + $enablefield = strtolower($enablefield); + print "\t\t" . 'document.iform.' . $enablefield . '.disabled = 0;' . "\n"; + } + } + if (isset($field['checkdisablefields'])) { + $checkenablefields = explode(',', $field['checkdisablefields']); + foreach ($checkenablefields as $checkenablefield) { + $checkenablefield = strtolower($checkenablefield); + print "\t\t" . 'document.iform.' . $checkenablefield . '.checked = 0;' . "\n"; + } + } + print "\t" . '}' . "\n"; + } + } +?> +} + +function showchange() { +<?php + foreach ($pkg['step'][$stepid]['fields']['field'] as $field) { + if (isset($field['showfields'])) { + print "\t" . 'if (document.iform.' . strtolower($field['name']) . '.checked == false) {' . "\n"; + if (isset($field['showfields'])) { + $showfields = explode(',', $field['showfields']); + foreach ($showfields as $showfield) { + $showfield = strtolower($showfield); + //print "\t\t" . 'document.iform.' . $showfield . ".display =\"none\";\n"; + print "\t\t jQuery('#". $showfield . "').hide();"; + } + } + print "\t" . '} else {' . "\n"; + if (isset($field['showfields'])) { + $showfields = explode(',', $field['showfields']); + foreach ($showfields as $showfield) { + $showfield = strtolower($showfield); + #print "\t\t" . 'document.iform.' . $showfield . ".display =\"\";\n"; + print "\t\t jQuery('#". $showfield . "').show();"; + } + } + print "\t" . '}' . "\n"; + } + } +?> +} +//]]> +</script> +<?php } ?> + +<form action="wizard.php" method="post" name="iform" id="iform"> +<input type="hidden" name="xml" value="<?= htmlspecialchars($xml) ?>" /> +<input type="hidden" name="stepid" value="<?= htmlspecialchars($stepid) ?>" /> + +<center> + + <br /> + +<?php + if ($title == "Reload in progress") { + $ip = fixup_string("\$myurl"); + } else { + $ip = "/"; + } + echo "<a href='$ip'>"; +?> +<img border="0" src="./themes/<?= $g['theme']; ?>/images/logo.gif" alt="logo" /></a> +<p> </p> +<div style="width:800px;background-color:#ffffff" id="roundme"> +<?php + if ($input_errors) { + print_input_errors($input_errors); + } + if ($savemsg) { + print_info_box($savemsg); + } + if ($_GET['message'] != "") { + print_info_box(htmlspecialchars($_GET['message'])); + } + if ($_POST['message'] != "") { + print_info_box(htmlspecialchars($_POST['message'])); + } +?> +<table bgcolor="#ffffff" width="95%" border="0" cellspacing="0" cellpadding="2" summary="wizard"> + <!-- wizard goes here --> + <tr><td> </td></tr> + <tr> + <td class="tabcont"> + <table width="100%" border="0" cellpadding="6" cellspacing="0" summary="main area"> + <tr> + <td colspan="2" align="center"><font size="2"><b><?= fixup_string($description) ?></b></font></td> + </tr> + <tr> + <td> </td> + </tr> +<?php + if (!$pkg['step'][$stepid]['disableheader']) { + echo "<tr><td colspan=\"2\" class=\"listtopic\">" . fixup_string($title) . "</td></tr>"; + } +?> + +<?php + $inputaliases = array(); + if ($pkg['step'][$stepid]['fields']['field'] <> "") { + foreach ($pkg['step'][$stepid]['fields']['field'] as $field) { + + $value = $field['value']; + $name = $field['name']; + + $name = preg_replace("/\s+/", "", $name); + $name = strtolower($name); + + if ($field['bindstofield'] <> "") { + $arraynum = ""; + $field_conv = ""; + $field_split = explode("->", $field['bindstofield']); + // arraynum is used in cases where there is an array of the same field + // name such as dnsserver (2 of them) + if ($field['arraynum'] <> "") { + $arraynum = "[" . $field['arraynum'] . "]"; + } + foreach ($field_split as $f) { + $field_conv .= "['" . $f . "']"; + } + if ($field['type'] == "checkbox") { + $toeval = "if (isset(\$config" . $field_conv . $arraynum . ")) { \$value = \$config" . $field_conv . $arraynum . "; if (empty(\$value)) \$value = true; }"; + } else { + $toeval = "if (isset(\$config" . $field_conv . $arraynum . ")) \$value = \$config" . $field_conv . $arraynum . ";"; + } + eval($toeval); + } + + if (!$field['combinefieldsend']) { + echo "<tr>"; + } + + switch ($field['type']) { + case "input": + if ($field['displayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo $field['displayname']; + echo ":</td>\n"; + } else if (!$field['dontdisplayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo fixup_string($field['name']); + echo ":</td>\n"; + } + if (!$field['dontcombinecells']) { + echo "<td class=\"vtable\">\n"; + } + + echo "<input class='formfld unknown' id='" . $name . "' name='" . $name . "' value=\"" . htmlspecialchars($value) . "\""; + if ($field['size']) { + echo " size='" . $field['size'] . "' "; + } + if ($field['validate']) { + echo " onchange='FieldValidate(this.value, \"{$field['validate']}\", \"{$field['message']}\");'"; + } + echo " />\n"; + + if ($field['description'] <> "") { + echo "<br /> " . $field['description']; + } + break; + case "text": + echo "<td colspan=\"2\" align=\"center\" class=\"vncell\">\n"; + if ($field['description'] <> "") { + echo "<center><br /> " . $field['description'] . "</center>"; + } + break; + case "inputalias": + if ($field['displayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo $field['displayname']; + echo ":</td>\n"; + } else if (!$field['dontdisplayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo fixup_string($field['name']); + echo ":</td>\n"; + } + if (!$field['dontcombinecells']) { + echo "<td class=\"vtable\">\n"; + } + + $inputaliases[] = $name; + echo "<input class='formfldalias' autocomplete='off' id='" . $name . "' name='" . $name . "' value=\"" . htmlspecialchars($value) . "\""; + if ($field['size']) { + echo " size='" . $field['size'] . "' "; + } + if ($field['validate']) { + echo " onchange='FieldValidate(this.value, \"{$field['validate']}\", \"{$field['message']}\");'"; + } + echo " />\n"; + + if ($field['description'] <> "") { + echo "<br /> " . $field['description']; + } + break; + case "interfaces_selection": + case "interface_select": + $size = ""; + $multiple = ""; + $name = strtolower($name); + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo fixup_string($field['displayname'] ? $field['displayname'] : $field['name']) . ":\n"; + echo "</td>"; + echo "<td class=\"vtable\">\n"; + if ($field['size'] <> "") { + $size = "size=\"{$field['size']}\""; + } + if ($field['multiple'] <> "" and $field['multiple'] <> "0") { + $multiple = "multiple=\"multiple\""; + $name .= "[]"; + } + echo "<select class='formselect' id='{$name}' name='{$name}' {$size} {$multiple}>\n"; + if ($field['add_to_interfaces_selection'] <> "") { + $SELECTED = ""; + if ($field['add_to_interfaces_selection'] == $value) { + $SELECTED = " selected=\"selected\""; + } + echo "<option value='" . $field['add_to_interfaces_selection'] . "'" . $SELECTED . ">" . $field['add_to_interfaces_selection'] . "</option>\n"; + } + if ($field['type'] == "interface_select") { + $interfaces = get_interface_list(); + } else { + $interfaces = get_configured_interface_with_descr(); + } + foreach ($interfaces as $ifname => $iface) { + if ($field['type'] == "interface_select") { + $iface = $ifname; + if ($iface['mac']) { + $iface .= " ({$iface['mac']})"; + } + } + $SELECTED = ""; + if ($value == $ifname) $SELECTED = " selected=\"selected\""; + $to_echo = "<option value='" . $ifname . "'" . $SELECTED . ">" . $iface . "</option>\n"; + $to_echo .= "<!-- {$value} -->"; + $canecho = 0; + if ($field['interface_filter'] <> "") { + if (stristr($ifname, $field['interface_filter']) == true) { + $canecho = 1; + } + } else { + $canecho = 1; + } + if ($canecho == 1) { + echo $to_echo; + } + } + echo "</select>\n"; + + if ($field['description'] <> "") { + echo "<br /> " . $field['description']; + } + + break; + case "password": + if ($field['displayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo $field['displayname']; + echo ":</td>\n"; + } else if (!$field['dontdisplayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo fixup_string($field['name']); + echo ":</td>\n"; + } + if (!$field['dontcombinecells']) { + echo "<td class=\"vtable\">"; + } + echo "<input class='formfld pwd' id='" . $name . "' name='" . $name . "' value=\"" . htmlspecialchars($value) . "\" type='password' "; + if ($field['size']) { + echo " size='" . $field['size'] . "' "; + } + echo " />\n"; + + if ($field['description'] <> "") { + echo "<br /> " . $field['description']; + } + + break; + case "certca_selection": + $size = ""; + $multiple = ""; + $name = strtolower($name); + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo fixup_string($field['displayname'] ? $field['displayname'] : $field['name']) . ":\n"; + echo "</td>"; + echo "<td class=\"vtable\">\n"; + if ($field['size'] <> "") { + $size = "size=\"{$field['size']}\""; + } + echo "<select id='{$name}' name='{$name}' {$size}>\n"; + if ($field['add_to_certca_selection'] <> "") { + $SELECTED = ""; + if ($field['add_to_certca_selection'] == $value) { + $SELECTED = " selected=\"selected\""; + } + echo "<option value='" . $field['add_to_certca_selection'] . "'" . $SELECTED . ">" . $field['add_to_certca_selection'] . "</option>\n"; + } + foreach ($config['ca'] as $ca) { + $name = htmlspecialchars($ca['descr']); + $SELECTED = ""; + if ($value == $name) $SELECTED = " selected=\"selected\""; + $to_echo = "<option value='" . $ca['refid'] . "'" . $SELECTED . ">" . $name . "</option>\n"; + $to_echo .= "<!-- {$value} -->"; + $canecho = 0; + if ($field['certca_filter'] <> "") { + if (stristr($name, $field['certca_filter']) == true) { + $canecho = 1; + } + } else { + $canecho = 1; + } + if ($canecho == 1) { + echo $to_echo; + } + } + echo "</select>\n"; + + if ($field['description'] <> "") { + echo "<br /> " . $field['description']; + } + + break; + case "cert_selection": + $size = ""; + $multiple = ""; + $name = strtolower($name); + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo fixup_string($field['displayname'] ? $field['displayname'] : $field['name']) . ":\n"; + echo "</td>"; + echo "<td class=\"vtable\">\n"; + if ($field['size'] <> "") { + $size = "size=\"{$field['size']}\""; + } + echo "<select id='{$name}' name='{$name}' {$size}>\n"; + if ($field['add_to_cert_selection'] <> "") { + $SELECTED = ""; + if ($field['add_to_cert_selection'] == $value) { + $SELECTED = " selected=\"selected\""; + } + echo "<option value='" . $field['add_to_cert_selection'] . "'" . $SELECTED . ">" . $field['add_to_cert_selection'] . "</option>\n"; + } + foreach ($config['cert'] as $ca) { + if (stristr($ca['descr'], "webconf")) { + continue; + } + $name = htmlspecialchars($ca['descr']); + $SELECTED = ""; + if ($value == $name) { + $SELECTED = " selected=\"selected\""; + } + $to_echo = "<option value='" . $ca['refid'] . "'" . $SELECTED . ">" . $name . "</option>\n"; + $to_echo .= "<!-- {$value} -->"; + $canecho = 0; + if ($field['cert_filter'] <> "") { + if (stristr($name, $field['cert_filter']) == true) { + $canecho = 1; + } + } else { + $canecho = 1; + } + if ($canecho == 1) { + echo $to_echo; + } + } + echo "</select>\n"; + + if ($field['description'] <> "") { + echo "<br /> " . $field['description']; + } + + break; + case "select": + if ($field['displayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo $field['displayname']; + echo ":</td>\n"; + } else if (!$field['dontdisplayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo fixup_string($field['name']); + echo ":</td>\n"; + } + if ($field['size']) { + $size = " size='" . $field['size'] . "' "; + } + if ($field['multiple'] == "yes") { + $multiple = "multiple=\"multiple\" "; + } + if (!$field['dontcombinecells']) { + echo "<td class=\"vtable\">\n"; + } + $onchange = ""; + foreach ($field['options']['option'] as $opt) { + if ($opt['enablefields'] <> "") { + $onchange = "onchange=\"enableitems(this.selectedIndex);\" "; + } + } + echo "<select class='formselect' " . $onchange . $multiple . $size . "id='" . $name . "' name='" . $name . "'>\n"; + foreach ($field['options']['option'] as $opt) { + $selected = ""; + if ($value == $opt['value']) { + $selected = " selected=\"selected\""; + } + echo "\t<option value='" . $opt['value'] . "'" . $selected . ">"; + if ($opt['displayname']) { + echo $opt['displayname']; + } else { + echo $opt['name']; + } + echo "</option>\n"; + } + echo "</select>\n"; + echo "<!-- {$value} -->\n"; + + if ($field['description'] <> "") { + echo $field['description']; + } + + break; + case "textarea": + if ($field['displayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo $field['displayname']; + echo ":</td>\n"; + } else if (!$field['dontdisplayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo fixup_string($field['name']); + echo ":</td>"; + } + if (!$field['dontcombinecells']) { + echo "<td class=\"vtable\">"; + } + echo "<textarea class='formpre' id='" . $name . "' name='" . $name . "'"; + if ($field['rows']) { + echo " rows='" . $field['rows'] . "' "; + } + if ($field['cols']) { + echo " cols='" . $field['cols'] . "' "; + } + echo ">" . $value . "</textarea>\n"; + + + if ($field['description'] <> "") { + echo "<br /> " . $field['description']; + } + + break; + case "submit": + echo "<td> <br /></td></tr>"; + echo "<tr><td colspan=\"2\" align=\"center\">"; + echo "<input type='submit' name='" . $name . "' value=\"" . htmlspecialchars($field['name']) . "\" />\n"; + + if ($field['description'] <> "") { + echo "<br /> " . $field['description']; + } + + break; + case "listtopic": + echo "<td> </td></tr>"; + echo "<tr><td colspan=\"2\" class=\"listtopic\">" . $field['name'] . "<br />\n"; + + break; + case "subnet_select": + if ($field['displayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo $field['displayname']; + echo ":</td>\n"; + } else if (!$field['dontdisplayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo fixup_string($field['name']); + echo ":</td>"; + } + if (!$field['dontcombinecells']) { + echo "<td class=\"vtable\">"; + } + echo "<select class='formselect' name='{$name}'>\n"; + for ($x=1; $x<33; $x++) { + $CHECKED = ""; + if ($value == $x) { + $CHECKED = " selected=\"selected\""; + } + if ($x <> 31) { + echo "<option value='{$x}' {$CHECKED}>{$x}</option>\n"; + } + } + echo "</select>\n"; + + if ($field['description'] <> "") { + echo "<br /> " . $field['description']; + } + + break; + case "timezone_select": + exec('/usr/bin/tar -tzf /usr/share/zoneinfo.tgz', $timezonelist); + $timezonelist = array_filter($timezonelist, 'is_timezone'); + sort($timezonelist); + + /* kill carriage returns */ + for ($x = 0; $x < count($timezonelist); $x++) { + $timezonelist[$x] = str_replace("\n", "", $timezonelist[$x]); + } + + if ($field['displayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo $field['displayname']; + echo ":</td>\n"; + } else if (!$field['dontdisplayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo fixup_string($field['name']); + echo ":</td>"; + } + if (!$field['dontcombinecells']) { + echo "<td class=\"vtable\">"; + } + echo "<select class='formselect' name='{$name}'>\n"; + foreach ($timezonelist as $tz) { + if (strstr($tz, "GMT")) { + continue; + } + $SELECTED = ""; + if ($value == $tz) { + $SELECTED = " selected=\"selected\""; + } + echo "<option value=\"" . htmlspecialchars($tz) . "\" {$SELECTED}>"; + echo htmlspecialchars($tz); + echo "</option>\n"; + } + echo "</select>\n"; + + if ($field['description'] <> "") { + echo "<br /> " . $field['description']; + } + + break; + case "checkbox": + if ($field['displayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo $field['displayname']; + echo ":</td>\n"; + } else if (!$field['dontdisplayname']) { + echo "<td width=\"22%\" align=\"right\" class=\"vncellreq\">\n"; + echo $field['name']; + echo ":</td>"; + } + $checked = ""; + if ($value <> "") { + $checked = " checked=\"checked\""; + } + echo "<td class=\"vtable\"><input value=\"on\" type='checkbox' id='" . $name . "' name='" . $name . "' " . $checked; + if (isset($field['enablefields']) or isset($field['checkenablefields'])) { + echo " onclick=\"enablechange()\""; + } else if (isset($field['disablefields']) or isset($field['checkdisablefields'])) { + echo " onclick=\"disablechange()\""; + } + echo " />\n"; + + if ($field['description'] <> "") { + echo $field['description']; + } + + break; + } + + if ($field['typehint'] <> "") { + echo $field['typehint']; + } + if ($field['warning'] <> "") { + echo "<br /><b><font color=\"red\">" . $field['warning'] . "</font></b>"; + } + + if (!$field['combinefieldsbegin']) { + if (!$field['dontcombinecells']) { + echo "</td>"; + } + + echo "</tr>\n"; + } + + } + } +?> + </table> + </td> + </tr> +</table> +<br /> +</div> +</center> +</form> +<script type="text/javascript"> +//<![CDATA[ + if (typeof ext_change != 'undefined') { + ext_change(); + } + if (typeof proto_change != 'undefined') { + ext_change(); + } + if (typeof proto_change != 'undefined') { + proto_change(); + } + +<?php + $isfirst = 0; + $aliases = ""; + $addrisfirst = 0; + $aliasesaddr = ""; + if ($config['aliases']['alias'] <> "" and is_array($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $alias_name) { + if ($isfirst == 1) { + $aliases .= ","; + } + $aliases .= "'" . $alias_name['name'] . "'"; + $isfirst = 1; + } + } +?> + + var customarray=new Array(<?php echo $aliases; ?>); + + window.onload = function () { + +<?php + $counter = 0; + foreach ($inputaliases as $alias) { + echo "var oTextbox$counter = new AutoSuggestControl(document.getElementById(\"$alias\"), new StateSuggestions(customarray));\n"; + $counter++; + } +?> + + } + +//]]> +</script> + +<?php + +$fieldnames_array = Array(); +if ($pkg['step'][$stepid]['disableallfieldsbydefault'] <> "") { + // create a fieldname loop that can be used with javascript + // hide and enable features. + echo "\n<script type=\"text/javascript\">\n"; + echo "//<![CDATA[\n"; + echo "function disableall() {\n"; + foreach ($pkg['step'][$stepid]['fields']['field'] as $field) { + if ($field['type'] <> "submit" and $field['type'] <> "listtopic") { + if (!$field['donotdisable'] <> "") { + array_push($fieldnames_array, $field['name']); + $fieldname = preg_replace("/\s+/", "", $field['name']); + $fieldname = strtolower($fieldname); + echo "\tdocument.forms[0]." . $fieldname . ".disabled = 1;\n"; + } + } + } + echo "}\ndisableall();\n"; + echo "function enableitems(selectedindex) {\n"; + echo "disableall();\n"; + $idcounter = 0; + if ($pkg['step'][$stepid]['fields']['field'] <> "") { + echo "\tswitch (selectedindex) {\n"; + foreach ($pkg['step'][$stepid]['fields']['field'] as $field) { + if ($field['options']['option'] <> "") { + foreach ($field['options']['option'] as $opt) { + if ($opt['enablefields'] <> "") { + echo "\t\tcase " . $idcounter . ":\n"; + $enablefields_split = explode(",", $opt['enablefields']); + foreach ($enablefields_split as $efs) { + $fieldname = preg_replace("/\s+/", "", $efs); + $fieldname = strtolower($fieldname); + if ($fieldname <> "") { + $onchange = "\t\t\tdocument.forms[0]." . $fieldname . ".disabled = 0; \n"; + echo $onchange; + } + } + echo "\t\t\tbreak;\n"; + } + $idcounter = $idcounter + 1; + } + } + } + echo "\t}\n"; + } + echo "}\n"; + echo "//]]>\n"; + echo "</script>\n\n"; +} +?> + +<script type="text/javascript"> +//<![CDATA[ +NiftyCheck(); +var bgcolor = document.getElementsByTagName("body")[0].style.backgroundColor; +Rounded("div#roundme","all",bgcolor,"#FFFFFF","smooth"); +enablechange(); +disablechange(); +showchange(); +//]]> +</script> + +<?php +if ($pkg['step'][$stepid]['stepafterformdisplay'] <> "") { + // handle after form display event. + eval($pkg['step'][$stepid]['stepafterformdisplay']); +} + +if ($pkg['step'][$stepid]['javascriptafterformdisplay'] <> "") { + // handle after form display event. + echo "\n<script type=\"text/javascript\">\n"; + echo "//<![CDATA[\n"; + echo $pkg['step'][$stepid]['javascriptafterformdisplay'] . "\n"; + echo "//]]>\n"; + echo "</script>\n\n"; +} + +/* + * HELPER FUNCTIONS + */ + +function fixup_string($string) { + global $config, $g, $myurl, $title; + $newstring = $string; + // fixup #1: $myurl -> http[s]://ip_address:port/ + switch ($config['system']['webgui']['protocol']) { + case "http": + $proto = "http"; + break; + case "https": + $proto = "https"; + break; + default: + $proto = "http"; + break; + } + $port = $config['system']['webgui']['port']; + if ($port != "") { + if (($port == "443" and $proto != "https") or ($port == "80" and $proto != "http")) { + $urlport = ":" . $port; + } elseif ($port != "80" and $port != "443") { + $urlport = ":" . $port; + } else { + $urlport = ""; + } + } + $http_host = $_SERVER['SERVER_NAME']; + $urlhost = $http_host; + // If finishing the setup wizard, check if accessing on a LAN or WAN address that changed + if ($title == "Reload in progress") { + if (is_ipaddr($urlhost)) { + $host_if = find_ip_interface($urlhost); + if ($host_if) { + $host_if = convert_real_interface_to_friendly_interface_name($host_if); + if ($host_if && is_ipaddr($config['interfaces'][$host_if]['ipaddr'])) { + $urlhost = $config['interfaces'][$host_if]['ipaddr']; + } + } + } else if ($urlhost == $config['system']['hostname']) { + $urlhost = $config['wizardtemp']['system']['hostname']; + } else if ($urlhost == $config['system']['hostname'] . '.' . $config['system']['domain']) { + $urlhost = $config['wizardtemp']['system']['hostname'] . '.' . $config['wizardtemp']['system']['domain']; + } + } + if ($urlhost != $http_host) { + file_put_contents("{$g['tmp_path']}/setupwizard_lastreferrer", $proto . "://" . $http_host . $urlport . $_SERVER['REQUEST_URI']); + } + $myurl = $proto . "://" . $urlhost . $urlport . "/"; + + if (strstr($newstring, "\$myurl")) { + $newstring = str_replace("\$myurl", $myurl, $newstring); + } + // fixup #2: $wanip + if (strstr($newstring, "\$wanip")) { + $curwanip = get_interface_ip(); + $newstring = str_replace("\$wanip", $curwanip, $newstring); + } + // fixup #3: $lanip + if (strstr($newstring, "\$lanip")) { + $lanip = get_interface_ip("lan"); + $newstring = str_replace("\$lanip", $lanip, $newstring); + } + // fixup #4: fix'r'up here. + return $newstring; +} + +function is_timezone($elt) { + return !preg_match("/\/$/", $elt); +} + +?> + +</body> +</html> diff --git a/src/usr/local/www/wizards/openvpn_wizard.inc b/src/usr/local/www/wizards/openvpn_wizard.inc new file mode 100644 index 0000000..ee530a2 --- /dev/null +++ b/src/usr/local/www/wizards/openvpn_wizard.inc @@ -0,0 +1,676 @@ +<?php +/* + Copyright (C) 2010 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_MODULE: openvpn +*/ +require_once("openvpn.inc"); + +function has_special_chars($text) { + return preg_match('/[^A-Za-z0-9 _-]/', $text); +} + +function step1_submitphpaction() { + global $stepid, $config; + if ($_POST['authtype'] == "local") { + $stepid = 4; + $config['ovpnserver']['step1']['type'] = "local"; + } else if ($_POST['authtype'] == "ldap") { + $stepid = 0; + } else if ($_POST['authtype'] == "radius") { + $stepid = 2; + $config['ovpnserver']['step1']['type'] = "radius"; + unset($config['ovpnserver']['step1']['uselist']); + } +} + +function step2_stepbeforeformdisplay() { + global $pkg, $stepid; + + $fields =& $pkg['step'][1]['fields']['field']; + + $found = false; + $authlist = auth_get_authserver_list(); + $fields[1]['options']['option'] = array(); + foreach ($authlist as $i => $auth) { + if ($auth['type'] != "ldap") + continue; + $found = true; + $opts = array(); + $opts['name'] = $auth['name']; + $opts['value'] = $auth['name']; + $fields[1]['options']['option'][] = $opts; + } + if ($found == false) { + $stepid = 2; + } +} + +function step2_submitphpaction() { + global $stepid; + + if (isset($_POST['next'])) { + $_POST['uselist'] = ""; + $stepid +=3; + } +} + +function step3_submitphpaction() { + global $stepid, $savemsg, $config; + + /* Default LDAP port is 389 for TCP and 636 for SSL */ + if (empty($_POST['port'])) { + if ($_POST['transport'] == "tcp") + $config['ovpnserver']['step2']['port'] = 389; + elseif ($_POST['transport'] == "ssl") + $config['ovpnserver']['step2']['port'] = 636; + } elseif (!is_port($_POST['port'])) { + $stepid--; + $savemsg = "Please enter a valid port number."; + } + + if (empty($_POST['name']) || empty($_POST['ip']) ||empty($_POST['transport']) || + empty($_POST['scope']) || empty($_POST['basedn']) || empty($_POST['authscope']) || empty($_POST['nameattr'])) { + $stepid--; + $savemsg = "Please enter all information for authentication server."; + } else if (count(($authcfg = auth_get_authserver($_POST['name']))) > 0) { + $stepid--; + $savemsg = "Please choose a different name because an authentication server with this name already exists."; + } elseif (!is_fqdn($_POST['ip']) && !is_ipaddr($_POST['ip'])) { + $stepid--; + $savemsg = "Please enter a valid IP address or hostname for the authentication server."; + } else { + $config['ovpnserver']['step2']['uselist'] = "on"; + $_POST['uselist'] = "on"; + $stepid += 2; + } +} + +function step4_stepbeforeformdisplay() { + global $pkg, $stepid; + + $fields =& $pkg['step'][3]['fields']['field']; + + $found = false; + $authlist = auth_get_authserver_list(); + $fields[1]['options']['option'] = array(); + foreach ($authlist as $i => $auth) { + if ($auth['type'] != "radius") + continue; + $found = true; + $opts = array(); + $opts['name'] = $auth['name']; + $opts['value'] = $auth['name']; + $fields[1]['options']['option'][] = $opts; + } + if ($found == false) + $stepid = 4; +} + +function step4_submitphpaction() { + global $stepid; + + if (isset($_POST['next'])) { + $_POST['uselist'] = ""; + $stepid++; + } +} + +function step5_submitphpaction() { + global $stepid, $savemsg, $config; + + /* Default RADIUS Auth port = 1812 */ + if (empty($_POST['port'])) { + $config['ovpnserver']['step2']['port'] = 1812; + } elseif (!is_port($_POST['port'])) { + $stepid--; + $savemsg = "Please enter a valid port number."; + } + + if (empty($_POST['name']) || empty($_POST['ip']) || empty($_POST['secret'])) { + $stepid--; + $savemsg = "Please enter all information for authentication server."; + } else if (count(($authcfg = auth_get_authserver($_POST['name']))) > 0) { + $stepid--; + $savemsg = "Please choose a different name because an authentication server with this name already exists."; + } elseif (!is_fqdn($_POST['ip']) && !is_ipaddr($_POST['ip'])) { + $stepid--; + $savemsg = "Please enter a valid IP address or hostname for the authentication server."; + } else { + $config['ovpnserver']['step2']['uselist'] = "on"; + $_POST['uselist'] = "on"; + } +} + +function step6_stepbeforeformdisplay() { + global $stepid, $config; + + if (count($config['ca']) < 1) { + $stepid++; + } +} + +function step6_submitphpaction() { + global $stepid, $config; + + if (isset($_POST['next'])) { + $_POST['uselist'] = ""; + unset($config['ovpnserver']['step6']['uselist']); + $stepid++; + } else { + $config['ovpnserver']['step6']['uselist'] = "on"; + $_POST['uselist'] = "on"; + } +} + +function step7_submitphpaction() { + global $input_errors, $stepid, $savemsg, $_POST, $config; + + $canames = array(); + $cacns = array(); + if (is_array($config['ca'])) { + foreach($config['ca'] as $ca) { + $canames[] = $ca['descr']; + $cainfo = cert_get_subject_hash($ca['crt']); + $cacns[] = $cainfo["CN"]; + } + } + + if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) { + $input_errors[] = "The field 'Descriptive Name' contains invalid characters."; + } + + if (empty($_POST['descr']) || empty($_POST['keylength']) || empty($_POST['lifetime']) || + empty($_POST['country']) || empty($_POST['state']) || empty($_POST['city']) || + empty($_POST['organization']) || empty($_POST['email'])) { + $stepid--; + $savemsg = "Please enter all information for the new Certificate Authority."; + } elseif (has_special_chars($_POST['country']) || has_special_chars($_POST['state']) || + has_special_chars($_POST['city']) || has_special_chars($_POST['organization'])) { + $stepid--; + $input_errors[] = "Please do not use special characters in Certificate field names."; + } elseif (in_array($_POST['descr'], $canames) || in_array($_POST['descr'], $cacns)) { + $stepid--; + $savemsg = "Please enter a different name for the Certificate Authority. A Certificate Authority with that name already exists."; + } elseif (strlen($_POST['country']) != 2) { + $stepid--; + $savemsg = "Please enter only a two-letter ISO country code"; + } else { + $config['ovpnserver']['step6']['uselist'] = "on"; + $_POST['uselist'] = "on"; + } +} + +function step8_stepbeforeformdisplay() { + global $stepid, $config; + + if (count($config['cert']) < 1 || + (count($config['cert']) == 1 && stristr($config['cert'][0]['descr'], "webconf"))) { + $stepid++; + } +} + +function step8_submitphpaction() { + global $stepid, $config, $_POST; + + if (isset($_POST['next'])) { + $_POST['uselist'] = ""; + unset($config['ovpnserver']['step9']['uselist']); + $stepid++; + } else { + $config['ovpnserver']['step6']['uselist'] = "on"; + $_POST['uselist'] = "on"; + } +} + +function step9_stepbeforeformdisplay() { + global $config, $pkg, $stepid; + + $pconfig = $config['ovpnserver']; + + if (isset($pconfig['step6']['uselist'])) { + $country = $pconfig['step6']['country']; + $state = $pconfig['step6']['state']; + $city = $pconfig['step6']['city']; + $org = $pconfig['step6']['organization']; + $email = $pconfig['step6']['email']; + } else { + $ca = lookup_ca($pconfig['step6']['authcertca']); + $cavl = cert_get_subject_array($ca['crt']); + $country = $cavl[0]['v']; + $state = $cavl[1]['v']; + $city = $cavl[2]['v']; + $org = $cavl[3]['v']; + $email = $cavl[4]['v']; + } + $fields =& $pkg['step'][$stepid]['fields']['field']; + + foreach ($fields as $idx => $field) { + switch ($field['name']) { + case 'country': + $fields[$idx]['value'] = $country; + break; + case 'state': + $fields[$idx]['value'] = $state; + break; + case 'city': + $fields[$idx]['value'] = $city; + break; + case 'organization': + $fields[$idx]['value'] = $org; + break; + case 'email': + $fields[$idx]['value'] = $email; + break; + } + } +} + +function step9_submitphpaction() { + global $input_errors, $stepid, $savemsg, $_POST, $config; + + $certnames = array(); + $certcns = array(); + if (is_array($config['cert'])) { + foreach($config['cert'] as $cert) { + $certnames[] = $cert['descr']; + $certinfo = cert_get_subject_hash($cert['crt']); + $certcns[] = $certinfo["CN"]; + } + } + + if (preg_match("/[\?\>\<\&\/\\\"\']/", $_POST['descr'])) { + $input_errors[] = "The field 'Descriptive Name' contains invalid characters."; + } + + if (empty($_POST['descr']) || empty($_POST['keylength']) || empty($_POST['lifetime']) || + empty($_POST['country']) || empty($_POST['state']) || empty($_POST['city']) || + empty($_POST['organization']) || empty($_POST['email'])) { + $stepid--; + $savemsg = "Please enter all information for the new certificate."; + } elseif (has_special_chars($_POST['country']) || has_special_chars($_POST['state']) || + has_special_chars($_POST['city']) || has_special_chars($_POST['organization'])) { + $stepid--; + $input_errors[] = "Please do not use special characters in Certificate field names."; + } elseif (in_array($_POST['descr'], $certnames) || in_array($_POST['descr'], $certcns)) { + $stepid--; + $savemsg = "Please enter a different name for the Certificate. A Certificate with that name/common name already exists."; + } elseif (strlen($_POST['country']) != 2) { + $stepid--; + $savemsg = "Please enter only a two-letter ISO country code"; + } else { + $config['ovpnserver']['step9']['uselist'] = "on"; + $_POST['uselist'] = "on"; + } +} + +function step10_stepbeforeformdisplay() { + global $pkg, $stepid, $netbios_nodetypes; + + foreach ($pkg['step'][$stepid]['fields']['field'] as $idx => $field) { + if ($field['name'] == "crypto") { + $pkg['step'][$stepid]['fields']['field'][$idx]['options']['option'] = array(); + $cipherlist = openvpn_get_cipherlist(); + foreach ($cipherlist as $name => $desc) { + $opt = array(); + $opt['name'] = $desc; + $opt['value'] = $name; + $pkg['step'][$stepid]['fields']['field'][$idx]['options']['option'][] = $opt; + } + } else if ($field['name'] == "digest") { + $pkg['step'][$stepid]['fields']['field'][$idx]['options']['option'] = array(); + $digestlist = openvpn_get_digestlist(); + foreach ($digestlist as $name => $desc) { + $opt = array(); + $opt['name'] = $desc; + $opt['value'] = $name; + $pkg['step'][$stepid]['fields']['field'][$idx]['options']['option'][] = $opt; + } + } else if ($field['name'] == "compression") { + global $openvpn_compression_modes; + $pkg['step'][$stepid]['fields']['field'][$idx]['options']['option'] = array(); + foreach ($openvpn_compression_modes as $name => $desc) { + $opt = array(); + $opt['name'] = $desc; + $opt['value'] = $name; + $pkg['step'][$stepid]['fields']['field'][$idx]['options']['option'][] = $opt; + } + } else if ($field['name'] == "engine") { + $pkg['step'][$stepid]['fields']['field'][$idx]['options']['option'] = array(); + $engines = openvpn_get_engines(); + foreach ($engines as $name => $desc) { + $opt = array(); + $opt['name'] = $desc; + $opt['value'] = $name; + $pkg['step'][$stepid]['fields']['field'][$idx]['options']['option'][] = $opt; + } + } else if ($field['name'] == "nbttype") { + $pkg['step'][$stepid]['fields']['field'][$idx]['options']['option'] = array(); + foreach ($netbios_nodetypes as $type => $name) { + $opt = array(); + $opt['name'] = $name; + $opt['value'] = $type; + $pkg['step'][$stepid]['fields']['field'][$idx]['options']['option'][] = $opt; + } + } else if ($field['name'] == "localport") { + $pkg['step'][$stepid]['fields']['field'][$idx]['value'] = openvpn_port_next('UDP'); + } + } +} + +function step10_submitphpaction() { + global $savemsg, $stepid; + + /* Default OpenVPN port to next available port if left empty. */ + if (empty($_POST['localport'])) + $pconfig["step10"]["localport"] = openvpn_port_next('UDP'); + + /* input validation */ + if ($result = openvpn_validate_port($_POST['localport'], 'Local port')) + $input_errors[] = $result; + + if ($result = openvpn_validate_cidr($_POST['tunnelnet'], 'Tunnel Network', false, "ipv4")) + $input_errors[] = $result; + + if ($result = openvpn_validate_cidr($_POST['localnet'], 'Local Network', true, "ipv4")) + $input_errors[] = $result; + + $portused = openvpn_port_used($_POST['protocol'], $_POST['interface'], $_POST['localport']); + if ($portused != 0) + $input_errors[] = "The specified 'Local port' is in use. Please enter a port not already in use."; + + if (!isset($_POST['generatetlskey']) && isset($_POST['tlsauthentication'])) + if (!strstr($_POST['tlssharedkey'], "-----BEGIN OpenVPN Static key V1-----") || + !strstr($_POST['tlssharedkey'], "-----END OpenVPN Static key V1-----")) + $input_errors[] = "The field 'TLS Authentication Key' does not appear to be valid"; + + if (!empty($_POST['dnsserver1']) && !is_ipaddr(trim($_POST['dnsserver1']))) + $input_errors[] = "The field 'DNS Server #1' must contain a valid IP address"; + if (!empty($_POST['dnsserver2']) && !is_ipaddr(trim($_POST['dnsserver2']))) + $input_errors[] = "The field 'DNS Server #2' must contain a valid IP address"; + if (!empty($_POST['dnsserver3']) && !is_ipaddr(trim($_POST['dnsserver3']))) + $input_errors[] = "The field 'DNS Server #3' must contain a valid IP address"; + if (!empty($_POST['dnsserver4']) && !is_ipaddr(trim($_POST['dnsserver4']))) + $input_errors[] = "The field 'DNS Server #4' must contain a valid IP address"; + + if (!empty($_POST['ntpserver1']) && !is_ipaddr(trim($_POST['ntpserver1']))) + $input_errors[] = "The field 'NTP Server #1' must contain a valid IP address"; + if (!empty($_POST['ntpserver2']) && !is_ipaddr(trim($_POST['ntpserver2']))) + $input_errors[] = "The field 'NTP Server #2' must contain a valid IP address"; + + if (!empty($_POST['winsserver1']) && !is_ipaddr(trim($_POST['winsserver1']))) + $input_errors[] = "The field 'WINS Server #1' must contain a valid IP address"; + if (!empty($_POST['winsserver2']) && !is_ipaddr(trim($_POST['winsserver2']))) + $input_errors[] = "The field 'WINS Server #2' must contain a valid IP address"; + + if ($_POST['concurrentcon'] && !is_numeric($_POST['concurrentcon'])) + $input_errors[] = "The field 'Concurrent connections' must be numeric."; + + if (empty($_POST['tunnelnet'])) + $input_errors[] = "You must specify a 'Tunnel network'."; + + if (count($input_errors) > 0) { + $savemsg = $input_errors[0]; + $stepid = $stepid - 1; + } +} + +function step12_submitphpaction() { + global $config; + + $pconfig = $config['ovpnserver']; + + if (!is_array($config['ovpnserver'])) { + $message = "No configuration found, please try again."; + header("Location:wizard.php?xml=openvpn_wizard.xml&stepid=1&message={$message}"); + exit; + } + + if ($pconfig['step1']['type'] == "local") { + $auth = array(); + $auth['name'] = "Local Database"; + $auth['type'] = "local"; + } else if (isset($pconfig['step2']['uselist'])) { + $auth = array(); + $auth['type'] = $pconfig['step1']['type']; + $auth['refid'] = uniqid(); + $auth['name'] = $pconfig['step2']['authtype']; + + if ($auth['type'] == "ldap") { + $auth['host'] = $pconfig['step2']['ip']; + $auth['ldap_port'] = $pconfig['step2']['port']; + if ($pconfig['step1']['transport'] == "tcp") + $auth['ldap_urltype'] = 'TCP - Standard'; + else + $auth['ldap_urltype'] = 'SSL - Encrypted'; + $auth['ldap_protver'] = 3; + $auth['ldap_scope'] = $pconfig['step2']['scope']; + $auth['ldap_basedn'] = $pconfig['step2']['basedn']; + $auth['ldap_authcn'] = $pconfig['step2']['authscope']; + $auth['ldap_binddn'] = $pconfig['step2']['userdn']; + $auth['ldap_bindpw'] = $pconfig['step2']['passdn']; + $auth['ldap_attr_user'] = $pconfig['step1']['nameattr']; + $auth['ldap_attr_member'] = $pconfig['step1']['memberattr']; + $auth['ldap_attr_group'] = $pconfig['step1']['groupattr']; + } else if ($auth['type'] == "radius") { + $auth['host'] = $pconfig['step2']['ip']; + $auth['radius_auth_port'] = $pconfig['step2']['port']; + $auth['radius_secret'] = $pconfig['step2']['password']; + $auth['radius_srvcs'] = "auth"; + } + if (!is_array($config['system']['authserver'])) + $config['system']['authserver'] = array(); + + $config['system']['authserver'][] = $auth; + } else if (!isset($pconfig['step2']['uselist']) && empty($pconfig['step2']['authserv'])) { + $message = "Please choose an authentication server ."; + header("Location:wizard.php?xml=openvpn_wizard.xml&stepid=1&message={$message}"); + exit; + } else if (!($auth = auth_get_authserver($pconfig['step2']['authserv']))) { + $message = "An invalid authentication server has been specified."; + header("Location:wizard.php?xml=openvpn_wizard.xml&stepid=1&message={$message}"); + exit; + } + + if (isset($pconfig['step6']['uselist']) && !empty($pconfig['step6']['certca'])) { + $ca = array(); + $ca['refid'] = uniqid(); + $ca['descr'] = $pconfig['step6']['certca']; + $dn = array( + 'countryName' => $pconfig['step6']['country'], + 'stateOrProvinceName' => $pconfig['step6']['state'], + 'localityName' => $pconfig['step6']['city'], + 'organizationName' => $pconfig['step6']['organization'], + 'emailAddress' => $pconfig['step6']['email'], + 'commonName' => $pconfig['step6']['certca']); + + ca_create($ca, $pconfig['step6']['keylength'], $pconfig['step6']['lifetime'], $dn, "sha256"); + if (!is_array($config['ca'])) + $config['ca'] = array(); + + $config['ca'][] = $ca; + } else if (!isset($pconfig['step6']['uselist']) && empty($pconfig['step6']['authcertca'])) { + $message = "Please choose a Certificate Authority."; + header("Location:wizard.php?xml=openvpn_wizard.xml&stepid=5&message={$message}"); + exit; + } else if (!($ca = lookup_ca($pconfig['step6']['authcertca']))) { + $message = "An invalid Certificate Authority has been specified."; + header("Location:wizard.php?xml=openvpn_wizard.xml&stepid=5&message={$message}"); + exit; + } + + if (isset($pconfig['step9']['uselist'])) { + $cert = array(); + $cert['refid'] = uniqid(); + $cert['descr'] = $pconfig['step9']['certname']; + $dn = array( + 'countryName' => $pconfig['step9']['country'], + 'stateOrProvinceName' => $pconfig['step9']['state'], + 'localityName' => $pconfig['step9']['city'], + 'organizationName' => $pconfig['step9']['organization'], + 'emailAddress' => $pconfig['step9']['email'], + 'commonName' => $pconfig['step9']['certname']); + + cert_create($cert, $ca['refid'], $pconfig['step9']['keylength'], $pconfig['step9']['lifetime'], $dn, 'server', "sha256"); + if (!is_array($config['cert'])) + $config['cert'] = array(); + + $config['cert'][] = $cert; + } else if (!isset($pconfig['step9']['uselist']) && empty($pconfig['step9']['authcertname'])) { + $message = "Please choose a Certificate."; + header("Location:wizard.php?xml=openvpn_wizard.xml&stepid=7&message={$message}"); + exit; + } else if (!($cert = lookup_cert($pconfig['step9']['authcertname']))) { + $message = "An invalid Certificate has been specified."; + header("Location:wizard.php?xml=openvpn_wizard.xml&stepid=7&message={$message}"); + exit; + } + $server = array(); + $server['vpnid'] = openvpn_vpnid_next(); + switch ($auth['type']) { + case "ldap": + $server['authmode'] = $auth['name']; + $server['mode'] = "server_user"; + break; + case "radius": + $server['authmode'] = $auth['name']; + $server['mode'] = "server_user"; + break; + default: + $server['authmode'] = "Local Database"; + $server['mode'] = "server_tls_user"; + break; + } + $server['caref'] = $ca['refid']; + $server['certref'] = $cert['refid']; + $server['protocol'] = $pconfig['step10']['protocol']; + $server['interface'] = $pconfig['step10']['interface']; + if (isset($pconfig['step10']['localport'])) + $server['local_port'] = $pconfig['step10']['localport']; + + if (strlen($pconfig['step10']['descr']) > 30) + $pconfig['step10']['descr'] = substr($pconfig['step10']['descr'], 0, 30); + $server['description'] = $pconfig['step10']['descr']; + $server['custom_options'] = $pconfig['step10']['advanced']; + if (isset($pconfig['step10']['tlsauth'])) { + if (isset($pconfig['step10']['gentlskey'])) + $tlskey = openvpn_create_key(); + else + $tlskey = $pconfig['step10']['tlskey']; + $server['tls'] = base64_encode($tlskey); + } + $server['dh_length'] = $pconfig['step10']['dhkey']; + $server['tunnel_network'] = $pconfig['step10']['tunnelnet']; + if (isset($pconfig['step10']['rdrgw'])) + $server['gwredir'] = $pconfig['step10']['rdrgw']; + if (isset($pconfig['step10']['localnet'])) + $server['local_network'] = $pconfig['step10']['localnet']; + if (isset($pconfig['step10']['concurrentcon'])) + $server['maxclients'] = $pconfig['step10']['concurrentcon']; + if (isset($pconfig['step10']['compression'])) + $server['compression'] = $pconfig['step10']['compression']; + if (isset($pconfig['step10']['tos'])) + $server['passtos'] = $pconfig['step10']['tos']; + if (isset($pconfig['step10']['interclient'])) + $server['client2client'] = $pconfig['step10']['interclient']; + if (isset($pconfig['step10']['duplicate_cn'])) + $server['duplicate_cn'] = $pconfig['step10']['duplicate_cn']; + if (isset($pconfig['step10']['dynip'])) + $server['dynamic_ip'] = $pconfig['step10']['dynip']; + if (isset($pconfig['step10']['addrpool'])) + $server['pool_enable'] = $pconfig['step10']['addrpool']; + if (isset($pconfig['step10']['defaultdomain'])) + $server['dns_domain'] = $pconfig['step10']['defaultdomain']; + if (isset($pconfig['step10']['dns1'])) + $server['dns_server1'] = $pconfig['step10']['dns1']; + if (isset($pconfig['step10']['dns2'])) + $server['dns_server2'] = $pconfig['step10']['dns2']; + if (isset($pconfig['step10']['dns3'])) + $server['dns_server3'] = $pconfig['step10']['dns3']; + if (isset($pconfig['step10']['dns4'])) + $server['dns_server4'] = $pconfig['step10']['dns4']; + if (isset($pconfig['step10']['ntp1'])) + $server['ntp_server1'] = $pconfig['step10']['ntp1']; + if (isset($pconfig['step10']['ntp2'])) + $server['ntp_server2'] = $pconfig['step10']['ntp2']; + if (isset($pconfig['step10']['wins1'])) + $server['wins_server1'] = $pconfig['step10']['wins1']; + if (isset($pconfig['step10']['wins2'])) + $server['wins_server2'] = $pconfig['step10']['wins2']; + if (isset($pconfig['step10']['nbtenable'])) { + $server['netbios_ntype'] = $pconfig['step10']['nbttype']; + if (isset($pconfig['step10']['nbtscope'])) + $server['netbios_scope'] = $pconfig['step10']['nbtscope']; + $server['netbios_enable'] = $pconfig['step10']['nbtenable']; + } + $server['crypto'] = $pconfig['step10']['crypto']; + $server['digest'] = $pconfig['step10']['digest']; + $server['engine'] = $pconfig['step10']['engine']; + + if (isset($pconfig['step11']['ovpnrule'])) { + $rule = array(); + $rule['descr'] = sprintf(gettext("OpenVPN %s wizard"),$server['description']); + /* Ensure the rule descr is not too long for pf to handle */ + if (strlen($rule['descr']) > 52) + $rule['descr'] = substr($rule['descr'], 0, 52); + $rule['direction'] = "in"; + $rule['source']['any'] = TRUE; + $rule['destination']['network'] = $server['interface'] . "ip"; + $rule['destination']['port'] = $server['local_port']; + $rule['interface'] = $server['interface']; + $rule['protocol'] = strtolower($server['protocol']); + $rule['type'] = "pass"; + $rule['enabled'] = "on"; + $rule['created'] = make_config_revision_entry(null, gettext("OpenVPN Wizard")); + $config['filter']['rule'][] = $rule; + } + if (isset($pconfig['step11']['ovpnallow'])) { + $rule = array(); + $rule['descr'] = sprintf(gettext("OpenVPN %s wizard"),$server['description']); + /* Ensure the rule descr is not too long for pf to handle */ + if (strlen($rule['descr']) > 52) + $rule['descr'] = substr($rule['descr'], 0, 52); + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['interface'] = "openvpn"; + //$rule['protocol'] = $server['protocol']; + $rule['type'] = "pass"; + $rule['enabled'] = "on"; + $rule['created'] = make_config_revision_entry(null, gettext("OpenVPN Wizard")); + $config['filter']['rule'][] = $rule; + } + + if (!is_array($config['openvpn']['openvpn-server'])) + $config['openvpn']['openvpn-server'] = array(); + + $config['openvpn']['openvpn-server'][] = $server; + + openvpn_resync('server', $server); + write_config(); + header("Location: vpn_openvpn_server.php"); + exit; +} + +?> diff --git a/src/usr/local/www/wizards/openvpn_wizard.xml b/src/usr/local/www/wizards/openvpn_wizard.xml new file mode 100644 index 0000000..6aea7fd --- /dev/null +++ b/src/usr/local/www/wizards/openvpn_wizard.xml @@ -0,0 +1,987 @@ +<?xml version="1.0" encoding="utf-8" ?> +<pfsensewizard> +<copyright> +/* + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2010 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +</copyright> +<totalsteps>12</totalsteps> +<step> + <id>1</id> + <title>OpenVPN Wizard: Authentication Type Selection</title> + <description>OpenVPN Remote Access Server Setup Wizard</description> + <disableheader>on</disableheader> + <fields> + <field> + <type>listtopic</type> + <name>Select an Authentication Backend Type</name> + </field> + <field> + <type>select</type> + <displayname>Type of Server</displayname> + <name>authtype</name> + <description><br/><b>NOTE:</b> If you are unsure, leave this set to "Local User Access."</description> + <bindstofield>ovpnserver->step1->type</bindstofield> + <options> + <option> + <name>Local User Access</name> + <value>local</value> + </option> + <option> + <name>LDAP</name> + <value>ldap</value> + </option> + <option> + <name>Radius</name> + <value>radius</value> + </option> + </options> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepsubmitphpaction>step1_submitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/openvpn_wizard.inc</includefile> +</step> +<step> + <id>2</id> + <title>OpenVPN Wizard: LDAP Server Selection</title> + <description>OpenVPN Remote Access Server Setup Wizard</description> + <disableheader>on</disableheader> + <fields> + <field> + <type>listtopic</type> + <name>LDAP Authentication Server List</name> + </field> + <field> + <name>authserv</name> + <displayname>LDAP servers</displayname> + <type>select</type> + <bindstofield>ovpnserver->step2->authserv</bindstofield> + <options> + <option> + <name>dummy</name> + <value>dummy</value> + </option> + </options> + </field> + <field> + <type>submit</type> + <name>Add new LDAP server</name> + </field> + <field> + <type>submit</type> + <name>Next</name> + </field> + </fields> + <stepbeforeformdisplay>step2_stepbeforeformdisplay();</stepbeforeformdisplay> + <stepsubmitphpaction>step2_submitphpaction();</stepsubmitphpaction> + <javascriptafterformdisplay>enablechange();</javascriptafterformdisplay> + <includefile>/usr/local/www/wizards/openvpn_wizard.inc</includefile> +</step> +<step> + <id>3</id> + <title>OpenVPN Wizard: Add LDAP Server</title> + <description>OpenVPN Remote Access Server Setup Wizard</description> + <disableheader>on</disableheader> + <fields> + <field> + <type>listtopic</type> + <name>LDAP Authentication Server Parameters</name> + </field> + <field> + <name>name</name> + <displayname>Name</displayname> + <type>input</type> + <bindstofield>ovpnserver->step2->authtype</bindstofield> + <size>30</size> + <description>Descriptive server name, for your own reference.</description> + </field> + <field> + <name>ip</name> + <displayname>Hostname or IP address</displayname> + <type>input</type> + <bindstofield>ovpnserver->step2->ip</bindstofield> + <description>Address of the LDAP server.</description> + </field> + <field> + <name>port</name> + <displayname>Port</displayname> + <type>input</type> + <size>8</size> + <bindstofield>ovpnserver->step2->port</bindstofield> + <description>LDAP Server port, leave blank for the default (389 for TCP, 636 for SSL).</description> + </field> + <field> + <name>transport</name> + <displayname>Transport</displayname> + <type>select</type> + <bindstofield>ovpnserver->step2->transport</bindstofield> + <options> + <option> + <name>TCP - Standard</name> + <value>tcp</value> + </option> + <option> + <name>SSL - Encrypted</name> + <value>ssl</value> + </option> + </options> + <description><br/>The protocol used by your LDAP server. It can either be standard TCP or SSL encrypted.</description> + </field> + <field> + <name>scope</name> + <displayname>Search Scope Level</displayname> + <type>select</type> + <options> + <option> + <name>One Level</name> + <value>one</value> + </option> + <option> + <name>Entire Subtree</name> + <value>subtree</value> + </option> + </options> + <bindstofield>ovpnserver->step2->scope</bindstofield> + </field> + <field> + <name>basedn</name> + <displayname>Search Scope Base DN</displayname> + <type>input</type> + <size>40</size> + <bindstofield>ovpnserver->step2->basedn</bindstofield> + </field> + <field> + <name>authscope</name> + <displayname>Authentication Containers</displayname> + <type>input</type> + <size>40</size> + <bindstofield>ovpnserver->step2->authscope</bindstofield> + <description>Semi-Colon separated. This will be prepended to the search base dn above or you can specify full container path.<br/>EXAMPLE: CN=Users;DC=example<br/>EXAMPLE: CN=Users,DC=example,DC=com;OU=OtherUsers,DC=example,DC=com </description> + </field> + <field> + <name>userdn</name> + <displayname>LDAP Bind User DN</displayname> + <type>input</type> + <size>20</size> + <description>If left blank, an anonymous bind will be done.</description> + <bindstofield>ovpnserver->step2->userdn</bindstofield> + </field> + <field> + <name>passdn</name> + <displayname>LDAP Bind Password</displayname> + <type>password</type> + <size>20</size> + <bindstofield>ovpnserver->step2->passdn</bindstofield> + <description>If a user DN was supplied above, this password will also be used when performing a bind operation.</description> + </field> + <field> + <name>nameattr</name> + <displayname>User Naming Attribute</displayname> + <type>input</type> + <bindstofield>ovpnserver->step2->nameattr</bindstofield> + <description>Typically "cn" (OpenLDAP, Novell eDirectory), "samAccountName" (Microsoft AD)</description> + </field> + <field> + <name>groupattr</name> + <displayname>Group Naming Attribute</displayname> + <type>input</type> + <bindstofield>ovpnserver->step2->groupattr</bindstofield> + <description>Typically "cn" (OpenLDAP, Microsoft AD, and Novell eDirectory)</description> + </field> + <field> + <name>memberattr</name> + <displayname>Member Naming Attribute</displayname> + <type>input</type> + <bindstofield>ovpnserver->step2->memberattr</bindstofield> + <description>Typically "member" (OpenLDAP), "memberOf" (Microsoft AD), "uniqueMember" (Novell eDirectory)</description> + </field> + <field> + <type>submit</type> + <name>Add new Server</name> + </field> + </fields> + <stepsubmitphpaction>step3_submitphpaction();</stepsubmitphpaction> + <javascriptafterformdisplay>enablechange();</javascriptafterformdisplay> + <includefile>/usr/local/www/wizards/openvpn_wizard.inc</includefile> +</step> +<step> + <id>4</id> + <title>OpenVPN Wizard: RADIUS Server Selection</title> + <description>OpenVPN Remote Access Server Setup Wizard</description> + <disableheader>on</disableheader> + <fields> + <field> + <type>listtopic</type> + <name>RADIUS Authentication Server List</name> + </field> + <field> + <name>authserv</name> + <displayname>RADIUS servers</displayname> + <type>select</type> + <bindstofield>ovpnserver->step2->authserv</bindstofield> + <options> + <option> + <name>dummy</name> + <value>dummy</value> + </option> + </options> + </field> + <field> + <type>submit</type> + <name>Add new RADIUS server</name> + </field> + <field> + <type>submit</type> + <name>Next</name> + </field> + </fields> + <stepbeforeformdisplay>step4_stepbeforeformdisplay();</stepbeforeformdisplay> + <stepsubmitphpaction>step4_submitphpaction();</stepsubmitphpaction> + <javascriptafterformdisplay>enablechange();</javascriptafterformdisplay> + <includefile>/usr/local/www/wizards/openvpn_wizard.inc</includefile> +</step> +<step> + <id>5</id> + <title>OpenVPN Wizard: Add RADIUS Server</title> + <description>OpenVPN Remote Access Server Setup Wizard</description> + <disableheader>on</disableheader> + <fields> + <field> + <type>listtopic</type> + <name>RADIUS Authentication Server Parameters</name> + </field> + <field> + <name>name</name> + <displayname>Name</displayname> + <type>input</type> + <bindstofield>ovpnserver->step2->authtype</bindstofield> + <size>20</size> + <description>Descriptive name for the RADIUS server, for your reference.</description> + </field> + <field> + <name>ip</name> + <displayname>Hostname or IP address</displayname> + <type>input</type> + <bindstofield>ovpnserver->step2->ip</bindstofield> + <description>Address of the RADIUS server.</description> + </field> + <field> + <name>port</name> + <displayname>Authentication Port</displayname> + <type>input</type> + <size>8</size> + <bindstofield>ovpnserver->step2->port</bindstofield> + <description>Port used by the RADIUS server for accepting Authentication requests, typically 1812.</description> + </field> + <field> + <name>secret</name> + <displayname>Shared Secret</displayname> + <type>password</type> + <size>20</size> + <bindstofield>ovpnserver->step2->password</bindstofield> + <description></description> + </field> + <field> + <name>Add new Server</name> + <type>submit</type> + </field> + </fields> + <stepsubmitphpaction>step5_submitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/openvpn_wizard.inc</includefile> +</step> +<step> + <id>6</id> + <title>OpenVPN Wizard: Certificate Authority Selection</title> + <description>OpenVPN Remote Access Server Setup Wizard</description> + <disableheader>on</disableheader> + <fields> + <field> + <name>Choose a Certificate Authority (CA)</name> + <type>listtopic</type> + </field> + <field> + <type>certca_selection</type> + <name>certca</name> + <displayname>Certificate Authority</displayname> + <bindstofield>ovpnserver->step6->authcertca</bindstofield> + </field> + <field> + <type>submit</type> + <name>Add new CA</name> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepbeforeformdisplay>step6_stepbeforeformdisplay();</stepbeforeformdisplay> + <stepsubmitphpaction>step6_submitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/openvpn_wizard.inc</includefile> +</step> +<step> + <id>7</id> + <title>OpenVPN Wizard: Add Certificate Authority</title> + <description>OpenVPN Remote Access Server Setup Wizard</description> + <disableheader>on</disableheader> + <fields> + <field> + <name>Create a New Certificate Authority (CA) Certificate</name> + <type>listtopic</type> + </field> + <field> + <name>descr</name> + <displayname>Descriptive name</displayname> + <description>A name for your reference, to identify this certificate. This is the same as common-name field for other Certificates.</description> + <type>input</type> + <size>20</size> + <bindstofield>ovpnserver->step6->certca</bindstofield> + </field> + <field> + <name>keylength</name> + <displayname>Key length</displayname> + <description><br/>Size of the key which will be generated. The larger the key, the more security it offers, but larger keys are generally slower to use.</description> + <type>select</type> + <value>2048</value> + <bindstofield>ovpnserver->step6->keylength</bindstofield> + <options> + <option> + <name>512 bit</name> + <value>512</value> + </option> + <option> + <name>1024 bit</name> + <value>1024</value> + </option> + <option> + <name>2048 bit</name> + <value>2048</value> + </option> + <option> + <name>4096 bit</name> + <value>4096</value> + </option> + </options> + </field> + <field> + <name>lifetime</name> + <displayname>Lifetime</displayname> + <type>input</type> + <size>10</size> + <value>3650</value> + <description>Lifetime in days. This is commonly set to 3650 (Approximately 10 years.)</description> + <bindstofield>ovpnserver->step6->lifetime</bindstofield> + </field> + <field> + <name>country</name> + <displayname>Country Code</displayname> + <description>Two-letter ISO country code (e.g. US, AU, CA) </description> + <type>input</type> + <size>5</size> + <bindstofield>ovpnserver->step6->country</bindstofield> + </field> + <field> + <name>state</name> + <displayname>State or Province</displayname> + <description>Full State or Province name, not abbreviated (e.g. Kentucky, Indiana, Ontario).</description> + <type>input</type> + <size>30</size> + <bindstofield>ovpnserver->step6->state</bindstofield> + </field> + <field> + <name>city</name> + <displayname>City</displayname> + <description>City or other Locality name (e.g. Louisville, Indianapolis, Toronto).</description> + <type>input</type> + <size>30</size> + <bindstofield>ovpnserver->step6->city</bindstofield> + </field> + <field> + <name>organization</name> + <displayname>Organization</displayname> + <description>Organization name, often the Company or Group name.</description> + <type>input</type> + <size>30</size> + <bindstofield>ovpnserver->step6->organization</bindstofield> + </field> + <field> + <name>email</name> + <displayname>E-mail</displayname> + <description>E-mail address for the Certificate contact. Often the e-mail of the person generating the certificate (i.e. You.)</description> + <type>input</type> + <size>30</size> + <bindstofield>ovpnserver->step6->email</bindstofield> + </field> + <field> + <name>Add new CA</name> + <type>submit</type> + </field> + </fields> + <stepsubmitphpaction>step7_submitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/openvpn_wizard.inc</includefile> + <javascriptafterformdisplay>enablechange();</javascriptafterformdisplay> +</step> +<step> + <id>8</id> + <title>OpenVPN Wizard: Server Certificate Selection</title> + <description>OpenVPN Remote Access Server Setup Wizard</description> + <disableheader>on</disableheader> + <fields> + <field> + <name>Choose a Server Certificate</name> + <type>listtopic</type> + </field> + <field> + <type>cert_selection</type> + <name>certname</name> + <displayname>Certificate</displayname> + <bindstofield>ovpnserver->step9->authcertname</bindstofield> + </field> + <field> + <type>submit</type> + <name>Add new Certificate</name> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepbeforeformdisplay>step8_stepbeforeformdisplay();</stepbeforeformdisplay> + <stepsubmitphpaction>step8_submitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/openvpn_wizard.inc</includefile> +</step> +<step> + <id>9</id> + <title>OpenVPN Wizard: Add a Server Certificate</title> + <description>OpenVPN Remote Access Server Setup Wizard</description> + <disableheader>on</disableheader> + <fields> + <field> + <name>Create a New Server Certificate</name> + <type>listtopic</type> + </field> + <field> + <name>descr</name> + <displayname>Descriptive name</displayname> + <description>A name for your reference, to identify this certificate. This is also known as the certificate's "Common Name."</description> + <type>input</type> + <size>20</size> + <bindstofield>ovpnserver->step9->certname</bindstofield> + </field> + <field> + <name>keylength</name> + <displayname>Key length</displayname> + <description><br/>Size of the key which will be generated. The larger the key, the more security it offers, but larger keys are generally slower to use.</description> + <type>select</type> + <value>2048</value> + <bindstofield>ovpnserver->step9->keylength</bindstofield> + <options> + <option> + <name>512 bits</name> + <value>512</value> + </option> + <option> + <name>1024 bits</name> + <value>1024</value> + </option> + <option> + <name>2048 bits</name> + <value>2048</value> + </option> + <option> + <name>4096 bits</name> + <value>4096</value> + </option> + </options> + </field> + <field> + <name>lifetime</name> + <displayname>Lifetime</displayname> + <description>Lifetime in days. This is commonly set to 3650 (Approximately 10 years.)</description> + <type>input</type> + <size>10</size> + <value>3650</value> + <bindstofield>ovpnserver->step9->lifetime</bindstofield> + </field> + <field> + <name>country</name> + <displayname>Country Code</displayname> + <description>Two-letter ISO country code (e.g. US, AU, CA) </description> + <type>input</type> + <size>5</size> + <bindstofield>ovpnserver->step9->country</bindstofield> + </field> + <field> + <name>state</name> + <displayname>State or Province</displayname> + <description>Full State of Province name, not abbreviated (e.g. Kentucky, Indiana, Ontario).</description> + <type>input</type> + <size>30</size> + <bindstofield>ovpnserver->step9->state</bindstofield> + </field> + <field> + <name>city</name> + <displayname>City</displayname> + <description>City or other Locality name (e.g. Louisville, Indianapolis, Toronto).</description> + <type>input</type> + <size>30</size> + <bindstofield>ovpnserver->step9->city</bindstofield> + </field> + <field> + <name>organization</name> + <displayname>Organization</displayname> + <description>Organization name, often the Company or Group name.</description> + <type>input</type> + <size>30</size> + <bindstofield>ovpnserver->step9->organization</bindstofield> + </field> + <field> + <name>email</name> + <displayname>E-mail</displayname> + <description>E-mail address for the Certificate contact. Often the e-mail of the person generating the certificate (i.e. You.)</description> + <type>input</type> + <size>30</size> + <bindstofield>ovpnserver->step9->email</bindstofield> + </field> + <field> + <name>Create new Certificate</name> + <type>submit</type> + </field> + </fields> + <stepbeforeformdisplay>step9_stepbeforeformdisplay();</stepbeforeformdisplay> + <stepsubmitphpaction>step9_submitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/openvpn_wizard.inc</includefile> +</step> +<step> + <id>10</id> + <title>OpenVPN Wizard: Server Setup</title> + <description>OpenVPN Remote Access Server Setup Wizard</description> + <disableheader>on</disableheader> + <fields> + <field> + <type>listtopic</type> + <name>General OpenVPN Server Information</name> + </field> + <field> + <name>interface</name> + <type>interfaces_selection</type> + <description>The interface where OpenVPN will listen for incoming connections (typically WAN.)</description> + <displayname>Interface</displayname> + <bindstofield>ovpnserver->step10->interface</bindstofield> + </field> + <field> + <name>Protocol</name> + <type>select</type> + <bindstofield>ovpnserver->step10->protocol</bindstofield> + <options> + <option> + <name>UDP</name> + <value>UDP</value> + </option> + <option> + <name>TCP</name> + <value>TCP</value> + </option> + </options> + <description><br/>Protocol to use for OpenVPN connections. If you are unsure, leave this set to UDP.</description> + </field> + <field> + <name>localport</name> + <displayname>Local Port</displayname> + <description>Local port upon which OpenVPN will listen for connections. The default port is 1194. This can be left at its default unless you need to use a different port.</description> + <type>input</type> + <size>10</size> + <bindstofield>ovpnserver->step10->localport</bindstofield> + </field> + <field> + <name>description</name> + <displayname>Description</displayname> + <description>A name for this OpenVPN instance, for your reference. It can be set however you like, but is often used to distinguish the purpose of the service (e.g. "Remote Technical Staff"). It is also used by OpenVPN Client Export to identify this VPN on clients.</description> + <type>input</type> + <size>30</size> + <bindstofield>ovpnserver->step10->descr</bindstofield> + </field> + <field> + <type>listtopic</type> + <name>Cryptographic Settings</name> + </field> + <field> + <name>TLS Authentication</name> + <type>checkbox</type> + <value>on</value> + <description>Enable authentication of TLS packets.</description> + <bindstofield>ovpnserver->step10->tlsauth</bindstofield> + </field> + <field> + <displayname>Generate TLS Key</displayname> + <name>generatetlskey</name> + <disablefields>tlssharedkey</disablefields> + <value>on</value> + <type>checkbox</type> + <description>Automatically generate a shared TLS authentication key.</description> + <bindstofield>ovpnserver->step10->gentlskey</bindstofield> + </field> + <field> + <displayname>TLS Shared Key</displayname> + <name>tlssharedkey</name> + <description>Paste in a shared TLS key if one has already been generated.</description> + <type>textarea</type> + <cols>30</cols> + <rows>5</rows> + <bindstofield>ovpnserver->step10->tlskey</bindstofield> + </field> + <field> + <displayname>DH Parameters Length</displayname> + <name>dhparameters</name> + <type>select</type> + <value>2048</value> + <bindstofield>ovpnserver->step10->dhkey</bindstofield> + <options> + <option> + <name>1024 bit</name> + <value>1024</value> + </option> + <option> + <name>2048 bit</name> + <value>2048</value> + </option> + <option> + <name>4096 bit</name> + <value>4096</value> + </option> + </options> + <description><br/>Length of Diffie-Hellman (DH) key exchange parameters, used for establishing a secure communications channel. As with other such settings, the larger values are more secure, but may be slower in operation.</description> + </field> + <field> + <name>crypto</name> + <type>select</type> + <displayname>Encryption Algorithm</displayname> + <bindstofield>ovpnserver->step10->crypto</bindstofield> + <value>AES-256-CBC</value> + <options> + <option> + <name>dummy</name> + <value>dummy</value> + </option> + </options> + <description><br/>The algorithm used to encrypt traffic between endpoints. This setting must match on the client and server side, but is otherwise set however you like. Certain algorithms will perform better on different hardware, depending on the availability of supported VPN accelerator chips.</description> + </field> + <field> + <name>digest</name> + <type>select</type> + <displayname>Auth Digest Algorithm</displayname> + <bindstofield>ovpnserver->step10->digest</bindstofield> + <options> + <option> + <name>dummy</name> + <value>dummy</value> + </option> + </options> + <value>SHA1</value> + <description><br/>The method used to authenticate traffic between endpoints. This setting must match on the client and server side, but is otherwise set however you like.</description> + </field> + <field> + <name>engine</name> + <type>select</type> + <displayname>Hardware Crypto</displayname> + <bindstofield>ovpnserver->step10->engine</bindstofield> + <options> + <option> + <name>dummy</name> + <value>dummy</value> + </option> + </options> + <description><br/>The hardware cryptographic accelerator to use for this VPN connection, if any.</description> + </field> + <field> + <type>listtopic</type> + <name>Tunnel Settings</name> + </field> + <field> + <displayname>Tunnel Network</displayname> + <name>tunnelnet</name> + <type>input</type> + <size>20</size> + <bindstofield>ovpnserver->step10->tunnelnet</bindstofield> + <description>This is the virtual network used for private communications between this server and client hosts expressed using CIDR notation (eg. 10.0.8.0/24). The first network address will be assigned to the server virtual interface. The remaining network addresses can optionally be assigned to connecting clients. (see Address Pool)</description> + </field> + <field> + <displayname>Redirect Gateway</displayname> + <name>redirectgw</name> + <type>checkbox</type> + <description>Force all client generated traffic through the tunnel.</description> + <bindstofield>ovpnserver->step10->rdrgw</bindstofield> + </field> + <field> + <displayname>Local Network</displayname> + <name>localnet</name> + <type>input</type> + <size>20</size> + <bindstofield>ovpnserver->step10->localnet</bindstofield> + <description>This is the network that will be accessible from the remote endpoint, expressed as a CIDR range. You may leave this blank if you don't want to add a route to the local network through this tunnel on the remote machine. This is generally set to your LAN network.</description> + </field> + <field> + <displayname>Concurrent Connections</displayname> + <name>concurrentcon</name> + <description>Specify the maximum number of clients allowed to concurrently connect to this server.</description> + <type>input</type> + <size>10</size> + <bindstofield>ovpnserver->step10->concurrentcon</bindstofield> + </field> + <field> + <displayname>Compression</displayname> + <name>compression</name> + <description><br/>Compress tunnel packets using the LZO algorithm. Adaptive compression will dynamically disable compression for a period of time if OpenVPN detects that the data in the packets is not being compressed efficiently.</description> + <bindstofield>ovpnserver->step10->compression</bindstofield> + <type>select</type> + <options> + <option> + <name>dummy</name> + <value>dummy</value> + </option> + </options> + </field> + <field> + <displayname>Type-of-Service</displayname> + <name>tos</name> + <type>checkbox</type> + <description>Set the TOS IP header value of tunnel packets to match the encapsulated packet's TOS value.</description> + <bindstofield>ovpnserver->step10->tos</bindstofield> + </field> + <field> + <displayname>Inter-Client Communication</displayname> + <name>interclient</name> + <type>checkbox</type> + <description>Allow communication between clients connected to this server.</description> + <bindstofield>ovpnserver->step10->interclient</bindstofield> + </field> + <field> + <displayname>Duplicate Connections</displayname> + <name>duplicate_cn</name> + <type>checkbox</type> + <description>Allow multiple concurrent connections from clients using the same Common Name.<br/>NOTE: This is not generally recommended, but may be needed for some scenarios.</description> + <bindstofield>ovpnserver->step10->duplicate_cn</bindstofield> + </field> + <field> + <type>listtopic</type> + <name>Client Settings</name> + </field> + <field> + <displayname>Dynamic IP</displayname> + <name>dynip</name> + <type>checkbox</type> + <value>on</value> + <description>Allow connected clients to retain their connections if their IP address changes.</description> + <bindstofield>ovpnserver->step10->dynip</bindstofield> + </field> + <field> + <displayname>Address Pool</displayname> + <name>addrpool</name> + <type>checkbox</type> + <value>on</value> + <description>Provide a virtual adapter IP address to clients (see Tunnel Network).</description> + <bindstofield>ovpnserver->step10->addrpool</bindstofield> + </field> + <field> + <displayname>DNS Default Domain</displayname> + <name>defaultdomain</name> + <type>input</type> + <description>Provide a default domain name to clients.</description> + <bindstofield>ovpnserver->step10->defaultdomain</bindstofield> + </field> + <field> + <displayname>DNS Server 1</displayname> + <name>dnsserver1</name> + <type>input</type> + <bindstofield>ovpnserver->step10->dns1</bindstofield> + <description>DNS server IP to provide to connecting clients.</description> + </field> + <field> + <displayname>DNS Server 2</displayname> + <name>dnserver2</name> + <type>input</type> + <bindstofield>ovpnserver->step10->dns2</bindstofield> + <description>DNS server IP to provide to connecting clients.</description> + </field> + <field> + <displayname>DNS Server 3</displayname> + <name>dnserver3</name> + <type>input</type> + <bindstofield>ovpnserver->step10->dns3</bindstofield> + <description>DNS server IP to provide to connecting clients.</description> + </field> + <field> + <displayname>DNS Server 4</displayname> + <name>dnserver4</name> + <type>input</type> + <bindstofield>ovpnserver->step10->dns4</bindstofield> + <description>DNS server IP to provide to connecting clients.</description> + </field> + <field> + <displayname>NTP Server</displayname> + <name>ntpserver1</name> + <type>input</type> + <bindstofield>ovpnserver->step10->ntp1</bindstofield> + <description>Network Time Protocol server to provide to connecting clients.</description> + </field> + <field> + <displayname>NTP Server 2</displayname> + <name>ntpserver2</name> + <type>input</type> + <bindstofield>ovpnserver->step10->ntp2</bindstofield> + <description>Network Time Protocol server to provide to connecting clients.</description> + </field> + <field> + <name>nbtenable</name> + <type>checkbox</type> + <displayname>NetBIOS Options</displayname> + <bindstofield>ovpnserver->step10->nbtenable</bindstofield> + <description>Enable NetBIOS over TCP/IP. <br/>If this option is not set, all NetBIOS-over-TCP/IP options (including WINS) will be disabled. </description> + </field> + <field> + <displayname>NetBIOS Node Type</displayname> + <name>nbttype</name> + <type>select</type> + <bindstofield>ovpnserver->step10->nbttype</bindstofield> + <options> + <option> + <name>dummy</name> + <value>dummy</value> + </option> + </options> + <description><br/>Possible options: b-node (broadcasts), p-node (point-to-point name queries to a WINS server), m-node (broadcast then query name server), and h-node (query name server, then broadcast).</description> + </field> + <field> + <displayname>NetBIOS Scope ID</displayname> + <name>nbtscope</name> + <type>input</type> + <bindstofield>ovpnserver->step10->nbtscope</bindstofield> + <description>A NetBIOS Scope ID provides an extended naming service for NetBIOS over TCP/IP. The NetBIOS scope ID isolates NetBIOS traffic on a single network to only those nodes with the same NetBIOS scope ID.</description> + </field> + <field> + <displayname>WINS Server 1</displayname> + <name>winsserver1</name> + <type>input</type> + <bindstofield>ovpnserver->step10->wins1</bindstofield> + <description>A Windows Internet Name Service (WINS) server IP to provide to connecting clients. Not desirable in most all modern networks.</description> + </field> + <field> + <displayname>WINS Server 2</displayname> + <name>winsserver2</name> + <type>input</type> + <bindstofield>ovpnserver->step10->wins2</bindstofield> + <description>A Windows Internet Name Service (WINS) server IP to provide to connecting clients. Not desirable in most all modern networks.</description> + </field> + <field> + <name>Advanced</name> + <type>textarea</type> + <cols>30</cols> + <rows>5</rows> + <description>Enter any additional options you would like to add to the OpenVPN server configuration here, separated by a semicolon. EXAMPLE: push "route 10.0.0.0 255.255.255.0"</description> + <bindstofield>ovpnserver->step10->advanced</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepbeforeformdisplay>step10_stepbeforeformdisplay();</stepbeforeformdisplay> + <stepsubmitphpaction>step10_submitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/openvpn_wizard.inc</includefile> +</step> +<step> + <id>11</id> + <title>OpenVPN Wizard: Firewall Rule Configuration</title> + <description>OpenVPN Remote Access Server Setup Wizard</description> + <disableheader>on</disableheader> + <fields> + <field> + <type>listtopic</type> + <name>Firewall Rule Configuration</name> + </field> + <field> + <type>text</type> + <description>Firewall rules control what network traffic is permitted. You must add rules to allow traffic to the OpenVPN server's IP and port, as well as allowing traffic from connected clients through the tunnel. These rules can be automatically added here, or configured manually after completing the wizard.</description> + </field> + <field> + <type>listtopic</type> + <name>Traffic from clients to server</name> + </field> + <field> + <name>ovpnrule</name> + <displayname>Firewall Rule</displayname> + <description>Add a rule to permit connections to this OpenVPN server process from clients anywhere on the Internet.</description> + <type>checkbox</type> + <bindstofield>ovpnserver->step11->ovpnrule</bindstofield> + </field> + <field> + <type>listtopic</type> + <name>Traffic from clients through VPN</name> + </field> + <field> + <name>ovpnallow</name> + <displayname>OpenVPN rule</displayname> + <description>Add a rule to allow all traffic from connected clients to pass inside the VPN tunnel.</description> + <type>checkbox</type> + <bindstofield>ovpnserver->step11->ovpnallow</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> +</step> +<step> + <id>12</id> + <title>OpenVPN Wizard: Finished!</title> + <description>OpenVPN Remote Access Server Setup Wizard</description> + <disableheader>on</disableheader> + <fields> + <field> + <type>listtopic</type> + <name>Configuration Complete!</name> + </field> + <field> + <type>text</type> + <description>Your configuration is now complete.</description> + </field> + <field> + <type>text</type> + <description>To be able to export client configurations, browse to System->Packages and install the OpenVPN Client Export package.</description> + </field> + <field> + <type>submit</type> + <name>Finish</name> + </field> + </fields> + <stepsubmitphpaction>step12_submitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/openvpn_wizard.inc</includefile> +</step> +</pfsensewizard>
\ No newline at end of file diff --git a/src/usr/local/www/wizards/setup_wizard.xml b/src/usr/local/www/wizards/setup_wizard.xml new file mode 100644 index 0000000..9da0509 --- /dev/null +++ b/src/usr/local/www/wizards/setup_wizard.xml @@ -0,0 +1,703 @@ +<?xml version="1.0" encoding="utf-8" ?> +<pfsensewizard> +<copyright> +/* $Id$ */ +/* + setup_wizard.xml + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2004, 2005 Scott Ullrich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +</copyright> +<totalsteps>10</totalsteps> +<step> + <id>1</id> + <title>pfSense Setup Wizard</title> + <disableheader>true</disableheader> + <description>This wizard will guide you through the initial configuration of pfSense.<br/><br/> The wizard may be stopped at any time by clicking the logo image at the top of the screen.</description> + <fields> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepbeforeformdisplay> + conf_mount_rw(); + unlink_if_exists('/conf/trigger_initial_wizard'); + conf_mount_ro(); + </stepbeforeformdisplay> +</step> +<step> + <id>2</id> + <title>Bling your pfSense with pfSense Gold</title> + <disableheader>true</disableheader> + <description>Feel the power of a pfSense Gold subscription. Receive special benefits while supporting ongoing development of the Open Source pfSense project.<br/> <br/> Benefits include access to our AutoConfigBackup secure cloud based backup service for up to 10 hosts, pre-publication access to the updated pfSense: The Definitive Guide book in PDF, fully updated for the pfSense 2.1 release, and a monthly online MeetUp! Video conference to discuss and demonstrate advanced features and architectures using pfSense. <br/> <br/> Go to <a href="https://www.pfsense.org/gold" target="_blank"> pfSense Gold Subscriptions</a> to sign up now <br/> <br/> </description> + <fields> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> +</step> +<step> + <id>3</id> + <title>General Information</title> + <description>On this screen you will set the general pfSense parameters.</description> + <fields> + <field> + <name>Hostname</name> + <type>input</type> + <bindstofield>wizardtemp->system->hostname</bindstofield> + <description>EXAMPLE: myserver</description> + <validate>^[a-zA-Z0-9-]+$</validate> + <message>Invalid Hostname</message> + </field> + <field> + <name>Domain</name> + <type>input</type> + <bindstofield>wizardtemp->system->domain</bindstofield> + <description>EXAMPLE: mydomain.com</description> + <validate>^[a-zA-Z0-9.-]+$</validate> + <message>Domain name field is invalid</message> + </field> + <field> + <name>DNS Resolver Behavior</name> + <type>text</type> + <description>The default behavior of the DNS Resolver will ignore manually configured DNS servers for client queries and query root DNS servers directly. To use the manually configured DNS servers below for client queries, visit Services > DNS Resolver and enable DNS Query Forwarding after completing the wizard.</description> + </field> + <field> + <name>Primary DNS Server</name> + <type>input</type> + <bindstofield>system->dnsserver</bindstofield> + <!-- we must unset the fields because this is an array. --> + <unsetfield>yes</unsetfield> + <arraynum>0</arraynum> + <validate>^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$</validate> + <message>Primary DNS Server field is invalid</message> + </field> + <field> + <name>Secondary DNS Server</name> + <type>input</type> + <bindstofield>system->dnsserver</bindstofield> + <arraynum>1</arraynum> + <validate>^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$</validate> + <message>Secondary DNS Server field is invalid</message> + </field> + <field> + <name>Override DNS</name> + <description>Allow DNS servers to be overridden by DHCP/PPP on WAN</description> + <type>checkbox</type> + <bindstofield>system->dnsallowoverride</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepbeforeformdisplay> + <![CDATA[ + $config['wizardtemp'] = array(); + $config['wizardtemp']['system'] = array(); + $config['wizardtemp']['system']['hostname'] = $config['system']['hostname']; + $config['wizardtemp']['system']['domain'] = $config['system']['domain']; + ]]> + </stepbeforeformdisplay> + <stepsubmitphpaction> + <![CDATA[ + if(empty($_POST['hostname']) || !is_unqualified_hostname($_POST['hostname'])) { + print_info_box_np("Hostname is invalid. Please press back in your browser window and correct."); + die; + } + if(empty($_POST['domain']) || !is_domain($_POST['domain'])) { + print_info_box_np("Domain is invalid. Please press back in your browser window and correct."); + die; + } + if(!empty($_POST['primarydnsserver']) && !is_ipaddr($_POST['primarydnsserver'])) { + print_info_box_np("Primary DNS server is invalid. Please press back in your browser window and correct."); + die; + } + if(!empty($_POST['secondarydnsserver']) && !is_ipaddr($_POST['secondarydnsserver'])) { + print_info_box_np("Second DNS server is invalid. Please press back in your browser window and correct."); + die; + } + ]]> + </stepsubmitphpaction> +</step> +<step> + <id>4</id> + <title>Time Server Information</title> + <description>Please enter the time, date and time zone.</description> + <fields> + <field> + <name>Time server hostname</name> + <description>Enter the hostname (FQDN) of the time server.</description> + <type>input</type> + <bindstofield>system->timeservers</bindstofield> + </field> + <field> + <name>Timezone</name> + <type>timezone_select</type> + <bindstofield>system->timezone</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepsubmitphpaction> + <![CDATA[ + foreach (explode(' ', $_POST['timeserverhostname']) as $ts) { + if (!is_domain($ts)) { + print_info_box_np(gettext("NTP Time Server names may only contain the characters a-z, 0-9, '-' and '.'. Entries may be separated by spaces. Please press back in your browser window and correct.")); + die; + } + } + ]]> + </stepsubmitphpaction> +</step> +<step> + <id>5</id> + <disableallfieldsbydefault>true</disableallfieldsbydefault> + <title>Configure WAN Interface</title> + <description>On this screen we will configure the Wide Area Network information.</description> + <javascriptafterformdisplay> + var selectedItem = 0; + if(document.forms[0].ipaddress.value == 'dhcp') { + selectedItem = 1; + document.forms[0].ipaddress.value = ''; + } else if(document.forms[0].ipaddress.value == 'pppoe') { + selectedItem = 2; + document.forms[0].ipaddress.value = ''; + } else if(document.forms[0].ipaddress.value == 'pptp') { + selectedItem = 3; + document.forms[0].ipaddress.value = ''; + } else if(document.forms[0].ipaddress.value == 'ppp' || document.forms[0].ipaddress.value == 'l2tp') { + document.forms[0].ipaddress.value = ''; + } else { + selectedItem = 0; + } + document.forms[0].selectedtype.selectedIndex = selectedItem; + enableitems(selectedItem); + </javascriptafterformdisplay> + <fields> + <field> + <name>SelectedType</name> + <type>select</type> + <donotdisable>true</donotdisable> + <options> + <option> + <name>Static</name> + <value>Static</value> + <enablefields>ipaddress,subnetmask,upstreamgateway</enablefields> + </option> + <option> + <name>DHCP</name> + <value>dhcp</value> + <enablefields>dhcphostname</enablefields> + </option> + <option> + <name>PPPoE</name> + <value>pppoe</value> + <enablefields>pppoeusername,pppoepassword,pppoeservicename,pppoedialondemand,pppoeidletimeout</enablefields> + </option> + <option> + <name>PPTP</name> + <value>pptp</value> + <enablefields>pptpusername,pptppassword,pptplocalipaddress,pptplocalsubnet,pptpremoteipaddress,pptpdialondemand,pptpidletimeout + </enablefields> + </option> + </options> + </field> + <field> + <name>General configuration</name> + <type>listtopic</type> + </field> + <field> + <donotdisable>true</donotdisable> + <name>MAC Address</name> + <bindstofield>interfaces->wan->spoofmac</bindstofield> + <type>input</type> + <description> This field can be used to modify ("spoof") the MAC address of the WAN interface (may be required with some cable connections). Enter a MAC address in the following format: xx:xx:xx:xx:xx:xx or leave blank.</description> + <validate>^([0-9a-f]{2}([:-]||$)){6}$</validate> + <message>MAC Address field is invalid</message> + </field> + <field> + <donotdisable>true</donotdisable> + <name>MTU</name> + <type>input</type> + <bindstofield>interfaces->wan->mtu</bindstofield> + <description> Set the MTU of the WAN interface. If you leave this field blank, an MTU of 1492 bytes for PPPoE and 1500 bytes for all other connection types will be assumed.</description> + </field> + <field> + <donotdisable>true</donotdisable> + <name>MSS</name> + <type>input</type> + <bindstofield>interfaces->wan->mss</bindstofield> + <description> If you enter a value in this field, then MSS clamping for TCP connections to the value entered above minus 40 (TCP/IP header size) will be in effect. If you leave this field blank, an MSS of 1492 bytes for PPPoE and 1500 bytes for all other connection types will be assumed. This should match the above MTU value in most all cases.</description> + </field> + <field> + <name>Static IP Configuration</name> + <type>listtopic</type> + </field> + <field> + <name>IP Address</name> + <bindstofield>interfaces->wan->ipaddr</bindstofield> + <type>input</type> + <typehint> / </typehint> + <combinefieldsbegin>true</combinefieldsbegin> + <validate>^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$</validate> + <message>IP Address field is invalid</message> + </field> + <field> + <combinefieldsend>true</combinefieldsend> + <dontdisplayname>true</dontdisplayname> + <dontcombinecells>true</dontcombinecells> + <name>Subnet Mask</name> + <bindstofield>interfaces->wan->subnet</bindstofield> + <type>subnet_select</type> + </field> + <field> + <name>Upstream Gateway</name> + <bindstofield>wizardtemp->wangateway</bindstofield> + <type>input</type> + <validate>^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$</validate> + <message>Gateway IP Address field is invalid</message> + </field> + <field> + <name>DHCP client configuration</name> + <type>listtopic</type> + </field> + <field> + <name>DHCP Hostname</name> + <type>input</type> + <bindstofield>interfaces->wan->dhcphostname</bindstofield> + <description> The value in this field is sent as the DHCP client identifier and hostname when requesting a DHCP lease. Some ISPs may require this (for client identification).</description> + </field> + <field> + <name>PPPoE configuration</name> + <type>listtopic</type> + </field> + <field> + <name>PPPoE Username</name> + <type>input</type> + <bindstofield>wizardtemp->wan->username</bindstofield> + </field> + <field> + <name>PPPoE Password</name> + <type>input</type> + <bindstofield>wizardtemp->wan->password</bindstofield> + </field> + <field> + <name>PPPoE Service name</name> + <type>input</type> + <description>Hint: this field can usually be left empty</description> + <bindstofield>wizardtemp->wan->provider</bindstofield> + </field> + <field> + <name>PPPoE Dial on demand</name> + <typehint>Enable Dial-On-Demand mode</typehint> + <type>checkbox</type> + <description>This option causes the interface to operate in dial-on-demand mode, allowing you to have a virtual full time connection. The interface is configured, but the actual connection of the link is delayed until qualifying outgoing traffic is detected.</description> + <bindstofield>wizardtemp->wan->ondemand</bindstofield> + </field> + <field> + <name>PPPoE Idle timeout</name> + <type>input</type> + <description>If no qualifying outgoing packets are transmitted for the specified number of seconds, the connection is brought down. An idle timeout of zero disables this feature.</description> + <bindstofield>wizardtemp->wan->idletimeout</bindstofield> + </field> + <field> + <name>PPTP configuration</name> + <type>listtopic</type> + </field> + <field> + <name>PPTP Username</name> + <type>input</type> + <bindstofield>wizardtemp->wan->pptpusername</bindstofield> + </field> + <field> + <name>PPTP Password</name> + <type>input</type> + <bindstofield>wizardtemp->wan->pptppassword</bindstofield> + </field> + <field> + <combinefieldsbegin>true</combinefieldsbegin> + <name>PPTP Local IP Address</name> + <type>input</type> + <typehint> / </typehint> + <bindstofield>wizardtemp->wan->localip</bindstofield> + <validate>^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$</validate> + <message>PPTP Local IP Address field is invalid</message> + </field> + <field> + <combinefieldsend>true</combinefieldsend> + <dontdisplayname>true</dontdisplayname> + <dontcombinecells>true</dontcombinecells> + <name>pptplocalsubnet</name> + <bindstofield>wizardtemp->wan->subnet</bindstofield> + <type>subnet_select</type> + </field> + <field> + <name>PPTP Remote IP Address</name> + <bindstofield>wizardtemp->wan->gateway</bindstofield> + <type>input</type> + <validate>^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$</validate> + <message>PPTP Remote IP Address field is invalid</message> + </field> + <field> + <name>PPTP Dial on demand</name> + <typehint>Enable Dial-On-Demand mode</typehint> + <type>checkbox</type> + <bindstofield>wizardtemp->wan->pptpondemand</bindstofield> + <description>This option causes the interface to operate in dial-on-demand mode, allowing you to have a virtual full time connection. The interface is configured, but the actual connection of the link is delayed until qualifying outgoing traffic is detected.</description> + </field> + <field> + <name>PPTP Idle timeout</name> + <type>input</type> + <bindstofield>wizardtemp->wan->pptpidletimeout</bindstofield> + <description>If no qualifying outgoing packets are transmitted for the specified number of seconds, the connection is brought down. An idle timeout of zero disables this feature.</description> + </field> + <field> + <name>RFC1918 Networks</name> + <type>listtopic</type> + </field> + <field> + <donotdisable>true</donotdisable> + <name>Block RFC1918 Private Networks</name> + <description> When set, this option blocks traffic from IP addresses that are reserved for private networks as per RFC 1918 (10/8, 172.16/12, 192.168/16) as well as loopback addresses (127/8). You should generally leave this option turned on, unless your WAN network lies in such a private address space, too.</description> + <type>checkbox</type> + <bindstofield>interfaces->wan->blockpriv</bindstofield> + <typehint>Block private networks from entering via WAN</typehint> + </field> + <field> + <name>Block bogon networks</name> + <type>listtopic</type> + </field> + <field> + <donotdisable>true</donotdisable> + <name>Block bogon networks</name> + <description>When set, this option blocks traffic from IP addresses that are reserved (but not RFC 1918) or not yet assigned by IANA. Bogons are prefixes that should never appear in the Internet routing table, and obviously should not appear as the source address in any packets you receive.</description> + <type>checkbox</type> + <bindstofield>interfaces->wan->blockbogons</bindstofield> + <typehint>Block non-Internet routed networks from entering via WAN</typehint> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepbeforeformdisplay> + <![CDATA[ + if (empty($config['interfaces']['wan']['gateway'])) { + $wangw_name = "WANGW"; + } else { + $wangw_name = $config['interfaces']['wan']['gateway']; + } + if (is_array($config['gateways']['gateway_item'])) + foreach ($config['gateways']['gateway_item'] as $gw) + if ($gw['name'] == $wangw_name || (!empty($config['wizardtemp']['wangateway']) && $gw['gateway'] == $config['wizardtemp']['wangateway'])) + $config['wizardtemp']['wangateway'] = $gw['gateway']; + ]]> + </stepbeforeformdisplay> + <stepsubmitphpaction> + <![CDATA[ + if(!empty($_POST['mtu']) && ($_POST['mtu'] < 576)) { + print_info_box_np("MTU Must be at least 576 (Per RFC 791). Please press back in your browser window and correct."); + die; + } + if(!empty($_POST['macaddress']) && !is_macaddr($_POST['macaddress'])) { + print_info_box_np("Invalid MAC Address. Please press back in your browser window and correct."); + die; + } + if(!empty($_POST['ipaddress']) && ($_POST['selectedtype'] == "Static")) { + if (!is_ipaddr($_POST['ipaddress'])) { + print_info_box_np("Invalid WAN IP Address. Please press back in your browser window and correct."); + die; + } + if ($_POST['subnetmask'] < 31 && + ($_POST['ipaddress'] == gen_subnet($_POST['ipaddress'], $_POST['subnetmask']) || + $_POST['ipaddress'] == gen_subnet_max($_POST['ipaddress'], $_POST['subnetmask']))) { + print_info_box_np("Invalid WAN IP Address. Please press back in your browser window and correct."); + die; + } + } + if(!empty($_POST['dhcphostname']) && !is_hostname($_POST['dhcphostname'])) { + print_info_box_np("Invalid DHCP Hostname. Please press back in your browser window and correct."); + die; + } + if(!empty($_POST['pptplocalipaddress']) && !is_ipaddr($_POST['pptplocalipaddress'])) { + print_info_box_np("Invalid PPTP Local IP Address. Please press back in your browser window and correct."); + die; + } + if(!empty($_POST['pptpremoteipaddress']) && !is_ipaddr($_POST['pptpremoteipaddress'])) { + print_info_box_np("Invalid PPTP Remote IP Address. Please press back in your browser window and correct."); + die; + } + $type = $_POST['selectedtype']; + + if (!is_array($config['ppps']['ppp'])) + $config['ppps']['ppp'] = array(); + if (count($config['ppps']['ppp'])) { + foreach ($config['ppps']['ppp'] as $pppid => $ppp) { + if ($ppp['ptpid'] == "0") { + if ((substr($config['interfaces']['wan']['if'],0,5) == "pppoe") || (substr($config['interfaces']['wan']['if'],0,4) == "pptp")) { + $oldif = explode(",", $ppp['ports']); + $config['interfaces']['wan']['if'] = $oldif[0]; + } + if ($type == "pppoe" || $type == "pptp") + unset($config['ppps']['ppp'][$pppid]); + } + } + } + + if ($type == "pppoe" || $type == "pptp") { + if ($type == "pptp") { + $config['wizardtemp']['wan']['username'] = $config['wizardtemp']['wan']['pptpusername']; + $config['wizardtemp']['wan']['password'] = $config['wizardtemp']['wan']['pptppassword']; + $config['wizardtemp']['wan']['ondemand'] = $config['wizardtemp']['wan']['pptpondemand']; + $config['wizardtemp']['wan']['idletimeout'] = $config['wizardtemp']['wan']['pptpidletimeout']; + unset($config['wizardtemp']['wan']['pptpusername']); + unset($config['wizardtemp']['wan']['pptppassword']); + unset($config['wizardtemp']['wan']['pptpondemand']); + unset($config['wizardtemp']['wan']['pptpidletimeout']); + } + $config['wizardtemp']['wan']['password'] = base64_encode($config['wizardtemp']['wan']['password']); + $tmp = array(); + $tmp['ptpid'] = "0"; + $tmp['type'] = $type; + $tmp['if'] = $type . "0"; + $tmp['ports'] = $config['interfaces']['wan']['if']; + $config['ppps']['ppp'][] = array_merge($tmp, $config['wizardtemp']['wan']); + unset($tmp); + $config['interfaces']['wan']['if'] = $type."0"; + } + unset($config['wizardtemp']['wan']); + ]]> + </stepsubmitphpaction> + <stepsubmitbeforesave> + <![CDATA[ + if($_POST['selectedtype'] == "Static") { + + } else { + $_POST['ipaddress'] = $_POST['selectedtype']; + $config['interfaces']['wan']['ipaddr'] = $_POST['selectedtype']; + write_config(); + if(!$config['interfaces']['lan']) + header("Location: /wizard.php?xml=setup_wizard.xml&stepid=6&next=Next"); + } + ]]> + </stepsubmitbeforesave> +</step> +<step> + <id>6</id> + <title>Configure LAN Interface</title> + <description>On this screen we will configure the Local Area Network information.</description> + <fields> + <field> + <name>LAN IP Address</name> + <type>input</type> + <bindstofield>interfaces->lan->ipaddr</bindstofield> + <description>Type dhcp if this interface uses DHCP to obtain its IP address.</description> + <validate>^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$</validate> + <message>LAN IP Address field is invalid</message> + </field> + <field> + <name>Subnet Mask</name> + <type>subnet_select</type> + <bindstofield>interfaces->lan->subnet</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepsubmitphpaction> + <![CDATA[ + if(empty($_POST['lanipaddress']) || !is_ipaddr($_POST['lanipaddress'])) { + print_info_box_np("Invalid LAN IP Address. Please press back in your browser window and correct."); + die; + } + + $lowestip = gen_subnet($_POST['lanipaddress'], $_POST['subnetmask']); + $highestip = gen_subnet_max($_POST['lanipaddress'], $_POST['subnetmask']); + + if ($_POST['subnetmask'] < 31) { + if ($_POST['lanipaddress'] == $lowestip) { + print_info_box_np("LAN IP Address equals subnet network address. This is not allowed. Please press back in your browser window and correct."); + die; + } + if ($_POST['lanipaddress'] == $highestip) { + print_info_box_np("LAN IP Address equals subnet broadcast address. This is not allowed. Please press back in your browser window and correct."); + die; + } + } else { + print_info_box_np("Invalid subnet mask, choose a mask less than 31. Please press back in your browser window and correct."); + die; + } + + $ipaddresses_before = ip_range_size_v4($lowestip, $_POST['lanipaddress']); + $ipaddresses_after = ip_range_size_v4($_POST['lanipaddress'], $highestip); + + if ($ipaddresses_after >= $ipaddresses_before) { + // The LAN IP is in the 1st half of the subnet, so put DHCP in the 2nd half. + if ($ipaddresses_after > 30) { + // There is reasonable space in the subnet, use a smaller chunk of the space for DHCP + // This case will work out like the old defaults if the user has specified the ".1" address. + // The range will be something like ".10" to ".245" + $config['dhcpd']['lan']['range']['from'] = ip_after($_POST['lanipaddress'], 9); + $config['dhcpd']['lan']['range']['to'] = ip_before($highestip, 10); + } else { + // There is not much space in the subnet, so allocate everything above the LAN IP to DHCP. + $config['dhcpd']['lan']['range']['from'] = ip_after($_POST['lanipaddress']); + $config['dhcpd']['lan']['range']['to'] = ip_before($highestip); + } + } else { + // The LAN IP is in the 2nd half of the subnet, so put DHCP in the 1st half. + if ($ipaddresses_before > 30) { + // There is reasonable space in the subnet, use a smaller chunk of the space for DHCP + $config['dhcpd']['lan']['range']['from'] = ip_after($lowestip, 10); + $config['dhcpd']['lan']['range']['to'] = ip_before($_POST['lanipaddress'], 9); + } else { + // There is not much space in the subnet, so allocate everything below the LAN IP to DHCP. + $config['dhcpd']['lan']['range']['from'] = ip_after($lowestip); + $config['dhcpd']['lan']['range']['to'] = ip_before($_POST['lanipaddress']); + } + } + ]]> + </stepsubmitphpaction> +</step> +<step> + <id>7</id> + <title>Set Admin WebGUI Password</title> + <description>On this screen we will set the admin password, which is used to access the WebGUI and also SSH services if you wish to enable them.</description> + <fields> + <field> + <name>Admin Password</name> + <type>password</type> + </field> + <field> + <name>Admin Password AGAIN</name> + <type>password</type> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepsubmitphpaction> + if($_POST['adminpassword'] != "") { + if($_POST['adminpassword'] == $_POST['adminpasswordagain']) { + $admin_user =& getUserEntryByUID(0); + local_user_set_password($admin_user, $_POST['adminpassword']); + local_user_set($admin_user); + write_config(); + } else { + print_info_box_np("Passwords do not match! Please press back in your browser window and correct."); + die; + } + } + </stepsubmitphpaction> +</step> +<step> + <id>8</id> + <title>Reload configuration</title> + <disableheader>true</disableheader> + <description>Click 'Reload' to reload pfSense with new changes.</description> + <fields> + <field> + <name>Reload</name> + <type>submit</type> + </field> + </fields> +</step> +<step> + <id>9</id> + <title>Reload in progress</title> + <description> + A reload is now in progress. Please wait. <p> + <meta http-equiv="refresh" content="5; url=wizard.php?xml=setup_wizard.xml&stepid=9" > + <p> + The wizard will redirect to the next step once the reload is completed. + </description> + <stepafterformdisplay> + <![CDATA[ + $config['system']['hostname'] = $config['wizardtemp']['system']['hostname']; + $config['system']['domain'] = $config['wizardtemp']['system']['domain']; + if (!empty($config['wizardtemp']['wangateway'])) { + if (!is_array($config['gateways']['gateway_item'])) + $config['gateways']['gateway_item'] = array(); + $found = false; + $defaultgw_found = false; + foreach ($config['gateways']['gateway_item'] as & $gw) { + if ($gw['interface'] != "wan") + continue; + if (isset($gw['defaultgw'])) + $defaultgw_found = true; + if ($gw['name'] == 'WANGW' || (!empty($config['wizardtemp']['wangateway']) && $gw['gateway'] == $config['wizardtemp']['wangateway'])) { + $found = true; + $gw['gateway'] = $config['wizardtemp']['wangateway']; + $config['interfaces']['wan']['gateway'] = $gw['name']; + } + } + if (!$found) { + $newgw = array(); + $newgw['interface'] = "wan"; + $newgw['gateway'] = $config['wizardtemp']['wangateway']; + $newgw['name'] = "WANGW"; + $newgw['weight'] = 1; + $newgw['descr'] = "WAN Gateway"; + $newgw['defaultgw'] = !$defaultgw_found; + $config['gateways']['gateway_item'][] = $newgw; + $config['interfaces']['wan']['gateway'] = "WANGW"; + } + } + unset($config['wizardtemp']); + write_config(); + reload_all(); + mwexec_bg("/etc/rc.update_bogons.sh now"); + ]]> + </stepafterformdisplay> +</step> +<step> + <id>10</id> + <title>Wizard completed.</title> + <stepbeforeformdisplay> + <![CDATA[ + if($g['product_name'] <> 'pfSense') { + header("Location: " . fixup_string("\$myurl")); + exit; + } + ]]> + </stepbeforeformdisplay> + <description> + <![CDATA[ + Congratulations! pfSense is now configured.<p/> + Please consider contributing back to the project!<p/> + Click <a target='_new' href='https://www.pfsense.org/get-involved/index.html'>here</a> to purchase services offered by the pfSense team and find other ways to contribute.<p/> + Click <a href='$myurl'>here</a> to continue on to pfSense webConfigurator. + ]]> + </description> +</step> +</pfsensewizard> diff --git a/src/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc b/src/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc new file mode 100644 index 0000000..d984fed --- /dev/null +++ b/src/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc @@ -0,0 +1,1664 @@ +<?php +/* + traffic_shaper_wizard_dedicated.inc + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2006 Bill Marquette - bill.marquette@gmail.com. + Copyright (C) 2006 Scott Ullrich - sullrich@pfsense.com. + Copyright (C) 2008-2010 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +function step1_stepbeforeformdisplay() { + global $stepid, $savemsg, $pkg; + + $fields =& $pkg['step'][0]['fields']['field']; + + $wans = 0; + $lans = 0; + + $iflisttmp = get_configured_interface_with_descr(); + foreach ($iflisttmp as $if => $ifdesc) { + if (!is_altq_capable(get_real_interface($if))) + continue; + if (interface_has_gateway($if) || interface_has_gatewayv6($if)) + $wans++; + else + $lans++; + } + + foreach ($fields as &$field) + if ($field['name'] == 'numberofconnections') + $field['value'] = ($wans < $lans ? $wans : $lans); +} + +function step1_submitphpaction() { + global $stepid, $savemsg; + + if (!isset($_POST['numberofconnections'])) { + $savemsg=gettext("You need to specify the number of connections."); + $stepid--; + return; + } + if (intval($_POST['numberofconnections']) < 1) { + $savemsg=gettext("The number of connections should be greater than 1."); + $stepid--; + return; + } +} + +function step2_stepbeforeformdisplay() { + global $config, $pkg; + global $stepid, $savemsg; + + $wans = 0; + $lans = 0; + $iflist = array(); + $iflisttmp = get_configured_interface_with_descr(); + foreach ($iflisttmp as $if => $ifdesc) { + if (!is_altq_capable(get_real_interface($if))) + continue; + if (interface_has_gateway($if) || interface_has_gatewayv6($if)) + $wans++; + else + $lans++; + $iflist[$if] = $ifdesc; + } + $numberofconnections = intval($config['ezshaper']['step1']['numberofconnections']); + if ($numberofconnections > ($wans < $lans ? $wans : $lans)) { + $savemsg=gettext("You have less interfaces than number of connections!"); + $stepid--; + return; + } + + $cfgname = "traffic_shaper_wizard_dedicated.xml"; + + $fields =& $pkg['step'][1]['fields']['field']; + + /* + unset($config['ezshaper']['step2']); + $config['ezshaper']['step2'] = array(); + write_config(); + */ + $fields = array(); + + for ($i = 0; $i < $numberofconnections; $i++) { + $field = array(); + $linknum = $i+1; + $ifsel = ($i * 2); + $field['name'] = "Connection #{$linknum} parameters"; + $field['type'] = "listtopic"; + $fields[] = $field; + + $field = array(); + $field['displayname'] = "Local interface"; + $field['name'] = "local{$i}interface"; + $field['type'] = "select"; + $field['options']['option'] = array(); + $ifcnt = 0; + foreach ($iflist as $ifname => $ifdescr) { + // Skip wan interfaces here + if (interface_has_gateway($ifname) || interface_has_gatewayv6($ifname)) + continue; + if ($ifcnt == ($ifsel + 1)) + $field['value'] = $ifname; + $opts = array(); + $opts['displayname'] = $ifdescr; + $opts['name'] = $ifname; + $opts['value'] = $ifname; + $field['options']['option'][] = $opts; + $ifcnt++; + } + $field['combinefieldsbegin'] = "true"; + $field['bindstofield'] = "ezshaper->step2->local{$i}interface"; + $fields[] = $field; + + $field = array(); + $field['combinefieldsend'] = "true"; + $field['dontdisplayname'] = "true"; + $field['dontcombinecells'] = "true"; + $field['name'] = "local{$i}downloadscheduler"; + $field['type'] = "select"; + $field['typehint'] = "Queueing discipline to apply on the download of this connection."; + $field['options']['option'] = array(); + $opts = array(); + $opts['name'] = "HFSC"; + $opts['value'] = "HFSC"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "CBQ"; + $opts['value'] = "CBQ"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "PRIQ"; + $opts['value'] = "PRIQ"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step2->local{$i}downloadscheduler"; + $fields[] = $field; + + $field = array(); + $field['displayname'] = "WAN Interface"; + $field['name'] = "conn{$i}interface"; + $field['type'] = "select"; + $field['options']['option'] = array(); + $ifcnt = 0; + foreach ($iflist as $ifname => $ifdescr) { + // Skip lan interfaces here + if (!interface_has_gateway($ifname) && !interface_has_gatewayv6($ifname)) + continue; + if ($ifcnt == $ifsel) + $field['value'] = $ifname; + $opts = array(); + $opts['displayname'] = $ifdescr; + $opts['name'] = $ifname; + $opts['value'] = $ifname; + $field['options']['option'][] = $opts; + $ifcnt++; + } + $field['bindstofield'] = "ezshaper->step2->conn{$i}interface"; + $field['combinefieldsbegin'] = "true"; + $fields[] = $field; + + $field = array(); + $field['name'] = "conn{$i}uploadscheduler"; + $field['combinefieldsend'] = "true"; + $field['dontdisplayname'] = "true"; + $field['dontcombinecells'] = "true"; + $field['type'] = "select"; + $field['typehint'] = "Queueing discipline to apply on the upload of this connection."; + $field['options']['option'] = array(); + $opts = array(); + $opts['name'] = "HFSC"; + $opts['value'] = "HFSC"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "CBQ"; + $opts['value'] = "CBQ"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "PRIQ"; + $opts['value'] = "PRIQ"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step2->conn{$i}uploadscheduler"; + $fields[] = $field; + + $field = array(); + $field['displayname'] = "Upload"; + $field['name'] = "conn{$i}upload"; + $field['type'] = "input"; + $field['bindstofield'] = "ezshaper->step2->conn{$i}upload"; + $field['combinefieldsbegin'] = "true"; + $fields[] = $field; + + $field = array(); + $field['combinefieldsend'] = "true"; + $field['dontdisplayname'] = "true"; + $field['dontcombinecells'] = "true"; + $field['name'] = "conn{$i}uploadspeed"; + $field['typehint'] = "Upload bandwidth on this connection."; + $field['type'] = "select"; + $field['options']['option'] = array(); + $opts = array(); + $opts['value'] = "Kb"; + $opts['name'] = "Kbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Mb"; + $opts['name'] = "Mbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Gb"; + $opts['name'] = "Gbit/s"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step2->conn{$i}uploadspeed"; + $fields[] = $field; + + $field = array(); + $field['displayname'] = "Download"; + $field['name'] = "conn{$i}download"; + $field['type'] = "input"; + $field['bindstofield'] = "ezshaper->step2->conn{$i}download"; + $field['combinefieldsbegin'] = "true"; + $fields[] = $field; + + $field = array(); + $field['combinefieldsend'] = "true"; + $field['dontdisplayname'] = "true"; + $field['dontcombinecells'] = "true"; + $field['name'] = "conn{$i}downloadspeed"; + $field['typehint'] = "Download bandwidth on this connection."; + $field['type'] = "select"; + $field['options']['option'] = array(); + $opts = array(); + $opts['value'] = "Kb"; + $opts['name'] = "Kbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Mb"; + $opts['name'] = "Mbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Gb"; + $opts['name'] = "Gbit/s"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step2->conn{$i}downloadspeed"; + $fields[] = $field; + } + $field = array(); + $field['name'] = "Next"; + $field['type'] = "submit"; + $fields[] = $field; +} + +function step2_stepsubmitphpaction() { + global $config; + global $stepid, $savemsg; + $sumdownloads = 0; + + /* Input Validation */ + $steps = intval($config['ezshaper']['step1']['numberofconnections']); + for ($i = 0; $i < $steps; $i++) { + for ($j = $i + 1; $j <= $steps; $j++) { + if ($_POST["conn{$i}interface"] == $_POST["conn{$j}interface"] || $_POST["conn{$i}interface"] == $_POST["local{$j}interface"]) { + $savemsg=gettext("You cannot select the same interface for connections {$i} and {$j}."); + $stepid--; + return; + } + if (trim($_POST["conn{$i}uploadscheduler"]) != "PRIQ") { + if (!is_numeric($_POST["conn{$i}upload"])) { + $savemsg = gettext("Upload bandwidth of connection {$i} is not valid."); + $stepid--; + return; + } + if (!is_numeric($_POST["conn{$i}download"])) { + $savemsg = gettext("Download bandwidth of connection {$i} is not valid."); + $stepid--; + return; + } + $upbw = $_POST["conn{$i}upload"]; + $downbw = $_POST["conn{$i}download"]; + if ($upbw < 1 || $downbw < 1) { + $savemsg = gettext("You cannot specify bandwidth smaller than 1!"); + $stepid--; + return; + } + if (intval($upbw) < 128 && $_POST["conn{$i}uploadspeed"] == "Kb" && trim($_POST["conn{$i}uploadscheduler"]) == "CBQ") { + $savemsg=gettext("Uploads smaller than 128Kbit/s is not supported for connection {$i} on CBQ scheduler."); + $stepid--; + return; + } + } + if ($_POST["local{$i}interface"] == $_POST["conn{$j}interface"] || $_POST["local{$i}interface"] == $_POST["local{$j}interface"]) { + $savemsg=gettext("You cannot select the same interface for local and outside."); + $stepid--; + return; + } + } + } + + /* This is necessary since the wizard expects predefined fields. */ + unset($config['ezshaper']['step2']); + $config['ezshaper']['step2'] = array(); + + for ($i = 0; $i < $steps; $i++) { + $config['ezshaper']['step2']["local{$i}downloadscheduler"] = $_POST["local{$i}downloadscheduler"]; + $config['ezshaper']['step2']["local{$i}interface"] = $_POST["local{$i}interface"]; + $config['ezshaper']['step2']["conn{$i}uploadscheduler"] = $_POST["conn{$i}uploadscheduler"]; + $config['ezshaper']['step2']["conn{$i}upload"] = $_POST["conn{$i}upload"]; + $config['ezshaper']['step2']["conn{$i}uploadspeed"] = $_POST["conn{$i}uploadspeed"]; + $config['ezshaper']['step2']["conn{$i}download"] = $_POST["conn{$i}download"]; + $config['ezshaper']['step2']["conn{$i}downloadspeed"] = $_POST["conn{$i}downloadspeed"]; + $config['ezshaper']['step2']["conn${i}interface"] = $_POST["conn{$i}interface"]; + } +} + +function step3_stepbeforeformdisplay() { + global $config, $pkg; + global $stepid, $savemsg; + + $cfgname = "traffic_shaper_wizard_dedicated.xml"; + + $numberofconnections = intval($config['ezshaper']['step1']['numberofconnections']); + + $fields =& $pkg['step'][1]['fields']['field']; + + $voipfields =& $pkg['step'][2]['fields']['field']; + + $voipfields = array(); + $enablefields = array(); + + $field = array(); + $field['name'] = "Enable"; + $field['type'] = "checkbox"; + $field['typehint'] = "Prioritize Voice over IP traffic."; + $field['bindstofield'] = "ezshaper->step3->enable"; + $field['descritpion'] = "This will raise the priority of VOIP traffic above all other traffic."; + $voipfields[] = $field; + + $field = array(); + $field['name'] = "Next"; + $field['type'] = "submit"; + $voipfields[] = $field; + + $field = array(); + $field['name'] = "VOIP specific settings"; + $field['type'] = "listtopic"; + $voipfields[] = $field; + + $field = array(); + $field['name'] = "Provider"; + $enablefields[] = "Provider"; + $field['type'] = "select"; + $field['description'] = "Choose Generic if your provider isn't listed."; + $field['options']['option'] = array(); + $opts = array(); + $opts['name'] = "Generic (lowdelay)"; + $opts['value'] = "Generic"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "VoicePulse"; + $opts['value'] = "VoicePulse"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "Asterisk/Vonage"; + $opts['value'] = "Asterisk"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "PanasonicTDA"; + $opts['value'] = "Panasonic"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step3->provider"; + $voipfields[] = $field; + + $field = array(); + $field['displayname'] = "Upstream SIP Server"; + $field['name'] = "upstream_sip_server"; + $enablefields[] = "upstream_sip_server"; + $field['type'] = "inputalias"; + $field['description'] = "(Optional) If this is chosen, the provider field will be overridden. This allows you to provide the IP address of the <strong>remote</strong> PBX or SIP Trunk to prioritize. <br />NOTE: You can also use a Firewall Alias in this location."; + $field['message'] = "IP Address field is non-blank and doesn't look like an IP address."; + $field['bindstofield'] = "ezshaper->step3->address"; + $voipfields[] = $field; + + for ($i = 0; $i < $numberofconnections; $i++) { + $connum = $i + 1; + $field = array(); + $field['name'] = "Connection #{$connum} parameters"; + $field['type'] = "listtopic"; + $voipfields[] = $field; + + $field = array(); + $field['displayname'] = "Upload"; + $field['name'] = "conn{$i}upload"; + $enablefields[] = "conn{$i}upload"; + $field['type'] = "input"; + $field['bindstofield'] = "ezshaper->step3->conn{$i}upload"; + $field['combinefieldsbegin'] = "true"; + $voipfields[] = $field; + + $field = array(); + $field['combinefieldsend'] = "true"; + $field['dontdisplayname'] = "true"; + $field['dontcombinecells'] = "true"; + $field['name'] = "conn{$i}uploadspeed"; + $enablefields[] = "conn{$i}uploadspeed"; + $field['typehint'] = "Upload bandwidth guarantee for VOIP phone(s) on connection {$i}."; + $field['type'] = "select"; + $field['options']['option'] = array(); + $opts = array(); + $opts['value'] = "Kb"; + $opts['name'] = "Kbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Mb"; + $opts['name'] = "Mbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Gb"; + $opts['name'] = "Gbit/s"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step3->conn{$i}uploadspeed"; + $voipfields[] = $field; + + $field = array(); + $field['displayname'] = "Download"; + $field['name'] = "local{$i}download"; + $enablefields[] = "local{$i}download"; + $field['type'] = "input"; + $field['bindstofield'] = "ezshaper->step3->local{$i}download"; + $field['combinefieldsbegin'] = "true"; + $voipfields[] = $field; + + $field = array(); + $field['combinefieldsend'] = "true"; + $field['dontdisplayname'] = "true"; + $field['dontcombinecells'] = "true"; + $field['name'] = "local{$i}downloadspeed"; + $enablefields[] = "local{$i}downloadspeed"; + $field['typehint'] = "Download bandwidth guarantee for VOIP phone(s) on connections."; + $field['type'] = "select"; + $field['options']['option'] = array(); + $opts = array(); + $opts['value'] = "Kb"; + $opts['name'] = "Kbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Mb"; + $opts['name'] = "Mbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Gb"; + $opts['name'] = "Gbit/s"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step3->local{$i}downloadspeed"; + $voipfields[] = $field; + } + + $field = array(); + $field['name'] = "Next"; + $field['type'] = "submit"; + $voipfields[] = $field; + $voipfields[0]['enablefields'] = implode(",", $enablefields); +} + +function step3_stepsubmitphpaction() { + global $config; + global $stepid, $savemsg; + + if (!$_POST['enable']) + return; + + if($_POST['upstream_sip_server']) { + if(!is_ipaddroralias($_POST['upstream_sip_server'])) { + /* item is not an ip or alias. error out */ + $savemsg=gettext("Address must be a valid IP address or Firewall Alias. Please correct this value to continue."); + $stepid--; + return; + } + } + + $steps = intval($config['ezshaper']['step1']['numberofconnections']); + for ($i = 0; $i < $steps; $i++) { + if ($config['ezshaper']['step2']["conn{$i}uploadscheduler"] != "PRIQ") { + if (!is_numeric($_POST["conn{$i}upload"])) { + $savemsg = gettext("Upload bandwidth of connection {$i} is not valid."); + $stepid--; + return; + } + if ($_POST["conn{$i}uploadspeed"] == "%") { + if (intval($_POST["conn{$i}upload"]) > 80) { + $savemsg=gettext("You cannot set the VoIP upload bandwidth on connection {$i} higher than 80% of the connection."); + $stepid--; + return; + } + } else { + $factor = wizard_get_bandwidthtype_scale($config['ezshaper']['step2']["conn{$i}uploadspeed"]); + $ifbw = $factor * floatval($config['ezshaper']['step2']["conn{$i}upload"]); + $factor = wizard_get_bandwidthtype_scale($_POST["conn{$i}uploadspeed"]); + $input_bw = $factor * floatval($_POST["conn{$i}upload"]); + if ((0.8 * $ifbw) < $input_bw) { + $savemsg=gettext("You cannot set the VoIP upload bandwidth on connection {$i} higher than 80% of the connection."); + $stepid--; + return; + } + } + } + + if ($config['ezshaper']['step2']["local{$i}downloadscheduler"] != "PRIQ") { + if (!is_numeric($_POST["local{$i}download"])) { + $savemsg = gettext("Download bandwidth of connection {$i} is not valid."); + $stepid--; + return; + } + if ($_POST["local{$i}downloadspeed"] == "%") { + if (intval($_POST["local{$i}download"]) > 80) { + $savemsg=gettext("You cannot set the VoIP upload bandwidth on connection {$i} higher than 80% of the connection."); + $stepid--; + return; + } + } else { + $factor = wizard_get_bandwidthtype_scale($config['ezshaper']['step2']["conn{$i}downloadspeed"]); + $ifbw = $factor * floatval($config['ezshaper']['step2']["conn{$i}download"]); + $factor = wizard_get_bandwidthtype_scale($_POST["local{$i}downloadspeed"]); + $input_bw = $factor * floatval($_POST["local{$i}download"]); + if ((0.8 * $ifbw) < $input_bw) { + $savemsg=gettext("You cannot set the VoIP download bandwidth on connection {$i} higher than 80% of the connection."); + $stepid--; + return; + } + } + } + } + + /* This is necessary since the wizard expects predefined fields. */ + unset($config['ezshaper']['step3']); + $config['ezshaper']['step3'] = array(); + + if (!empty($_POST['upstream_sip_server'])) + $config['ezshaper']['step3']['address'] = $_POST['upstream_sip_server']; + if ($_POST['enable'] == 'on') + $config['ezshaper']['step3']['enable'] = 'on'; + for ($i = 0; $i < $steps; $i++) { + $config['ezshaper']['step3']["local{$i}download"] = $_POST["local{$i}download"]; + $config['ezshaper']['step3']["local{$i}downloadspeed"] = $_POST["local{$i}downloadspeed"]; + $config['ezshaper']['step3']["conn{$i}upload"] = $_POST["conn{$i}upload"]; + $config['ezshaper']['step3']["conn{$i}uploadspeed"] = $_POST["conn{$i}uploadspeed"]; + } +} + +function step4_stepsubmitphpaction() { + global $config; + global $stepid, $savemsg; + + if ( $_POST['enable'] ) { + if(!$_POST['bandwidth']) { + $savemsg="You need to specify a value for bandwidth!"; + $stepid--; + return; + } + if(!is_numeric($_POST['bandwidth'])) { + $savemsg="The posted value is not a valid bandwidth."; + $stepid--; + return; + } + + if ($_POST['bandwidthspeed'] <> "%") { + $savemsg = gettext("Only percentage bandwidth specification is allowed."); + $stepid--; + return; + } + $bw = $_POST['bandwidth']; + if($bw > 15 || $bw < 2) { + $savemsg="Values should be between 2% and 15%!"; + $stepid--; + return; + } + if($_POST['address'] <> "" && !is_ipaddroralias($_POST['address'])) { + /* item is not an ip or alias. error out */ + $savemsg=gettext("Address must be a valid IP address or Firewall Alias. Please correct this value to continue."); + $stepid--; + } + } +} + +function step5_stepsubmitphpaction() { + global $stepid, $savemsg; + + if ( $_POST['enable'] ) { + if ($_POST['p2pcatchall']) { + if(!is_numeric($_POST['bandwidth'])) { + $savemsg="Posted value is not a valid bandwidth."; + $stepid--; + return; + } + if ($_POST['bandwidthspeed'] <> "%") { + $savemsg = gettext("Only percentage bandwidth specification is allowed."); + $stepid--; + return; + } + $bw = $_POST['bandwidth']; + if($bw > 15 || $bw < 2) { + $savemsg="Values should be between 2% and 15%!"; + $stepid--; + return; + } + } + } +} + +function step8_stepsubmitphpaction() { + global $g, $config; + + /* save the new configuration */ + apply_all_chosen_items(); + + /* reset rrd queues */ + system("rm -f /var/db/rrd/*queuedrops.rrd"); + system("rm -f /var/db/rrd/*queues.rrd"); + enable_rrd_graphing(); + + /* apply the new configuration to the system */ + filter_configure(); + + /* And we're no longer dirty! */ + clear_subsystem_dirty('shaper'); + + update_filter_reload_status("Initializing"); + header("Location: status_filter_reload.php"); + exit; +} + +function apply_all_chosen_items() { + global $config, $g, $altq_list_queues, $gamesplist, $voiplist, $othersplist, $p2plist; + + require_once("wizardapp.inc"); + + /* + * Wipe previous config. + * Doing it here makes sense since we can wipe the previous config only after + * the user decides to do so, finishing the wizard. + */ + if(isset($config['shaper']['queue'])) + unset($config['shaper']['queue']); + /* XXX: This is redundant, because this should be handled by converter at startup. */ + if(isset($config['shaper']['rule'])) + unset($config['shaper']['rule']); + foreach ($config['filter']['rule'] as $key => $rule) + if ($rule['wizard'] == "yes") + unset($config['filter']['rule'][$key]); + + /* restart the cached config */ + unset($altq_list_queues); + $altq_list_queues = array(); + + $steps = intval($config['ezshaper']['step1']['numberofconnections']); + + $interfacelist = array(); + + for ($i = 0; $i < $steps; $i++) { + + $tmppath = array(); + $altq =& new altq_root_queue(); + + $altq->SetInterface($config['ezshaper']['step2']["conn{$i}interface"]); + $interfacelist[] = $config['ezshaper']['step2']["conn{$i}interface"]; + $altq->SetScheduler($config['ezshaper']['step2']["conn{$i}uploadscheduler"]); + $altq->SetBandwidth(floatval($config['ezshaper']['step2']["conn{$i}upload"])); + $altq->SetBwscale($config['ezshaper']['step2']["conn{$i}uploadspeed"]); + $altq->SetEnabled("on"); + $altq_list_queues[$altq->GetQname()] =& $altq; + array_push($tmppath, $config['ezshaper']['step2']["conn{$i}interface"]); + $altq->SetLink($tmppath); + $altq->wconfig(); + + $sched = $config['ezshaper']['step2']["conn{$i}uploadscheduler"]; + $voipbw =0; + $voipbwunit = "Kb"; + $voip = false; + $penalty = false; + $penaltybw = 0; + $penaltybwunit = "Kb"; + $p2p = false; + $p2pcatchall = false; + $p2pcatchbw = 0; + $p2pcatchbwunit = "%"; + $games = false; + $otherpriority = false; + $remainbw = 0; + $factor = 0; + $upfactor = wizard_get_bandwidthtype_scale($config['ezshaper']['step2']["conn{$i}uploadspeed"]); + $upbw = floatval($config['ezshaper']['step2']["conn{$i}upload"]) * $upfactor; + + if ($config['ezshaper']['step3']['enable']) { + $voip = true; + $voipbw = $config['ezshaper']['step3']["conn{$i}upload"]; + $voipbwunit = $config['ezshaper']['step3']["conn{$i}uploadspeed"]; + if ($voipbwunit == "%") + $factor = $upbw/100; + else + $factor = wizard_get_bandwidthtype_scale($voipbwunit); + $remainbw += $voipbw * $factor; + } + if ($config['ezshaper']['step4']['enable']) { + $penalty = true; + $penaltybw = $config['ezshaper']['step4']['bandwidth']; + $penaltybwunit = $config['ezshaper']['step4']['bandwidthunit']; + if ($penaltybwunit == "%") + $factor = $upbw/100; + else + $factor = wizard_get_bandwidthtype_scale($penaltybwunit); + $remainbw += $penaltybw * $factor; + } else { + $penalty = false; + $penaltybw = 0; + } + if ($config['ezshaper']['step5']['enable']) { + $p2p = true; + if ($config['ezshaper']['step5']['p2pcatchall']) { + $p2pcatchall = true; + $p2pcatchbw = $config['ezshaper']['step5']['bandwidth']; + $p2pcatchbwunit = $config['ezshaper']['step5']['bandwidthunit']; + if ($p2pcatchbwunit == "%") + $factor = $upbw/100; + else + $factor = wizard_get_bandwidthtype_scale($p2pcatchbwunit); + $remainbw += $p2pcatchbw * $factor; + } else { + $p2pcatchall = false; + $p2pcatchbw = 0; + } + } else { + $p2p = false; + $p2pcatchall = false; + $p2pcatchbw = 0; + } + if ($config['ezshaper']['step6']['enable']) { + $games = true; + } else { + $games = false; + } + + if ($config['ezshaper']['step7']['enable']) { + $otherpriority = true; + } else { + $otherpriority = false; + } + $remainbw = round($remainbw / $upbw * 100, 2); + + if (intval($remainbw) > 0 && intval($remainbw) > 30) { + $savemsg=gettext("Custom Bandwidths are greater than 30%. Please lower them for the wizard to continue."); + header("Location: wizard.php?xml=traffic_shaper_wizard_dedicated.xml&stepid=2&message={$savemsg}"); + exit; + } else { + $remainbw = 100 - $remainbw; + } + + if ($sched != "PRIQ") { + if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qInternet"; + //$tmpcf['priority'] = 6; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + If ($sched == "CBQ") { + $tmpcf['bandwidth'] = floatval($config['ezshaper']['step2']["conn{$i}upload"]); + $tmpcf['bandwidthtype'] = $config['ezshaper']['step2']["conn{$i}uploadspeed"]; + } + else if ($sched == "HFSC") { + $tmpcf['upperlimit'] = "on"; + $tmpcf['upperlimit3'] = + floatval($config['ezshaper']['step2']["conn{$i}upload"]) . $config['ezshaper']['step2']["conn{$i}uploadspeed"]; + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = floatval($config['ezshaper']['step2']["conn{$i}upload"]) . $config['ezshaper']['step2']["conn{$i}uploadspeed"]; + $tmpcf['bandwidth'] = floatval($config['ezshaper']['step2']["conn{$i}upload"]); + $tmpcf['bandwidthtype'] = $config['ezshaper']['step2']["conn{$i}uploadspeed"]; + } + array_push($tmppath, "qInternet"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + //array_pop($tmppath); + //echo "qInternet <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + $altq =& $qtmp; + } + + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qACK"; + $tmpcf['priority'] = 6; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + If ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.2; + $tmpcf['bandwidthtype'] = "%"; + } + else if ($sched == "HFSC") { + $lkbw = 0.20 * $remainbw; + $tmpcf['linkshare3'] = "{$lkbw}%"; + $tmpcf['linkshare'] = "on"; + $tmpcf['bandwidth'] = $lkbw; + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, "qACK"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qACK <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + if ($p2pcatchall) + $tmpcf['name'] = "qOthersDefault"; + else + $tmpcf['name'] = "qDefault"; + $tmpcf['priority'] = 3; + $tmpcf['enabled'] = "on"; + if (!$p2pcatchall) + $tmpcf['default'] = "on"; + $tmpcf['ecn'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.1; /* 10% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $tmpcf['bandwidth'] = $remainbw * 0.1; /* 10% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, $tmpcf['name']); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qDefault <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + + if ($p2p) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qP2P"; + $tmpcf['priority'] = 1; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($p2pcatchall) { + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $p2pcatchbw; + $tmpcf['bandwidthtype'] = $p2pcatchbwunit; + } else if ($sched == "HFSC") { + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = "{$p2pcatchbw}{$p2pcatchbwunit}"; + $tmpcf['upperlimit'] = "on"; + $tmpcf['upperlimit3'] = "{$p2pcatchbw}{$p2pcatchbwunit}"; + $tmpcf['bandwidth'] = $p2pcatchbw; + $tmpcf['bandwidthtype'] = $p2pcatchbwunit; + } + $tmpcf['default'] = "on"; + + } else { + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.05; /* 5% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $tmpbw = $remainbw * 0.05; /* 5% bandwidth */ + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = "{$tmpbw}%"; + $tmpcf['upperlimit'] = "on"; + $tmpcf['upperlimit3'] = "{$tmpbw}%"; + $tmpcf['bandwidth'] = $tmpbw; + $tmpcf['bandwidthtype'] = "%"; + } + } + array_push($tmppath, "qP2P"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qP2P <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($voip) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qVoIP"; + $tmpcf['priority'] = 7; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + if ($voipbw > 0) { + $tmpcf['bandwidth'] = $voipbw; + $tmpcf['bandwidthtype'] = $voipbwunit; + } else { + $tmpcf['bandwidth'] = $remainbw * 0.2; /* 20% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } + } else if ($sched == "HFSC") { + if ($voipbw > 0) { + $tmpcf['realtime3'] = "{$voipbw}{$voipbwunit}"; + } else { + $voipbw = $remainbw * 0.20; /* 20% bandwidth */ + $tmpcf['realtime3'] = "{$voipbw}%"; + } + $tmpcf['realtime'] = "on"; + $tmpcf['bandwidth'] = 32; + $tmpcf['bandwidthtype'] = "Kb"; + } + array_push($tmppath, "qVoIP"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qVoIP <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($games) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qGames"; + $tmpcf['priority'] = 5; + $tmpcf['enabled'] = "on"; + $tmpcf['ecn'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.2; /* 20% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $gamesbw = $remainbw * 0.2; /* 20% bandwidth */ + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = "{$gamesbw}%"; + $tmpcf['bandwidth'] = "{$gamesbw}"; + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, "qGames"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qGames <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($otherpriority) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qOthersHigh"; + $tmpcf['priority'] = 4; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.1; /* 10% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $tmpcf['linkshare'] = "on"; + $otherbw = $remainbw * 0.1; /* 10% bandwidth */ + $tmpcf['linkshare3'] = "{$otherbw}%"; + $tmpcf['bandwidth'] = $otherbw; + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, "qOthersHigh"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qHigh <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + + + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qOthersLow"; + $tmpcf['priority'] = 2; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + if ($penalty) { + $tmpcf['bandwidthtype'] = $penaltybwunit; + $tmpcf['bandwidth'] = $penaltybw; + } else { + $tmpcf['bandwidth'] = $remainbw * 0.05; /* 5% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } + } else if ($sched == "HFSC") { + if ($penalty) { + $tmpcf['linkshare3'] = "{$penaltybw}{$penaltybwunit}"; + $tmpcf['bandwidth'] = $penaltybw; + $tmpcf['bandwidthtype'] = $penaltybwunit; + } else { + $lsbw = $remainbw * 0.05; + $tmpcf['linkshare3'] = "{$lsbw}%"; /* 5% bandwidth */ + $tmpcf['bandwidth'] = $lsbw; + $tmpcf['bandwidthtype'] = "%"; + } + $tmpcf['linkshare'] = "on"; + } + array_push($tmppath, "qOthersLow"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qLow <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + array_pop($tmppath); + + $downfactor = wizard_get_bandwidthtype_scale($config['ezshaper']['step2']["conn{$i}downloadspeed"]); + $downbw = floatval($config['ezshaper']['step2']["conn{$i}download"]) * $downfactor; + + $tmppath = array(); + $altq =& new altq_root_queue(); + + $altq->SetInterface($config['ezshaper']['step2']["local{$i}interface"]); + $altq->SetScheduler($config['ezshaper']['step2']["local{$i}downloadscheduler"]); + //$altq->SetBandwidth($config['ezshaper']['step2']["conn{$i}download"]); + //$altq->SetBwscale($config['ezshaper']['step2']["conn{$i}downloadspeed"]); + $altq->SetEnabled("on"); + $altq_list_queues[$altq->GetQname()] =& $altq; + array_push($tmppath, $config['ezshaper']['step2']["local{$i}interface"]); + $altq->SetLink($tmppath); + //var_dump($input_errors); + $altq->wconfig(); + + $sched = $config['ezshaper']['step2']["local{$i}downloadscheduler"]; + $voipbw =0; + $voipbwunit = "%"; + $voip = false; + $penalty = false; + $penaltybw = 0; + $penaltybwunit = "%"; + $p2p = false; + $p2pcatchall = false; + $p2pcatchbw = 0; + $games = false; + $otherpriority = false; + $remainbw = 0; + + if ($config['ezshaper']['step3']['enable']) { + $voip = true; + $voipbw = $config['ezshaper']['step3']["local{$i}download"]; + $voipbwunit = $config['ezshaper']['step3']["local{$i}downloadspeed"]; + if ($sched != HFSC) { + if ($voipbwunit == "%") + $factor = $downbw/100; + else + $factor = wizard_get_bandwidthtype_scale($voipbwunit); + $remainbw += floatval($voipbw) * $factor; + } else + $remainbw += 32000; /* 32Kbit/s reserved for HFSC link sharing */ + } + if ($config['ezshaper']['step4']['enable']) { + $penalty = true; + $penaltybw = $config['ezshaper']['step4']['bandwidth']; + $penaltybwunit = $config['ezshaper']['step4']['bandwidthunit']; + if ($penaltybwunit == "%") + $factor = $downbw/100; + else + $factor = wizard_get_bandwidthtype_scale($penaltybwunit); + $remainbw += floatval($penaltybw) * $factor; + } else { + $penalty = false; + $penaltybw = 0; + } + if ($config['ezshaper']['step5']['enable']) { + $p2p = true; + if ($config['ezshaper']['step5']['p2pcatchall']) { + $p2pcatchall = true; + $p2pcatchbw = $config['ezshaper']['step5']['bandwidth']; + $p2pcatchbwunit = $config['ezshaper']['step5']['bandwidthunit']; + if ($p2pcatchbwunit == "%") + $factor = $downbw/100; + else + $factor = wizard_get_bandwidthtype_scale($p2pcatchbwunit); + $remainbw += floatval($p2pcatchbw) * $factor; + } else { + $p2pcatchall = false; + $p2pcatchbw = 0; + } + } else { + $p2p = false; + $p2pcatchall = false; + $p2pcatchbw = 0; + } + if ($config['ezshaper']['step6']['enable']) { + $games = true; + } else { + $games = false; + } + + if ($config['ezshaper']['step7']['enable']) { + $otherpriority = true; + } else { + $otherpriority = false; + } + $remainbw = round($remainbw / $downbw * 100, 2); + if (intval($remainbw) > 0 && intval($remainbw) > 40) { + $savemsg=gettext("Custom Bandwidths are greater than 40%. Please lower them for the wizard to continue."); + header("Location: wizard.php?xml=traffic_shaper_wizard_dedicated.xml&stepid=2&message={$savemsg}"); + exit; + } else { + $remainbw = 100 - $remainbw; + } + + if (!$p2pcatchall) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qLink"; + $tmpcf['priority'] = 2; + $tmpcf['enabled'] = "on"; + $tmpcf['default'] = "on"; + $tmpcf['qlimit'] = 500; + $tmpcf['ecn'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = 20; /* 20% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $tmpcf['bandwidth'] = 20; /* 20% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, $tmpcf['name']); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qDefault <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($sched != "PRIQ") { + if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qInternet"; + //$tmpcf['priority'] = 6; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + If ($sched == "CBQ") { + $tmpcf['bandwidth'] = floatval($config['ezshaper']['step2']["conn{$i}download"]); + $tmpcf['bandwidthtype'] = $config['ezshaper']['step2']["conn{$i}downloadspeed"]; + } + else if ($sched == "HFSC") { + $tmpcf['upperlimit'] = "on"; + $tmpcf['upperlimit3'] = floatval($config['ezshaper']['step2']["conn{$i}download"]) . $config['ezshaper']['step2']["conn{$i}downloadspeed"]; + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = floatval($config['ezshaper']['step2']["conn{$i}download"]) . $config['ezshaper']['step2']["conn{$i}downloadspeed"]; + $tmpcf['bandwidth'] = floatval($config['ezshaper']['step2']["conn{$i}download"]); + $tmpcf['bandwidthtype'] = $config['ezshaper']['step2']["conn{$i}downloadspeed"]; + } + array_push($tmppath, "qInternet"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + //array_pop($tmppath); + //echo "qInternet <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + $altq =& $qtmp; + } + + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qACK"; + $tmpcf['priority'] = 6; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + If ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.2; + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $lkbw = 0.20 * $remainbw; + $tmpcf['linkshare3'] = "{$lkbw}%"; + $tmpcf['linkshare'] = "on"; + $tmpcf['bandwidth'] = $lkbw; + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, "qACK"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qACK $remainbw <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + + if ($p2p) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qP2P"; + $tmpcf['priority'] = 1; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($p2pcatchall) { + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $p2pcatchbw; + $tmpcf['bandwidthtype'] = $p2pcatchbwunit; + } else if ($sched == "HFSC") { + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = "{$p2pcatchbw}{$p2pcatchbwunit}"; + $tmpcf['upperlimit'] = "on"; + $tmpcf['upperlimit3'] = "{$p2pcatchbw}{$p2pcatchbwunit}"; + $tmpcf['bandwidth'] = $p2pcatchbw; + $tmpcf['bandwidthtype'] = $p2pcatchbwunit; + } + $tmpcf['default'] = "on"; + $tmpcf['qlimit'] = 500; + } else { + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.05; /* 5% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $tmpbw = $remainbw * 0.05; /* 5% bandwidth */ + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = "{$tmpbw}%"; + $tmpcf['upperlimit'] = "on"; + $tmpcf['upperlimit3'] = "{$tmpbw}%"; + $tmpcf['bandwidth'] = $tmpbw; + $tmpcf['bandwidthtype'] = "%"; + } + } + array_push($tmppath, "qP2P"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qP2P <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($voip) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qVoIP"; + $tmpcf['priority'] = 7; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + if ($voipbw > 0) { + $tmpcf['bandwidth'] = $voipbw; + $tmpcf['bandwidthtype'] = $voipbwunit; + } else { + $tmpcf['bandwidth'] = $remainbw * 0.2; /* 20% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } + } else if ($sched == "HFSC") { + if ($voipbw > 0) { + $tmpcf['realtime3'] = "{$voipbw}{$voipbwunit}"; + } else { + $voipbw = $remainbw * 0.20; /* 20% bandwidth */ + $tmpcf['realtime3'] = "{$voipbw}%"; + } + $tmpcf['realtime'] = "on"; + $tmpcf['bandwidth'] = 32; + $tmpcf['bandwidthtype'] = "Kb"; + } + array_push($tmppath, "qVoIP"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qVoIP <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($games) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qGames"; + $tmpcf['priority'] = 5; + $tmpcf['enabled'] = "on"; + $tmpcf['ecn'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.2; /* 20% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $gamesbw = $remainbw * 0.2; /* 20% bandwidth */ + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = "{$gamesbw}%"; + $tmpcf['bandwidth'] = "{$gamesbw}"; + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, "qGames"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qGames <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($otherpriority) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qOthersHigh"; + $tmpcf['priority'] = 4; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.1; /* 10% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $tmpcf['linkshare'] = "on"; + $otherbw = $remainbw * 0.1; /* 10% bandwidth */ + $tmpcf['linkshare3'] = "{$otherbw}%"; + $tmpcf['bandwidth'] = $otherbw; + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, "qOthersHigh"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qHigh <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qOthersLow"; + $tmpcf['priority'] = 3; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + if ($penalty) { + $tmpcf['bandwidth'] = $penaltybw; + $tmpcf['bandwidthtype'] = $penaltybwunit; + } else { + $tmpcf['bandwidthtype'] = "%"; + $tmpcf['bandwidth'] = $remainbw * 0.05; /* 5% bandwidth */ + } + } else if ($sched == "HFSC") { + if ($penalty) { + $tmpcf['linkshare3'] = "{$penaltybw}{$penaltybwunit}"; + $tmpcf['bandwidth'] = $penaltybw; + $tmpcf['bandwidthtype'] = $penaltybwunit; + } else { + $lsbw = $remainbw * 0.05; + $tmpcf['linkshare3'] = "{$lsbw}%"; /* 5% bandwidth */ + $tmpcf['bandwidth'] = $lsbw; + $tmpcf['bandwidthtype'] = "%"; + } + $tmpcf['linkshare'] = "on"; + } + array_push($tmppath, "qOthersLow"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qLow <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + array_pop($tmppath); + } + + if (!is_array($config['filter']['rule'])) + $config['filter']['rule'] = array(); + + $interfacelist = implode(",", $interfacelist); + + /* Rules */ + if ($penalty) { + if( is_ipaddr($config['ezshaper']['step4']['address']) || is_alias($config['ezshaper']['step4']['address'])) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['descr'] = gettext("Penalty Box"); + $rule['defaultqueue'] = "qOthersLow"; + $rule['source']['address'] = $config['ezshaper']['step4']['address']; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + } + } + + /* If user specifies an IP, we don't bother with providers */ + if ($voip) { + if( is_ipaddr($config['ezshaper']['step3']['address']) || is_alias($config['ezshaper']['step3']['address'])) { + /* create VOIP rules */ + $rule = array(); + $rule['type'] = "match"; + //$rule['interface'] = $interfacelist; + $rule['descr'] = gettext("Connections From Upstream SIP Server"); + $rule['protocol'] = "udp"; + $rule['defaultqueue'] = "qVoIP"; + $rule['source']['address'] = $config['ezshaper']['step3']['address']; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + + $rule = array(); + $rule['type'] = "match"; + //$rule['interface'] = $interfacelist; + $rule['descr'] = gettext("Connections To Upstream SIP Server"); + $rule['protocol'] = "udp"; + $rule['defaultqueue'] = "qVoIP"; + $rule['source']['any'] = TRUE; + $rule['destination']['address'] = $config['ezshaper']['step3']['address']; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + + } elseif( $config['ezshaper']['step3']['provider'] == "Generic" ) { + /* create VOIP rules */ + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['descr'] = "DiffServ/Lowdelay/Upload"; + $rule['protocol'] = "udp"; + $rule['source']['any'] = TRUE; + $rule['defaultqueue'] = "qVoIP"; + $rule['destination']['any'] = TRUE; + $rule['iptos'] = "lowdelay"; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + + } else { + /* loop through voiplist[] */ + foreach ($voiplist[$config['ezshaper']['step3']['provider']] as $voip) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['defaultqueue'] = 'qVoIP'; + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['descr'] = "m_voip {$voip[0]} outbound"; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['destination']['port'] = $voip[2]."-".$voip[3]; + if($voip[1] != '') + $rule['protocol'] = $voip[1]; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + } + } + } + + /* loop through p2plist[] */ + if ($p2p) { + foreach($config['ezshaper']['step5'] as $key => $val) { + if (!is_array($p2plist[$key])) + continue; + foreach ($p2plist[$key] as $p2pclient) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['defaultqueue'] = 'qP2P'; + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['descr'] = "m_P2P {$p2pclient[0]} outbound"; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['destination']['port'] = $p2pclient[2]."-".$p2pclient[3]; + if($p2pclient[1] != '') + $rule['protocol'] = $p2pclient[1]; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + } + } + } + + /* loop through gamesplist[] */ + if ($games) { + foreach($config['ezshaper']['step6'] as $key => $val) { + if (!is_array($gamesplist[$key])) + continue; + foreach ($gamesplist[$key] as $Gameclient) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['defaultqueue'] = 'qGames'; + if ($Gameclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['descr'] = "m_Game {$Gameclient[0]} outbound"; + $rule['destination']['port'] = $Gameclient[2]."-".$Gameclient[3]; + if($Gameclient[1] != '') + $rule['protocol'] = $Gameclient[1]; + $config['filter']['rule'][] = $rule; + } + } + } + + /* loop through othersplist[] */ + if ($otherpriority) { + foreach($config['ezshaper']['step7'] as $key => $val) { + if (!is_array($othersplist[$key])) + continue; + foreach ($othersplist[$key] as $otherclient) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + switch ($val) { + case "H": + $rule['defaultqueue'] = 'qOthersHigh'; /* posted value H or L */ + if ($otherclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + $loop = 0; + break; + case "L": + $rule['defaultqueue'] = 'qOthersLow'; /* posted value H or L */ + if ($otherclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + $loop = 0; + break; + case "D": + if ($p2pcatchall) { + $loop = 0; + $rule['defaultqueue'] = 'qOthersDefault'; + if ($otherclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + } else + $loop = 1; /* It automatically goes to default queue */ + break; + default: + $loop = 1; + } + if (!$loop) { + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['descr'] = "m_Other {$otherclient[0]} outbound"; + + if($otherclient[2] or $otherclient[3]) { + $rule['destination']['port'] = $otherclient[2]."-".$otherclient[3]; + } + if($otherclient[1] != '') + $rule['protocol'] = $otherclient[1]; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + } + } + } + } + write_config(); +} + +function wizard_get_bandwidthtype_scale($type = "b") { + switch ($type) { + case "Gb": + $factor = 1024 * 1024 * 1024; + break; + case "Mb": + $factor = 1024 * 1024; + break; + case "Kb": + $factor = 1024; + break; + case "b": + default: + $factor = 1; + break; + } + return intval($factor); +} + +?> diff --git a/src/usr/local/www/wizards/traffic_shaper_wizard_dedicated.xml b/src/usr/local/www/wizards/traffic_shaper_wizard_dedicated.xml new file mode 100755 index 0000000..c86a72a --- /dev/null +++ b/src/usr/local/www/wizards/traffic_shaper_wizard_dedicated.xml @@ -0,0 +1,1649 @@ +<?xml version="1.0"?> +<pfsensewizard> + <copyright><![CDATA[ + /* + traffic_shaper_wizard_dedicated.xml + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2005 Bill Marquette - bill.marquette@gmail.com. + Copyright (C) 2008-2010 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */]]></copyright> + + <totalsteps>9</totalsteps> + <step> + <id>1</id> + <title>pfSense Traffic Shaper Wizard</title> + <disableheader>true</disableheader> + <description>This wizard will guide you through setting up the pfSense traffic shaper. + Please be aware that Custom Bandwidths should not exceed 30% of the interface/link bandwidth. Keep this in mind during the wizard. + </description> + <fields> + <field> + <type>listtopic</type> + <name>Traffic shaper Wizard</name> + </field> + <field> + <displayname>Enter number of WAN type connections</displayname> + <name>numberofconnections</name> + <type>input</type> + <validate>^[0-9]+$</validate> + <description>Number of connections you have</description> + <bindstofield>ezshaper->step1->numberofconnections</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepbeforeformdisplay>step1_stepbeforeformdisplay();</stepbeforeformdisplay> + <stepsubmitphpaction>step1_submitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc</includefile> + </step> + <step> + <id>2</id> + <title>pfSense Traffic Shaper Wizard</title> + <description>Shaper configuration</description> + <javascriptafterformdisplay/> + <stepbeforeformdisplay>step2_stepbeforeformdisplay();</stepbeforeformdisplay> + <stepsubmitphpaction>step2_stepsubmitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc</includefile> + <fields> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + </step> + <step> + <id>3</id> + <title>pfSense Traffic Shaper Wizard</title> + <description>Voice over IP</description> + <fields> + <field> + <name>Enable</name> + <type>checkbox</type> + <typehint>Prioritize Voice over IP traffic</typehint> + <description>This will raise the priority of VOIP traffic above all other traffic.</description> + <bindstofield>ezshaper->step3->enable</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + <field> + <name>VOIP specific settings</name> + <type>listtopic</type> + </field> + <field> + <name>Provider</name> + <type>select</type> + <description>Choose Generic if your provider isn't listed.</description> + <bindstofield>ezshaper->step3->provider</bindstofield> + <options> + <option> + <name>Generic (lowdelay)</name> + <value>Generic</value> + </option> + <option> + <name>VoicePulse</name> + <value>VoicePulse</value> + </option> + <option> + <name>Asterisk/Vonage</name> + <value>Asterisk</value> + </option> + <option> + <name>PanasonicTDA</name> + <value>Panasonic</value> + </option> + </options> + </field> + <field> + <displayname>Upstream SIP Server</displayname> + <name>upstream_sip_server</name> + <type>inputalias</type> + <description>(Optional) If this is chosen, the provider field will be overridden. This allows you to provide the IP address of the <strong>remote</strong> PBX or SIP Trunk to prioritize. <br />NOTE: You can also use a Firewall Alias in this location.</description> + <bindstofield>ezshaper->step3->address</bindstofield> + <message>IP Address field is non-blank and doesn't look like an IP address.</message> + </field> + <field> + <name>Bandwidth</name> + <type>input</type> + <validate>^[0-9]*$</validate> + <typehint>Total bandwidth in percentage(%)(should be between 5 and 40) guarantee for VOIP traffic.</typehint> + <bindstofield>ezshaper->step3->bandwidth</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepbeforeformdisplay>step3_stepbeforeformdisplay();</stepbeforeformdisplay> + <stepsubmitphpaction>step3_stepsubmitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc</includefile> + </step> + <step> + <id>4</id> + <title>pfSense Traffic Shaper Wizard</title> + <description>Penalty Box</description> + <disableallfieldsbydefault>true</disableallfieldsbydefault> + <fields> + <field> + <donotdisable>true</donotdisable> + <name>Enable</name> + <type>checkbox</type> + <typehint>Penalize IP or Alias</typehint> + <description>This will lower the priority of traffic from this IP or alias.</description> + <enablefields>Address,Bandwidth,BandwidthSpeed</enablefields> + <bindstofield>ezshaper->step4->enable</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + <field> + <name>PenaltyBox specific settings</name> + <type>listtopic</type> + </field> + <field> + <name>Address</name> + <type>inputalias</type> + <description>This allows you to just provide the IP address of the computer(s) to penalize. NOTE: You can also use a Firewall Alias in this location.</description> + <bindstofield>ezshaper->step4->address</bindstofield> + <message>IP Address field is non-blank and doesn't look like an IP address.</message> + </field> + <field> + <name>Bandwidth</name> + <type>input</type> + <validate>^[0-9]*$</validate> + <message>Speed must be numerical.</message> + <bindstofield>ezshaper->step4->bandwidth</bindstofield> + <combinefieldsbegin>true</combinefieldsbegin> + </field> + <field> + <combinefieldsend>true</combinefieldsend> + <dontdisplayname>true</dontdisplayname> + <dontcombinecells>true</dontcombinecells> + <donotdisable>true</donotdisable> + <name>BandwidthSpeed</name> + <description>The limit you want to apply.</description> + <type>select</type> + <options> + <option> + <name>%</name> + <value>%</value> + </option> + <option> + <name>bit/s</name> + <value>b</value> + </option> + <option> + <name>Kilobit/s</name> + <value>Kb</value> + </option> + <option> + <name>Megabit/s</name> + <value>Mb</value> + </option> + <option> + <name>Gigabit/s</name> + <value>Gb</value> + </option> + </options> + <bindstofield>ezshaper->step4->bandwidthunit</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepsubmitphpaction>step4_stepsubmitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc</includefile> + </step> + <step> + <id>5</id> + <title>pfSense Traffic Shaper Wizard</title> + <description>Peer to Peer networking</description> + <disableallfieldsbydefault>true</disableallfieldsbydefault> + <fields> + <field> + <donotdisable>true</donotdisable> + <name>Enable</name> + <type>checkbox</type> + <typehint>Lower priority of Peer-to-Peer traffic</typehint> + <description>This will lower the priority of P2P traffic below all other traffic. Please check the items that you would like to prioritize lower than normal traffic.</description> + <enablefields>p2pCatchAll,Bandwidth,BandwidthSpeed,Aimster,BitTorrent,BuddyShare,CuteMX,DCplusplus,dcc,DirectConnect,DirectFileExpress,EDonkey2000,FastTrack,Gnutella,grouper,hotComm,HotlineConnect,iMesh,Napster,OpenNap,Scour,Shareaza,SongSpy,WinMX</enablefields> + <bindstofield>ezshaper->step5->enable</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + <field> + <name>p2p Catch all</name> + <type>listtopic</type> + </field> + <field> + <name>p2pCatchAll</name> + <type>checkbox</type> + <typehint>When enabled, all uncategorized traffic is fed to the p2p queue.</typehint> + <bindstofield>ezshaper->step5->p2pcatchall</bindstofield> + </field> + <field> + <name>Bandwidth</name> + <type>input</type> + <validate>^[0-9]*$</validate> + <message>Speed must be numerical.</message> + <bindstofield>ezshaper->step5->bandwidth</bindstofield> + <combinefieldsbegin>true</combinefieldsbegin> + </field> + <field> + <combinefieldsend>true</combinefieldsend> + <dontdisplayname>true</dontdisplayname> + <dontcombinecells>true</dontcombinecells> + <donotdisable>true</donotdisable> + <name>BandwidthSpeed</name> + <description>The limit you want to apply.</description> + <type>select</type> + <options> + <option> + <name>%</name> + <value>%</value> + </option> + <option> + <name>bit/s</name> + <value>b</value> + </option> + <option> + <name>Kilobit/s</name> + <value>Kb</value> + </option> + <option> + <name>Megabit/s</name> + <value>Mb</value> + </option> + <option> + <name>Gigabit/s</name> + <value>Gb</value> + </option> + </options> + <bindstofield>ezshaper->step5->bandwidthunit</bindstofield> + </field> + <field> + <name>Enable/Disable specific P2P protocols</name> + <type>listtopic</type> + </field> + <field> + <name>Aimster</name> + <type>checkbox</type> + <typehint>Aimster and other P2P using the Aimster protocol and ports</typehint> + <bindstofield>ezshaper->step5->aimster</bindstofield> + </field> + <field> + <name>BitTorrent</name> + <type>checkbox</type> + <typehint>Bittorrent and other P2P using the Torrent protocol and ports</typehint> + <bindstofield>ezshaper->step5->bittorrent</bindstofield> + </field> + <field> + <name>BuddyShare</name> + <type>checkbox</type> + <typehint>BuddyShare and other P2P using the BuddyShare protocol and ports</typehint> + <bindstofield>ezshaper->step5->buddyshare</bindstofield> + </field> + <field> + <name>CuteMX</name> + <type>checkbox</type> + <typehint>CuteMX and other P2P using the CuteMX protocol and ports</typehint> + <bindstofield>ezshaper->step5->cutemx</bindstofield> + </field> + <field> + <name>DCplusplus</name> + <type>checkbox</type> + <typehint>DC++ and other P2P using the DC++ protocol and ports</typehint> + <bindstofield>ezshaper->step5->dcplusplus</bindstofield> + </field> + <field> + <name>DCC</name> + <type>checkbox</type> + <typehint>irc DCC file transfers</typehint> + <bindstofield>ezshaper->step5->dcc</bindstofield> + </field> + <field> + <name>DirectConnect</name> + <type>checkbox</type> + <typehint>DirectConnect and other P2P using the DirectConnect protocol and ports</typehint> + <bindstofield>ezshaper->step5->directconnect</bindstofield> + </field> + <field> + <name>DirectFileExpress</name> + <type>checkbox</type> + <typehint>DirectFileExpress and other P2P using the DirectFileExpress protocol and ports</typehint> + <bindstofield>ezshaper->step5->directfileexpress</bindstofield> + </field> + <field> + <name>eDonkey2000</name> + <type>checkbox</type> + <typehint>eDonkey and other P2P using the eDonkey protocol and ports</typehint> + <bindstofield>ezshaper->step5->edonkey2000</bindstofield> + </field> + <field> + <name>FastTrack</name> + <type>checkbox</type> + <typehint>FastTrack and other P2P using the FastTrack protocol and ports</typehint> + <bindstofield>ezshaper->step5->fasttrack</bindstofield> + </field> + <field> + <name>Gnutella</name> + <type>checkbox</type> + <typehint>Gnutella and other P2P using the Gnutella protocol and ports</typehint> + <bindstofield>ezshaper->step5->gnutella</bindstofield> + </field> + <field> + <name>grouper</name> + <type>checkbox</type> + <typehint>grouper and other P2P using the grouper protocol and ports</typehint> + <bindstofield>ezshaper->step5->grouper</bindstofield> + </field> + <field> + <name>hotComm</name> + <type>checkbox</type> + <typehint>hotComm and other P2P using the hotComm protocol and ports</typehint> + <bindstofield>ezshaper->step5->hotcomm</bindstofield> + </field> + <field> + <name>HotlineConnect</name> + <type>checkbox</type> + <typehint>HotlineConnect and other P2P using the HotlineConnect protocol and ports</typehint> + <bindstofield>ezshaper->step5->hotlineconnect</bindstofield> + </field> + <field> + <name>iMesh</name> + <type>checkbox</type> + <typehint>iMesh and other P2P using the iMesh protocol and ports</typehint> + <bindstofield>ezshaper->step5->imesh</bindstofield> + </field> + <field> + <name>Napster</name> + <type>checkbox</type> + <typehint>Napster and other P2P using the Napster protocol and ports</typehint> + <bindstofield>ezshaper->step5->napster</bindstofield> + </field> + <field> + <name>OpenNap</name> + <type>checkbox</type> + <typehint>OpenNap and other P2P using the OpenNap protocol and ports</typehint> + <bindstofield>ezshaper->step5->opennap</bindstofield> + </field> + <field> + <name>Scour</name> + <type>checkbox</type> + <typehint>Scour and other P2P using the Scour protocol and ports</typehint> + <bindstofield>ezshaper->step5->scour</bindstofield> + </field> + <field> + <name>Shareaza</name> + <type>checkbox</type> + <typehint>Shareaza and other P2P using the Shareaza protocol and ports</typehint> + <bindstofield>ezshaper->step5->shareaza</bindstofield> + </field> + <field> + <name>SongSpy</name> + <type>checkbox</type> + <typehint>SongSpy and other P2P using the SongSpy protocol and ports</typehint> + <bindstofield>ezshaper->step5->songspy</bindstofield> + </field> + <field> + <name>WinMX</name> + <type>checkbox</type> + <typehint>WinMX and other P2P using the WinMX protocol and ports</typehint> + <bindstofield>ezshaper->step5->winmx</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepsubmitphpaction>step5_stepsubmitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc</includefile> + </step> + <step> + <id>6</id> + <title>pfSense Traffic Shaper Wizard</title> + <disableallfieldsbydefault>true</disableallfieldsbydefault> + <description>Network Games</description> + <fields> + <field> + <name>Enable</name> + <type>checkbox</type> + <typehint>Prioritize network gaming traffic</typehint> + <description>This will raise the priority of gaming traffic to higher than most traffic.</description> + <enablefields>BattleNET,EAOrigin,GameForWindowsLive,PlayStationConsoles,Steam,WiiConsoles,XboxConsoles,ARMA2,ARMA3,Battlefield2,Battlefield3,BattlefieldBC2,Borderlands,CallOfDuty,Counterstrike,Crysis2,Crysis3,DeltaForce,DeadSpace2,DeadSpace3,Dirt3,DOOM3,DragonAge2,EmpireEarth,EveOnline,Everquest,Everquest2,FarCry,FarCry2,FarCry3,GunZOnline,HalfLife,LeagueofLegends,Lineage2,MassEffect3,MechwarriorOnline,Minecraft,OperationFlashpointDR,PlanetSide,PlanetSide2,QuakeIII,QuakeIV,StarWarsTOR,TigerWoods2004PS2,TribesAscend,UnrealTournament,WolfensteinEnemyTerritory,WorldOfWarcraft</enablefields> + <donotdisable>true</donotdisable> + <bindstofield>ezshaper->step6->enable</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + <field> + <name>Enable/Disable specific game consoles and services</name> + <type>listtopic</type> + </field> + <field> + <name>BattleNET</name> + <type>checkbox</type> + <typehint>Battle.net - Virtually every game from Blizzard publishing should match this. This includes the following game series: Starcraft, Diablo, Warcraft. Guild Wars also uses this port.</typehint> + <bindstofield>ezshaper->step6->battlenet</bindstofield> + </field> + <field> + <name>EAOrigin</name> + <type>checkbox</type> + <typehint>EA Origin Client - Some PC games by EA use this.</typehint> + <bindstofield>ezshaper->step6->eaorigin</bindstofield> + </field> + <field> + <name>GameForWindowsLive</name> + <type>checkbox</type> + <typehint>Games for Windows Live</typehint> + <bindstofield>ezshaper->step6->gamesforwindowslive</bindstofield> + </field> + <field> + <name>PlayStationConsoles</name> + <type>checkbox</type> + <typehint>PlayStation Consoles - This should cover all ports required for the Playstation 4, Playstation, PS Vita</typehint> + <bindstofield>ezshaper->step6->playstationconsoles</bindstofield> + </field> + <field> + <name>Steam</name> + <type>checkbox</type> + <typehint>Steam Game Client (Includes: America's Army 3, Counter-Strike: Source, Counter-Strike: Global Offensive, Half-Life 2, COD: Black Ops Series, Borderlands 2, Natural Selection 2, Left 4 Dead Series, Portal 2 and many other games on the Steam)</typehint> + <bindstofield>ezshaper->step6->steam</bindstofield> + </field> + <field> + <name>WiiConsoles</name> + <type>checkbox</type> + <typehint>Wii Consoles - Wii, Wii U, DS and 3DS</typehint> + <bindstofield>ezshaper->step6->wiiconsoles</bindstofield> + </field> + <field> + <name>XboxConsoles</name> + <type>checkbox</type> + <typehint>Xbox Consoles - Xbox 360 and Xbox One</typehint> + <bindstofield>ezshaper->step6->xboxconsoles</bindstofield> + </field> + <field> + <name>Enable/Disable specific games</name> + <type>listtopic</type> + </field> + <field> + <name>ARMA2</name> + <type>checkbox</type> + <typehint>ARMA 2</typehint> + <bindstofield>ezshaper->step6->arma2</bindstofield> + </field> + <field> + <name>ARMA3</name> + <type>checkbox</type> + <typehint>ARMA 3</typehint> + <bindstofield>ezshaper->step6->arma3</bindstofield> + </field> + <field> + <name>Battlefield2</name> + <type>checkbox</type> + <typehint>Battlefield 2 - this game uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->battlefield2</bindstofield> + </field> + <field> + <name>Battlefield3</name> + <type>checkbox</type> + <typehint>Battlefield 3 and 4 - this game uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->battlefield3</bindstofield> + </field> + <field> + <name>BattlefieldBC2</name> + <type>checkbox</type> + <typehint>Battlefield: Bad Company 2</typehint> + <bindstofield>ezshaper->step6->battlefieldbc2</bindstofield> + </field> + <field> + <name>Borderlands</name> + <type>checkbox</type> + <typehint>Borderlands</typehint> + <bindstofield>ezshaper->step6->borderlands</bindstofield> + </field> + <field> + <name>CallOfDuty</name> + <type>checkbox</type> + <typehint>Call Of Duty (United Offensive)</typehint> + <bindstofield>ezshaper->step6->callofduty</bindstofield> + </field> + <field> + <name>Counterstrike</name> + <type>checkbox</type> + <typehint>Counterstrike. The ultimate 1st person shooter.</typehint> + <bindstofield>ezshaper->step6->counterstrike</bindstofield> + </field> + <field> + <name>Crysis2</name> + <type>checkbox</type> + <typehint>Crysis 2</typehint> + <bindstofield>ezshaper->step6->crysis2</bindstofield> + </field> + <field> + <name>Crysis3</name> + <type>checkbox</type> + <typehint>Crysis 3</typehint> + <bindstofield>ezshaper->step6->crysis3</bindstofield> + </field> + <field> + <name>DeadSpace2</name> + <type>checkbox</type> + <typehint>Dead Space2 - this game uses a HUGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->deadspace2</bindstofield> + </field> + <field> + <name>DeadSpace3</name> + <type>checkbox</type> + <typehint>Dead Space 3</typehint> + <bindstofield>ezshaper->step6->deadspace3</bindstofield> + </field> + <field> + <name>DeltaForce</name> + <type>checkbox</type> + <typehint>Delta Force</typehint> + <bindstofield>ezshaper->step6->deltaforce</bindstofield> + </field> + <field> + <name>Dirt3</name> + <type>checkbox</type> + <typehint>Dirt 3</typehint> + <bindstofield>ezshaper->step6->dirt3</bindstofield> + </field> + <field> + <name>DOOM3</name> + <type>checkbox</type> + <typehint>DOOM3</typehint> + <bindstofield>ezshaper->step6->doom3</bindstofield> + </field> + <field> + <name>DragonAge2</name> + <type>checkbox</type> + <typehint>Dragon Age 2</typehint> + <bindstofield>ezshaper->step6->dragonage2</bindstofield> + </field> + <field> + <name>EmpireEarth</name> + <type>checkbox</type> + <typehint>Empire Earth</typehint> + <bindstofield>ezshaper->step6->empireearth</bindstofield> + </field> + <field> + <name>EveOnline</name> + <type>checkbox</type> + <typehint>EVE Online</typehint> + <bindstofield>ezshaper->step6->eveonline</bindstofield> + </field> + <field> + <name>Everquest</name> + <type>checkbox</type> + <typehint>Everquest - this game uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->everquest</bindstofield> + </field> + <field> + <name>Everquest2</name> + <type>checkbox</type> + <typehint>Everquest II</typehint> + <bindstofield>ezshaper->step6->everquest2</bindstofield> + </field> + <field> + <name>FarCry</name> + <type>checkbox</type> + <typehint>Far Cry</typehint> + <bindstofield>ezshaper->step6->farcry</bindstofield> + </field> + <field> + <name>FarCry2</name> + <type>checkbox</type> + <typehint>Far Cry 2</typehint> + <bindstofield>ezshaper->step6->farcry2</bindstofield> + </field> + <field> + <name>FarCry3</name> + <type>checkbox</type> + <typehint>Far Cry 3</typehint> + <bindstofield>ezshaper->step6->farcry3</bindstofield> + </field> + <field> + <name>GunZOnline</name> + <type>checkbox</type> + <typehint>GunZ Online</typehint> + <bindstofield>ezshaper->step6->gunzonline</bindstofield> + </field> + <field> + <name>HalfLife</name> + <type>checkbox</type> + <typehint>Half-Life</typehint> + <bindstofield>ezshaper->step6->halflife</bindstofield> + </field> + <field> + <name>LeagueofLegends</name> + <type>checkbox</type> + <typehint>League of Legends - this game uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->leagueoflegends</bindstofield> + </field> + <field> + <name>Lineage2</name> + <type>checkbox</type> + <typehint>Lineage II</typehint> + <bindstofield>ezshaper->step6->lineage2</bindstofield> + </field> + <field> + <name>MassEffect3</name> + <type>checkbox</type> + <typehint>Mass Effect 3</typehint> + <bindstofield>ezshaper->step6->masseffect3</bindstofield> + </field> + <field> + <name>MechwarriorOnline</name> + <type>checkbox</type> + <typehint>MechWarrior: Online - this game uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->mechwarrioronline</bindstofield> + </field> + <field> + <name>Minecraft</name> + <type>checkbox</type> + <typehint>Minecraft</typehint> + <bindstofield>ezshaper->step6->minecraft</bindstofield> + </field> + <field> + <name>PlanetSide</name> + <type>checkbox</type> + <typehint>PlanetSide</typehint> + <bindstofield>ezshaper->step6->planetside</bindstofield> + </field> + <field> + <name>PlanetSide2</name> + <type>checkbox</type> + <typehint>PlanetSide 2</typehint> + <bindstofield>ezshaper->step6->planetside2</bindstofield> + </field> + <field> + <name>OperationFlashpointDR</name> + <type>checkbox</type> + <typehint>Operation Flashpoint: Dragon Rising</typehint> + <bindstofield>ezshaper->step6->operationflashpoint-dr</bindstofield> + </field> + <field> + <name>QuakeIII</name> + <type>checkbox</type> + <typehint>Quake III</typehint> + <bindstofield>ezshaper->step6->quakeiii</bindstofield> + </field> + <field> + <name>QuakeIV</name> + <type>checkbox</type> + <typehint>Quake IV</typehint> + <bindstofield>ezshaper->step6->quakeiv</bindstofield> + </field> + <field> + <name>StarWarsTOR</name> + <type>checkbox</type> + <typehint>StarWars: The Old Republic - this game uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->starwarstor</bindstofield> + </field> + <field> + <name>TigerWoods2004PS2</name> + <type>checkbox</type> + <typehint>Tiger Woods 2004 for PS2</typehint> + <bindstofield>ezshaper->step6->tigerwoods2004ps2</bindstofield> + </field> + <field> + <name>TribesAscend</name> + <type>checkbox</type> + <typehint>Tribes Ascend</typehint> + <bindstofield>ezshaper->step6->tribesascend</bindstofield> + </field> + <field> + <name>UnrealTournament</name> + <type>checkbox</type> + <typehint>Unreal Tournament Series</typehint> + <bindstofield>ezshaper->step6->unrealtournament</bindstofield> + </field> + <field> + <name>WolfensteinEnemyTerritory</name> + <type>checkbox</type> + <typehint>Wolfenstein Enemy Territory</typehint> + <bindstofield>ezshaper->step6->wolfet</bindstofield> + </field> + <field> + <name>WorldOfWarcraft</name> + <type>checkbox</type> + <typehint>World of Warcraft</typehint> + <bindstofield>ezshaper->step6->wow</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc</includefile> + </step> + <step> + <id>7</id> + <title>pfSense Traffic Shaper Wizard</title> + <disableallfieldsbydefault>true</disableallfieldsbydefault> + <description>Raise or lower other Applications</description> + <fields> + <field> + <name>Enable</name> + <type>checkbox</type> + <typehint>Other networking protocols</typehint> + <description>This will help raise or lower the priority of other protocols higher than most traffic.</description> + <enablefields>AppleRemoteDesktop,MSRDP,PCAnywhere,VNC,AIM,Facetime,GoogleHangouts,ICQ,IRC,Jabber,MSN,TeamSpeak,TeamSpeak3,Ventrilo,PPTP,IPSEC,iTunesRadio,StreamingMP3,RTSP,RTMP,HTTP,IMAP,LotusNotes,POP3,SMTP,BattleNETDownloader,SteamDownloader,APNS,AppleMobileSync,CrashPlan,CVSUP,DNS,GIT,HBCI,ICMP,MySqlServer,NNTP,Slingbox,SMB,SNMP,Subversion</enablefields> + <donotdisable>true</donotdisable> + <bindstofield>ezshaper->step7->enable</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + <field> + <name>Remote Service / Terminal emulation</name> + <type>listtopic</type> + </field> + <field> + <name>AppleRemoteDesktop</name> + <bindstofield>ezshaper->step7->appleremotedesktop</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Apple Remote Desktop</typehint> + </field> + <field> + <name>MSRDP</name> + <type>select</type> + <bindstofield>ezshaper->step7->msrdp</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Microsoft Remote Desktop Protocol</typehint> + </field> + <field> + <name>PCAnywhere</name> + <bindstofield>ezshaper->step7->pcanywhere</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Symantec PC Anywhere</typehint> + </field> + <field> + <name>VNC</name> + <bindstofield>ezshaper->step7->vnc</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Virtual Network Computing</typehint> + </field> + <field> + <name>Messengers</name> + <type>listtopic</type> + </field> + <field> + <name>AIM</name> + <bindstofield>ezshaper->step7->aolinstantmessenger</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>AOL Instant Messenger</typehint> + </field> + <field> + <name>Facetime</name> + <bindstofield>ezshaper->step7->facetime</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Facetime</typehint> + </field> + <field> + <name>ICQ</name> + <bindstofield>ezshaper->step7->icq</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>ICQ</typehint> + </field> + <field> + <name>IRC</name> + <type>select</type> + <bindstofield>ezshaper->step7->irc</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Internet Relay Chat</typehint> + </field> + <field> + <name>Jabber</name> + <type>select</type> + <bindstofield>ezshaper->step7->jabber</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Jabber instant messenger</typehint> + </field> + <field> + <name>GoogleHangouts</name> + <bindstofield>ezshaper->step7->googlehangouts</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Google Hangouts</typehint> + </field> + <field> + <name>MSN</name> + <bindstofield>ezshaper->step7->msnmessenger</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>MSN Messenger</typehint> + </field> + <field> + <name>Teamspeak</name> + <bindstofield>ezshaper->step7->teamspeak</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>TeamSpeak</typehint> + </field> + <field> + <name>Teamspeak3</name> + <bindstofield>ezshaper->step7->teamspeak3</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>TeamSpeak 3</typehint> + </field> + <field> + <name>Ventrilo</name> + <bindstofield>ezshaper->step7->ventrilo</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Ventrilo</typehint> + </field> + <field> + <name>VPN</name> + <type>listtopic</type> + </field> + <field> + <name>PPTP</name> + <type>select</type> + <bindstofield>ezshaper->step7->pptp</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Microsoft Point to Point tunneling protocol</typehint> + </field> + <field> + <name>IPSEC</name> + <type>select</type> + <bindstofield>ezshaper->step7->ipsec</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>IPSEC VPN traffic</typehint> + </field> + <field> + <name>Multimedia/Streaming</name> + <type>listtopic</type> + </field> + <field> + <name>iTunesRadio</name> + <type>select</type> + <bindstofield>ezshaper->step7->itunesradio</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>iTunes Radio - this rule uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + </field> + <field> + <name>StreamingMP3</name> + <type>select</type> + <bindstofield>ezshaper->step7->streamingmp3</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Streaming Media</typehint> + </field> + <field> + <name>RTSP</name> + <bindstofield>ezshaper->step7->rtsp</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>RealTime streaming protocol</typehint> + </field> + <field> + <name>RTMP</name> + <bindstofield>ezshaper->step7->rtmp</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Real-Time Messaging Protocol - Used by video streaming services such as Twitch.tv.</typehint> + </field> + <field> + <name>Web</name> + <type>listtopic</type> + </field> + <field> + <name>HTTP</name> + <type>select</type> + <bindstofield>ezshaper->step7->http</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>HTTP and HTTPS aka Web Traffic</typehint> + </field> + <field> + <name>Mail</name> + <type>listtopic</type> + </field> + <field> + <name>SMTP</name> + <type>select</type> + <bindstofield>ezshaper->step7->smtp</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Mail Protocol</typehint> + </field> + <field> + <name>POP3</name> + <type>select</type> + <bindstofield>ezshaper->step7->pop3</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>POP3 Protocol</typehint> + </field> + <field> + <name>IMAP</name> + <bindstofield>ezshaper->step7->imap</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>IMAP Protocol</typehint> + </field> + <field> + <name>LotusNotes</name> + <bindstofield>ezshaper->step7->lotusnotes</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Lotus Notes</typehint> + </field> + <field> + <name>Game Downloader</name> + <type>listtopic</type> + </field> + <field> + <name>BattleNetDownloader</name> + <type>select</type> + <bindstofield>ezshaper->step7->battlenetdownloader</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Battle.NET Downloader</typehint> + </field> + <field> + <name>SteamDownloader</name> + <type>select</type> + <bindstofield>ezshaper->step7->steamdownloader</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Steam Downloader</typehint> + </field> + <field> + <name>Miscellaneous</name> + <type>listtopic</type> + </field> + <field> + <name>APNS</name> + <type>select</type> + <bindstofield>ezshaper->step7->apns</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Apple Push Notification Service</typehint> + </field> + <field> + <name>AppleMobileSync</name> + <type>select</type> + <bindstofield>ezshaper->step7->applemobilesync</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Apple Mobile Sync</typehint> + </field> + <field> + <name>CrashPlan</name> + <bindstofield>ezshaper->step7->crashplan</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>CrashPlan</typehint> + </field> + <field> + <name>CVSUP</name> + <bindstofield>ezshaper->step7->cvsup</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>CVSUP</typehint> + </field> + <field> + <name>DNS</name> + <type>select</type> + <bindstofield>ezshaper->step7->dns</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Domain Name Services</typehint> + </field> + <field> + <name>Git</name> + <bindstofield>ezshaper->step7->git</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Git Server</typehint> + </field> + <field> + <name>HBCI</name> + <bindstofield>ezshaper->step7->hbci</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>HBCI</typehint> + </field> + <field> + <name>ICMP</name> + <bindstofield>ezshaper->step7->icmp</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>ICMP Protocol</typehint> + </field> + <field> + <name>SMB</name> + <bindstofield>ezshaper->step7->smb</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Microsoft SMB Protocol and friends</typehint> + </field> + <field> + <name>SNMP</name> + <bindstofield>ezshaper->step7->snmp</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Simple Network Management Protocol</typehint> + </field> + <field> + <name>MySQLServer</name> + <bindstofield>ezshaper->step7->mysqlserver</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>MySQL Server</typehint> + </field> + <field> + <name>NNTP</name> + <bindstofield>ezshaper->step7->nntp</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Internet News</typehint> + </field> + <field> + <name>Slingbox</name> + <bindstofield>ezshaper->step7->slingbox</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Slingbox</typehint> + </field> + <field> + <name>Subversion</name> + <bindstofield>ezshaper->step7->subversion</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Subversion Server</typehint> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc</includefile> + </step> + <step> + <id>8</id> + <title>pfSense Traffic Shaper Wizard</title> + <field> + <name>Reload profile notice</name> + <type>listtopic</type> + </field> + <description> After pressing Finish the system will load the new profile.<br/> Please note that this may take a moment.<br/> Also note that the traffic shaper is stateful meaning that only new connections will be shaped.<br/> If this is an issue please reset the state table after loading the profile.<br/></description> + <fields> + <field> + <name>Finish</name> + <type>submit</type> + </field> + </fields> + <stepsubmitphpaction>step8_stepsubmitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc</includefile> + </step> +</pfsensewizard> diff --git a/src/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc b/src/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc new file mode 100644 index 0000000..3f68869 --- /dev/null +++ b/src/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc @@ -0,0 +1,1741 @@ +<?php +/* + traffic_shaper_wizard_multi_all.inc + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2006 Bill Marquette - bill.marquette@gmail.com. + Copyright (C) 2006 Scott Ullrich - sullrich@pfsense.com. + Copyright (C) 2008-2010 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +function step1_stepbeforeformdisplay() { + global $stepid, $savemsg, $pkg; + + $fields =& $pkg['step'][0]['fields']['field']; + + $lans = 0; + $wans = 0; + + $iflisttmp = get_configured_interface_with_descr(); + foreach ($iflisttmp as $if => $ifdesc) { + if (!is_altq_capable(get_real_interface($if))) + continue; + if (interface_has_gateway($if) || interface_has_gatewayv6($if)) + $wans++; + else + $lans++; + } + + foreach ($fields as &$field) { + if ($field['name'] == 'numberofconnections') + $field['value'] = $wans; + else if ($field['name'] == 'numberoflocalinterfaces') + $field['value'] = $lans; + } +} + +function step1_submitphpaction() { + global $stepid, $savemsg; + + if (!isset($_POST['numberofconnections'])) { + $savemsg=gettext("You need to specify the number of connections."); + $stepid--; + return; + } + if (intval($_POST['numberofconnections']) < 1) { + $savemsg=gettext("The number of connections should be greater than 1."); + $stepid--; + return; + } + + if (!isset($_POST['numberoflocalinterfaces'])) { + $savemsg=gettext("You need to specify the number of LAN type interfaces."); + $stepid--; + return; + } + if (intval($_POST['numberoflocalinterfaces']) < 1) { + $savemsg=gettext("The number of LAN type interfaces should be greater than 1."); + $stepid--; + return; + } +} + +function step2_stepbeforeformdisplay() { + global $config, $pkg; + global $stepid, $savemsg; + + $wans = 0; + $lans = 0; + $iflist = array(); + $iflisttmp = get_configured_interface_with_descr(); + foreach ($iflisttmp as $if => $ifdesc) { + if (!is_altq_capable(get_real_interface($if))) + continue; + if (interface_has_gateway($if) || interface_has_gatewayv6($if)) + $wans++; + else + $lans++; + $iflist[$if] = $ifdesc; + } + $numberofconnections = intval($config['ezshaper']['step1']['numberofconnections']); + if ($numberofconnections > $wans) { + $savemsg=gettext("You have less interfaces than number of connections!"); + $stepid--; + return; + } + + $numberoflocalinterfaces = intval($config['ezshaper']['step1']['numberoflocalinterfaces']); + if ($numberoflocalinterfaces > $lans) { + $savemsg=gettext("You have less interfaces than number of connections!"); + $stepid--; + return; + } + $cfgname = "traffic_shaper_wizard_multi_all.xml"; + + $fields =& $pkg['step'][1]['fields']['field']; + + /* + unset($config['ezshaper']['step2']); + $config['ezshaper']['step2'] = array(); + write_config(); + */ + $fields = array(); + + for ($i = 0; $i < $numberoflocalinterfaces; $i++) { + $field = array(); + $interface_friendly = $i+1; + $field['name'] = "Setup connection speed and scheduler information for interface LAN #{$interface_friendly}"; + $field['type'] = "listtopic"; + $fields[] = $field; + + $field = array(); + $field['displayname'] = "Interface & Scheduler"; + $field['name'] = "local{$i}interface"; + $field['type'] = "select"; + $field['options']['option'] = array(); + foreach ($iflist as $ifname => $ifdescr) { + // Skip wan interfaces here + if (interface_has_gateway($ifname) || interface_has_gatewayv6($ifname)) + continue; + $opts = array(); + $opts['displayname'] = $ifdescr; + $opts['name'] = $ifname; + $opts['value'] = $ifname; + $field['options']['option'][] = $opts; + } + $field['combinefieldsbegin'] = "true"; + $field['bindstofield'] = "ezshaper->step2->local{$i}interface"; + $fields[] = $field; + + $field = array(); + $field['combinefieldsend'] = "true"; + $field['dontdisplayname'] = "true"; + $field['dontcombinecells'] = "true"; + $field['name'] = "local{$i}downloadscheduler"; + $field['type'] = "select"; + $field['typehint'] = "Queueing discipline to apply on this local interface."; + $field['options']['option'] = array(); + $opts = array(); + $opts['name'] = "HFSC"; + $opts['value'] = "HFSC"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "CBQ"; + $opts['value'] = "CBQ"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "PRIQ"; + $opts['value'] = "PRIQ"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step2->local{$i}downloadscheduler"; + $fields[] = $field; + } + + for ($i = 0; $i < $numberofconnections; $i++) { + $field = array(); + $interface_friendly = $i+1; + $field['name'] = "Setup connection speed and scheduler information for interface WAN#{$interface_friendly}"; + $field['type'] = "listtopic"; + $fields[] = $field; + + $field = array(); + $field['displayname'] = "Interface & Scheduler"; + $field['name'] = "conn{$i}interface"; + $field['type'] = "select"; + $interface_real = $i+1; + $field['options']['option'] = array(); + foreach ($iflist as $ifname => $ifdescr) { + // Skip lan interfaces here + if (!interface_has_gateway($ifname) && !interface_has_gatewayv6($ifname)) + continue; + $opts = array(); + $opts['displayname'] = $ifdescr; + $opts['name'] = $ifname; + $opts['value'] = $ifname; + $field['options']['option'][] = $opts; + } + $field['bindstofield'] = "ezshaper->step2->conn{$i}interface"; + $field['combinefieldsbegin'] = "true"; + $fields[] = $field; + + $field = array(); + $field['combinefieldsend'] = "true"; + $field['dontdisplayname'] = "true"; + $field['dontcombinecells'] = "true"; + $field['name'] = "conn{$i}uploadscheduler"; + $field['type'] = "select"; + $field['typehint'] = "Queueing discipline to apply on the upload of this connection."; + $field['options']['option'] = array(); + $opts = array(); + $opts['name'] = "HFSC"; + $opts['value'] = "HFSC"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "CBQ"; + $opts['value'] = "CBQ"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "PRIQ"; + $opts['value'] = "PRIQ"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step2->conn{$i}uploadscheduler"; + $fields[] = $field; + + $field = array(); + $field['displayname'] = "Upload"; + $field['name'] = "conn{$i}upload"; + $field['type'] = "input"; + $field['bindstofield'] = "ezshaper->step2->conn{$i}upload"; + $field['combinefieldsbegin'] = "true"; + $fields[] = $field; + + $field = array(); + $field['combinefieldsend'] = "true"; + $field['dontdisplayname'] = "true"; + $field['dontcombinecells'] = "true"; + $field['name'] = "conn{$i}uploadspeed"; + $field['typehint'] = "Upload bandwidth on this connection."; + $field['type'] = "select"; + $field['options']['option'] = array(); + $opts = array(); + $opts['value'] = "Kb"; + $opts['name'] = "Kbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Mb"; + $opts['name'] = "Mbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Gb"; + $opts['name'] = "Gbit/s"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step2->conn{$i}uploadspeed"; + $fields[] = $field; + + $field = array(); + $field['displayname'] = "Download"; + $field['name'] = "conn{$i}download"; + $field['type'] = "input"; + $field['bindstofield'] = "ezshaper->step2->conn{$i}download"; + $field['combinefieldsbegin'] = "true"; + $fields[] = $field; + + $field = array(); + $field['combinefieldsend'] = "true"; + $field['dontdisplayname'] = "true"; + $field['dontcombinecells'] = "true"; + $field['name'] = "conn{$i}downloadspeed"; + $field['typehint'] = "Download bandwidth on this connection."; + $field['type'] = "select"; + $field['options']['option'] = array(); + $opts = array(); + $opts['value'] = "Kb"; + $opts['name'] = "Kbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Mb"; + $opts['name'] = "Mbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Gb"; + $opts['name'] = "Gbit/s"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step2->conn{$i}downloadspeed"; + $fields[] = $field; + } + $field = array(); + $field['name'] = "Next"; + $field['type'] = "submit"; + $fields[] = $field; +} + +function step2_stepsubmitphpaction() { + global $config; + global $stepid, $savemsg; + $sumdownloads = 0; + + /* Input Validation */ + $steps = intval($config['ezshaper']['step1']['numberofconnections']); + $localint = intval($config['ezshaper']['step1']['numberoflocalinterfaces']); + for ($i = 0; $i < $steps; $i++) { + for ($j = $i + 1; $j <= $steps; $j++) { + if ($_POST["conn{$i}interface"] == $_POST["conn{$j}interface"]) { + $savemsg=gettext("You cannot select the same interface for connections {$i} and {$j}."); + $stepid--; + return; + } + if (trim($_POST["conn{$i}uploadscheduler"]) != "PRIQ") { + if (!is_numeric($_POST["conn{$i}upload"])) { + $savemsg = gettext("Upload bandwidth of connection {$i} is not valid."); + $stepid--; + return; + } + if (!is_numeric($_POST["conn{$i}download"])) { + $savemsg = gettext("Download bandwidth of connection {$i} is not valid."); + $stepid--; + return; + } + $upbw = $_POST["conn{$i}upload"]; + $downbw = $_POST["conn{$i}download"]; + if ($upbw < 1 || $downbw < 1) { + $savemsg = gettext("You cannot specify 0 bandwidth!"); + $stepid--; + return; + } + if (intval($upbw) < 128 && $_POST["conn{$i}uploadspeed"] == "Kb" && trim($_POST["conn{$i}uploadscheduler"]) == "CBQ") { + $savemsg=gettext("Uploads smaller than 128Kbit/s is not supported for connection {$i} on CBQ scheduler."); + $stepid--; + return; + } + } + } + for ($j = 0; $j < $localint; $j++) { + if ($_POST["conn{$i}interface"] == $_POST["local{$j}interface"]) { + $savemsg=gettext("You cannot select the same interface for local and outside."); + $stepid--; + return; + } + } + } + for ($i = 0; $i < $localint; $i++) { + for ($j = $i + 1; $j < $localint; $j++) { + if ($_POST["local{$i}interface"] == $_POST["local{$j}interface"]) { + $savemsg=gettext("You cannot select the same interface twice on local interfaces."); + $stepid--; + return; + } + } + } + + /* This is necessary since the wizard expects predefined fields. */ + unset($config['ezshaper']['step2']); + $config['ezshaper']['step2'] = array(); + + for ($i = 0; $i < $localint; $i++) { + $config['ezshaper']['step2']["local{$i}downloadscheduler"] = $_POST["local{$i}downloadscheduler"]; + $config['ezshaper']['step2']["local{$i}interface"] = $_POST["local{$i}interface"]; + } + + for ($i = 0; $i < $steps; $i++) { + $config['ezshaper']['step2']["conn{$i}uploadscheduler"] = $_POST["conn{$i}uploadscheduler"]; + $config['ezshaper']['step2']["conn{$i}upload"] = $_POST["conn{$i}upload"]; + $config['ezshaper']['step2']["conn{$i}uploadspeed"] = $_POST["conn{$i}uploadspeed"]; + $config['ezshaper']['step2']["conn{$i}download"] = $_POST["conn{$i}download"]; + $config['ezshaper']['step2']["conn{$i}downloadspeed"] = $_POST["conn{$i}downloadspeed"]; + $config['ezshaper']['step2']["conn${i}interface"] = $_POST["conn{$i}interface"]; + } +} + +function step3_stepbeforeformdisplay() { + global $config, $pkg; + global $stepid, $savemsg; + + $cfgname = "traffic_shaper_wizard_multi_all.xml"; + + $numberofconnections = intval($config['ezshaper']['step1']['numberofconnections']); + $numberoflocalinterfaces = intval($config['ezshaper']['step1']['numberoflocalinterfaces']); + + $fields =& $pkg['step'][1]['fields']['field']; + + $voipfields =& $pkg['step'][2]['fields']['field']; + + $voipfields = array(); + $enablefields = array(); + + $field = array(); + $field['name'] = "enable"; + $field['type'] = "checkbox"; + $field['typehint'] = "Prioritize Voice over IP traffic."; + $field['bindstofield'] = "ezshaper->step3->enable"; + $field['descritpion'] = "This will raise the priority of VOIP traffic above all other traffic."; + $voipfields[] = $field; + + $field = array(); + $field['name'] = "Next"; + $field['type'] = "submit"; + $voipfields[] = $field; + + $field = array(); + $field['name'] = "VOIP specific settings"; + $field['type'] = "listtopic"; + $voipfields[] = $field; + + $field['name'] = "Provider"; + $enablefields[] = "Provider"; + $field['type'] = "select"; + $field['description'] = "Choose Generic if your provider isn't listed."; + $field['options']['option'] = array(); + $opts = array(); + $opts['name'] = "Generic (lowdelay)"; + $opts['value'] = "Generic"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "VoicePulse"; + $opts['value'] = "VoicePulse"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "Asterisk/Vonage"; + $opts['value'] = "Asterisk"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['name'] = "PanasonicTDA"; + $opts['value'] = "Panasonic"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step3->provider"; + $voipfields[] = $field; + + $field = array(); + $field['displayname'] = "Upstream SIP Server"; + $field['name'] = "upstream_sip_server"; + $enablefields[] = "upstream_sip_server"; + $field['type'] = "inputalias"; + $field['description'] = "(Optional) If this is chosen, the provider field will be overridden. This allows you to provide the IP address of the <strong>remote</strong> PBX or SIP Trunk to prioritize. <br />NOTE: You can also use a Firewall Alias in this location."; + $field['message'] = "IP Address field is non-blank and doesn't look like an IP address."; + $field['bindstofield'] = "ezshaper->step3->address"; + $voipfields[] = $field; + + for ($i = 0; $i < $numberofconnections; $i++) { + $field = array(); + $interface_friendly = $i+1; + $field['name'] = "Connection WAN #{$interface_friendly}"; + $field['type'] = "listtopic"; + $voipfields[] = $field; + + $field = array(); + $field['displayname'] = "Upload"; + $field['name'] = "conn{$i}upload"; + $enablefields[] = "conn{$i}upload"; + $field['type'] = "input"; + $field['bindstofield'] = "ezshaper->step3->conn{$i}upload"; + $field['combinefieldsbegin'] = "true"; + $voipfields[] = $field; + + $field = array(); + $field['combinefieldsend'] = "true"; + $field['dontdisplayname'] = "true"; + $field['dontcombinecells'] = "true"; + $field['name'] = "conn{$i}uploadspeed"; + $enablefields[] = "conn{$i}uploadspeed"; + $field['typehint'] = "Upload bandwidth guarantee for VOIP phone(s) on connection {$i}."; + $field['type'] = "select"; + $field['options']['option'] = array(); + $opts = array(); + $opts['value'] = "Kb"; + $opts['name'] = "Kbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Mb"; + $opts['name'] = "Mbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Gb"; + $opts['name'] = "Gbit/s"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step3->conn{$i}uploadspeed"; + $voipfields[] = $field; + } + + for ($i = 0; $i < $numberoflocalinterfaces; $i++) { + $field = array(); + $interface_friendly = $i+1; + $field['name'] = "Connection LAN #{$interface_friendly}"; + $field['type'] = "listtopic"; + $voipfields[] = $field; + + $field = array(); + $field['displayname'] = "Download"; + $field['name'] = "local{$i}download"; + $enablefields[] = "local{$i}download"; + $field['type'] = "input"; + $field['bindstofield'] = "ezshaper->step3->local{$i}download"; + $field['combinefieldsbegin'] = "true"; + $voipfields[] = $field; + + $field = array(); + $field['combinefieldsend'] = "true"; + $field['dontdisplayname'] = "true"; + $field['dontcombinecells'] = "true"; + $field['name'] = "local{$i}downloadspeed"; + $enablefields[] = "local{$i}downloadspeed"; + $field['typehint'] = "Download bandwidth guarantee for VOIP phone(s) on connections."; + $field['type'] = "select"; + $field['options']['option'] = array(); + $opts = array(); + $opts['value'] = "Kb"; + $opts['name'] = "Kbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Mb"; + $opts['name'] = "Mbit/s"; + $field['options']['option'][] = $opts; + $opts = array(); + $opts['value'] = "Gb"; + $opts['name'] = "Gbit/s"; + $field['options']['option'][] = $opts; + $field['bindstofield'] = "ezshaper->step3->local{$i}downloadspeed"; + $voipfields[] = $field; + } + + $field = array(); + $field['name'] = "Next"; + $field['type'] = "submit"; + $voipfields[] = $field; + $voipfields[0]['enablefields'] = implode(",", $enablefields); +} + +function step3_stepsubmitphpaction() { + global $config; + global $stepid, $savemsg; + + if (!$_POST['enable']) + return; + + if($_POST['upstream_sip_server']) { + if(!is_ipaddroralias($_POST['upstream_sip_server'])) { + /* item is not an ip or alias. error out */ + $savemsg=gettext("Address must be a valid IP address or Firewall Alias. Please correct this value to continue."); + $stepid--; + return; + } + } + + $steps = intval($config['ezshaper']['step1']['numberofconnections']); + for ($i = 0; $i < $steps; $i++) { + if ($config['ezshaper']['step2']["conn{$i}uploadscheduler"] == "PRIQ") + continue; + if (!is_numeric($_POST["conn{$i}upload"])) { + $savemsg = gettext("Upload bandwidth of connection {$i} is not valid."); + $stepid--; + return; + } + if ($_POST["conn{$i}uploadspeed"] == "%") { + if (intval($_POST["conn{$i}upload"]) > 80) { + $savemsg=gettext("You cannot set the VoIP upload bandwidth on connection {$i} higher than 80% of the connection."); + $stepid--; + return; + } + } else { + $factor = wizard_get_bandwidthtype_scale($config['ezshaper']['step2']["conn{$i}uploadspeed"]); + $ifbw = $factor * floatval($config['ezshaper']['step2']["conn{$i}upload"]); + $factor = wizard_get_bandwidthtype_scale($_POST["conn{$i}uploadspeed"]); + $input_bw = $factor * floatval($_POST["conn{$i}upload"]); + if ((0.8 * $ifbw) < $input_bw) { + $savemsg=gettext("You cannot set the VoIP upload bandwidth on connection {$i} higher than 80% of the connection."); + $stepid--; + return; + } + } + } + + $localint = intval($config['ezshaper']['step1']['numberoflocalinterfaces']); + for ($i = 0; $i < $localint; $i++) { + if ($config['ezshaper']['step2']["local{$i}downloadscheduler"] == "PRIQ") + continue; + if (!is_numeric($_POST["local{$i}download"])) { + $savemsg = gettext("Download bandwidth of connection {$i} is not valid."); + $stepid--; + return; + } + if ($_POST["local{$i}downloadspeed"] == "%") { + if (intval($_POST["local{$i}download"]) > 80) { + $savemsg=gettext("You cannot set the VoIP download bandwidth on connection {$i} higher than 80% of the connection."); + $stepid--; + return; + } + } else { + for ($j = 0; $j < $steps; $j++) { + $factor = wizard_get_bandwidthtype_scale($config['ezshaper']['step2']["conn{$j}downloadspeed"]); + $ifbw = $factor * floatval($config['ezshaper']['step2']["conn{$j}download"]); + $factor = wizard_get_bandwidthtype_scale($_POST["local{$i}downloadspeed"]); + $input_bw = $factor * floatval($_POST["local{$i}download"]); + if ((0.8 * $ifbw) < $input_bw) { + $savemsg=gettext("You cannot set the VoIP download bandwidth on connection {$j} higher than 80% of the connection."); + $stepid--; + return; + } + } + } + } + + /* This is necessary since the wizard expects predefined fields. */ + unset($config['ezshaper']['step3']); + $config['ezshaper']['step3'] = array(); + + if (!empty($_POST['upstream_sip_server'])) + $config['ezshaper']['step3']['address'] = $_POST['upstream_sip_server']; + if ($_POST['enable'] == 'on') + $config['ezshaper']['step3']['enable'] = 'on'; + if (!empty($_POST['provider'])) { + $VoIPproviders = array("Generic", "VoicePulse", "Asterisk", "Panasonic"); + if (in_array($_POST['provider'], $VoIPproviders)) { + $config['ezshaper']['step3']['provider'] = $_POST['provider']; + } + } + for ($i = 0; $i < $localint; $i++) { + $config['ezshaper']['step3']["local{$i}download"] = $_POST["local{$i}download"]; + $config['ezshaper']['step3']["local{$i}downloadspeed"] = $_POST["local{$i}downloadspeed"]; + } + + for ($i = 0; $i < $steps; $i++) { + $config['ezshaper']['step3']["conn{$i}upload"] = $_POST["conn{$i}upload"]; + $config['ezshaper']['step3']["conn{$i}uploadspeed"] = $_POST["conn{$i}uploadspeed"]; + } +} + +function step4_stepsubmitphpaction() { + global $config; + global $stepid, $savemsg; + + if ( $_POST['enable'] ) { + if(!$_POST['bandwidth']) { + $savemsg="You need to specify a value for bandwidth!"; + $stepid--; + return; + } + if(!is_numeric($_POST['bandwidth'])) { + $savemsg="The posted value is not a valid bandwidth."; + $stepid--; + return; + } + if ($_POST['bandwidthspeed'] <> "%") { + $savemsg = gettext("Only percentage bandwidth specification is allowed."); + $stepid--; + return; + } + $bw = $_POST['bandwidth']; + if($bw > 15 || $bw < 2) { + $savemsg="Values should be between 2% and 15%!"; + $stepid--; + return; + } + if($_POST['address'] <> "" && !is_ipaddroralias($_POST['address'])) { + /* item is not an ip or alias. error out */ + $savemsg=gettext("Address must be a valid IP address or Firewall Alias. Please correct this value to continue."); + $stepid--; + } + } +} + +function step5_stepsubmitphpaction() { + global $stepid, $savemsg; + if ( $_POST['enable'] ) { + if ($_POST['p2pcatchall']) { + if(!is_numeric($_POST['bandwidth'])) { + $savemsg="Posted value is not a valid bandwidth."; + $stepid--; + } + if ($_POST['bandwidthspeed'] <> "%") { + $savemsg = gettext("Only percentage bandwidth specification is allowed."); + $stepid--; + return; + } + $bw = $_POST['bandwidth']; + if($bw > 15 || $bw < 2) { + $savemsg="Values should be between 2% and 15%!"; + $stepid--; + return; + } + } + } +} + +function step8_stepsubmitphpaction() { + global $g, $config; + + /* save the new configuration */ + apply_all_chosen_items(); + + /* reset rrd queues */ + system("rm -f /var/db/rrd/*queuedrops.rrd"); + system("rm -f /var/db/rrd/*queues.rrd"); + enable_rrd_graphing(); + + /* apply the new configuration to the system */ + filter_configure(); + + /* And we're no longer dirty! */ + clear_subsystem_dirty('shaper'); + + update_filter_reload_status("Initializing"); + header("Location: status_filter_reload.php"); + exit; +} + +function apply_all_chosen_items() { + global $config, $g, $altq_list_queues, $gamesplist, $voiplist, $othersplist, $p2plist; + + require_once("wizardapp.inc"); + + /* + * Wipe previous config. + * Doing it here makes sense since we can wipe the previous config only after + * the user decides to do so, finishing the wizard. + */ + if(isset($config['shaper']['queue'])) + unset($config['shaper']['queue']); + /* XXX: This is redundant, because this should be handled by converter at startup. */ + if(isset($config['shaper']['rule'])) + unset($config['shaper']['rule']); + foreach ($config['filter']['rule'] as $key => $rule) + if ($rule['wizard'] == "yes") + unset($config['filter']['rule'][$key]); + + /* restart the cached config */ + unset($altq_list_queues); + $altq_list_queues = array(); + + $steps = intval($config['ezshaper']['step1']['numberofconnections']); + + $interfacelist = array(); + + for ($i = 0; $i < $steps; $i++) { + + $tmppath = array(); + $altq =& new altq_root_queue(); + + $altq->SetInterface($config['ezshaper']['step2']["conn{$i}interface"]); + $interfacelist[] = $config['ezshaper']['step2']["conn{$i}interface"]; + $altq->SetScheduler($config['ezshaper']['step2']["conn{$i}uploadscheduler"]); + $altq->SetBandwidth(floatval($config['ezshaper']['step2']["conn{$i}upload"])); + $altq->SetBwscale($config['ezshaper']['step2']["conn{$i}uploadspeed"]); + $altq->SetEnabled("on"); + $altq_list_queues[$altq->GetQname()] =& $altq; + array_push($tmppath, $config['ezshaper']['step2']["conn{$i}interface"]); + $altq->SetLink($tmppath); + $altq->wconfig(); + + $sched = $config['ezshaper']['step2']["conn{$i}uploadscheduler"]; + $voipbw =0; + $voipbwunit = "Kb"; + $voip = false; + $penalty = false; + $penaltybw = 0; + $penaltybwunit = "Kb"; + $p2p = false; + $p2pcatchall = false; + $p2pcatchbw = 0; + $p2pcatchbwunit = "%"; + $games = false; + $otherpriority = false; + $remainbw = 0; + $factor = 0; + $upfactor = wizard_get_bandwidthtype_scale($config['ezshaper']['step2']["conn{$i}uploadspeed"]); + $upbw = floatval($config['ezshaper']['step2']["conn{$i}upload"]) * $upfactor; + + if ($config['ezshaper']['step3']['enable']) { + $voip = true; + $voipbw = $config['ezshaper']['step3']["conn{$i}upload"]; + $voipbwunit = $config['ezshaper']['step3']["conn{$i}uploadspeed"]; + if ($voipbwunit == "%") + $factor = $upbw/100; + else + $factor = wizard_get_bandwidthtype_scale($voipbwunit); + $remainbw += $voipbw * $factor; + } + if ($config['ezshaper']['step4']['enable']) { + $penalty = true; + $penaltybw = $config['ezshaper']['step4']['bandwidth']; + $penaltybwunit = $config['ezshaper']['step4']['bandwidthunit']; + if ($penaltybwunit == "%") + $factor = $upbw/100; + else + $factor = wizard_get_bandwidthtype_scale($penaltybwunit); + $remainbw += $penaltybw * $factor; + } else { + $penalty = false; + $penaltybw = 0; + } + if ($config['ezshaper']['step5']['enable']) { + $p2p = true; + if ($config['ezshaper']['step5']['p2pcatchall']) { + $p2pcatchall = true; + $p2pcatchbw = $config['ezshaper']['step5']['bandwidth']; + $p2pcatchbwunit = $config['ezshaper']['step5']['bandwidthunit']; + if ($p2pcatchbwunit == "%") + $factor = $upbw/100; + else + $factor = wizard_get_bandwidthtype_scale($p2pcatchbwunit); + $remainbw += $p2pcatchbw * $factor; + } else { + $p2pcatchall = false; + $p2pcatchbw = 0; + } + } else { + $p2p = false; + $p2pcatchall = false; + $p2pcatchbw = 0; + } + if ($config['ezshaper']['step6']['enable']) { + $games = true; + } else { + $games = false; + } + + if ($config['ezshaper']['step7']['enable']) { + $otherpriority = true; + } else { + $otherpriority = false; + } + + $remainbw = round($remainbw / $upbw * 100, 2); + + if (intval($remainbw) > 0 && intval($remainbw) > 30) { + $savemsg=gettext("Custom Bandwidths are greater than 30%. Please lower them for the wizard to continue."); + header("Location: wizard.php?xml=traffic_shaper_wizard_multi_all.xml&stepid=2&message={$savemsg}"); + exit; + } else { + $remainbw = 100 - $remainbw; + } + + if ($sched != "PRIQ") { + if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qInternet"; + //$tmpcf['priority'] = 6; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + If ($sched == "CBQ") { + $tmpcf['bandwidth'] = floatval($config['ezshaper']['step2']["conn{$i}upload"]); + $tmpcf['bandwidthtype'] = $config['ezshaper']['step2']["conn{$i}uploadspeed"]; + } + else if ($sched == "HFSC") { + $tmpcf['linkshare3'] = + floatval($config['ezshaper']['step2']["conn{$i}upload"]) . $config['ezshaper']['step2']["conn{$i}uploadspeed"]; + $tmpcf['upperlimit3'] = + floatval($config['ezshaper']['step2']["conn{$i}upload"]) . $config['ezshaper']['step2']["conn{$i}uploadspeed"]; + $tmpcf['upperlimit'] = "on"; + + + $tmpcf['linkshare'] = "on"; + $tmpcf['bandwidth'] = floatval($config['ezshaper']['step2']["conn{$i}upload"]); + $tmpcf['bandwidthtype'] = $config['ezshaper']['step2']["conn{$i}uploadspeed"]; + } + array_push($tmppath, "qInternet"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + //array_pop($tmppath); + //echo "qInternet <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + $altq =& $qtmp; + } + + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qACK"; + $tmpcf['priority'] = 6; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + If ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.2; + $tmpcf['bandwidthtype'] = "%"; + } + else if ($sched == "HFSC") { + $lkbw = 0.20 * $remainbw; + $tmpcf['linkshare3'] = "{$lkbw}%"; + $tmpcf['linkshare'] = "on"; + $tmpcf['bandwidth'] = $lkbw; + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, "qACK"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qACK <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + if ($p2pcatchall) + $tmpcf['name'] = "qOthersDefault"; + else + $tmpcf['name'] = "qDefault"; + $tmpcf['priority'] = 3; + $tmpcf['enabled'] = "on"; + if (!$p2pcatchall) + $tmpcf['default'] = "on"; + $tmpcf['ecn'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.1; /* 10% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $tmpcf['bandwidth'] = $remainbw * 0.1; /* 10% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, $tmpcf['name']); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qDefault <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + + if ($p2p) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qP2P"; + $tmpcf['priority'] = 1; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($p2pcatchall) { + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $p2pcatchbw; + $tmpcf['bandwidthtype'] = $p2pcatchbwunit; + } else if ($sched == "HFSC") { + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = "{$p2pcatchbw}{$p2pcatchbwunit}"; + $tmpcf['upperlimit'] = "on"; + $tmpcf['upperlimit3'] = "{$p2pcatchbw}{$p2pcatchbwunit}"; + $tmpcf['bandwidth'] = $p2pcatchbw; + $tmpcf['bandwidthtype'] = $p2pcatchbwunit; + } + $tmpcf['default'] = "on"; + + } else { + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.05; /* 5% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $tmpbw = $remainbw * 0.05; /* 5% bandwidth */ + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = "{$tmpbw}%"; + $tmpcf['upperlimit'] = "on"; + $tmpcf['upperlimit3'] = "{$tmpbw}%"; + $tmpcf['bandwidth'] = $tmpbw; + $tmpcf['bandwidthtype'] = "%"; + } + } + array_push($tmppath, "qP2P"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qP2P <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($voip) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qVoIP"; + $tmpcf['priority'] = 7; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + if ($voipbw > 0) { + $tmpcf['bandwidth'] = $voipbw; + $tmpcf['bandwidthtype'] = $voipbwunit; + } else { + $tmpcf['bandwidth'] = $remainbw * 0.2; /* 20% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } + } else if ($sched == "HFSC") { + if ($voipbw > 0) { + $tmpcf['realtime3'] = "{$voipbw}{$voipbwunit}"; + } else { + $voipbw = $remainbw * 0.20; /* 20% bandwidth */ + $tmpcf['realtime3'] = "{$voipbw}%"; + } + $tmpcf['realtime'] = "on"; + $tmpcf['bandwidth'] = 32; + $tmpcf['bandwidthtype'] = "Kb"; + } + array_push($tmppath, "qVoIP"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qVoIP <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($games) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qGames"; + $tmpcf['priority'] = 5; + $tmpcf['enabled'] = "on"; + $tmpcf['ecn'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.2; /* 20% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $gamesbw = $remainbw * 0.2; /* 20% bandwidth */ + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = "{$gamesbw}%"; + $tmpcf['bandwidth'] = "{$gamesbw}"; + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, "qGames"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qGames <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($otherpriority) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qOthersHigh"; + $tmpcf['priority'] = 4; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.1; /* 10% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $tmpcf['linkshare'] = "on"; + $otherbw = $remainbw * 0.1; /* 10% bandwidth */ + $tmpcf['linkshare3'] = "{$otherbw}%"; + $tmpcf['bandwidth'] = $otherbw; + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, "qOthersHigh"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qHigh <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + + + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qOthersLow"; + $tmpcf['priority'] = 2; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + if ($penalty) { + $tmpcf['bandwidthtype'] = $penaltybwunit; + $tmpcf['bandwidth'] = $penaltybw; + } else { + $tmpcf['bandwidth'] = $remainbw * 0.05; /* 5% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } + } else if ($sched == "HFSC") { + if ($penalty) { + $tmpcf['linkshare3'] = "{$penaltybw}{$penaltybwunit}"; + $tmpcf['bandwidth'] = $penaltybw; + $tmpcf['bandwidthtype'] = $penaltybwunit; + } else { + $lsbw = $remainbw * 0.05; + $tmpcf['linkshare3'] = "{$lsbw}%"; /* 5% bandwidth */ + $tmpcf['bandwidth'] = $lsbw; + $tmpcf['bandwidthtype'] = "%"; + } + $tmpcf['linkshare'] = "on"; + } + array_push($tmppath, "qOthersLow"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qLow <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + array_pop($tmppath); + } + + /* LAN bandwidth ----------------------------------------------------------------------------------------- */ + $localint = intval($config['ezshaper']['step1']['numberoflocalinterfaces']); + $lanbw = 0; + for ($i = 0; $i < $steps; $i++) { + $down = wizard_get_bandwidthtype_scale($config['ezshaper']['step2']["conn{$i}downloadspeed"]); + $input_bw = floatval($config['ezshaper']['step2']["conn{$i}download"]) * $down; + $lanbw += $input_bw; + } + + for ($i = 0; $i < $localint; $i++) { + + $tmppath = array(); + $altq =& new altq_root_queue(); + + $altq->SetInterface($config['ezshaper']['step2']["local{$i}interface"]); + $altq->SetScheduler($config['ezshaper']['step2']["local{$i}downloadscheduler"]); + //$altq->SetBandwidth($lanbw/1000); + //$altq->SetBwscale("Kb"); + $altq->SetEnabled("on"); + $altq_list_queues[$altq->GetQname()] =& $altq; + array_push($tmppath, $config['ezshaper']['step2']["local{$i}interface"]); + $altq->SetLink($tmppath); + //var_dump($input_errors); + $altq->wconfig(); + + $sched = $config['ezshaper']['step2']["local{$i}downloadscheduler"]; + $voipbw =0; + $voipbwunit = "%"; + $voip = false; + $penalty = false; + $penaltybw = 0; + $penaltybwunit = "%"; + $p2p = false; + $p2pcatchall = false; + $p2pcatchbw = 0; + $games = false; + $otherpriority = false; + $remainbw = 0; + + + if ($config['ezshaper']['step3']['enable']) { + $voip = true; + $voipbw = $config['ezshaper']['step3']["local{$i}download"]; + $voipbwunit = $config['ezshaper']['step3']["local{$i}downloadspeed"]; + if ($sched != HFSC) { + if ($voipbwunit == "%") + $factor = $lanbw/100; + else + $factor = wizard_get_bandwidthtype_scale($voipbwunit); + $remainbw += floatval($voipbw) * $factor; + } else + $remainbw += 32000; /* 32Kbit/s reserved for HFSC link sharing */ + } + if ($config['ezshaper']['step4']['enable']) { + $penalty = true; + $penaltybw = $config['ezshaper']['step4']['bandwidth']; + $penaltybwunit = $config['ezshaper']['step4']['bandwidthunit']; + if ($penaltybwunit == "%") + $factor = $lanbw/100; + else + $factor = wizard_get_bandwidthtype_scale($penaltybwunit); + $remainbw += floatval($penaltybw) * $factor; + } else { + $penalty = false; + $penaltybw = 0; + } + if ($config['ezshaper']['step5']['enable']) { + $p2p = true; + if ($config['ezshaper']['step5']['p2pcatchall']) { + $p2pcatchall = true; + $p2pcatchbw = $config['ezshaper']['step5']['bandwidth']; + $p2pcatchbwunit = $config['ezshaper']['step5']['bandwidthunit']; + if ($p2pcatchbwunit == "%") + $factor = $upbw/100; + else + $factor = wizard_get_bandwidthtype_scale($p2pcatchbwunit); + $remainbw += floatval($p2pcatchbw) * $factor; + } else { + $p2pcatchall = false; + $p2pcatchbw = 0; + } + } else { + $p2p = false; + $p2pcatchall = false; + $p2pcatchbw = 0; + } + if ($config['ezshaper']['step6']['enable']) { + $games = true; + } else { + $games = false; + } + + if ($config['ezshaper']['step7']['enable']) { + $otherpriority = true; + } else { + $otherpriority = false; + } + $remainbw = round($remainbw / $lanbw * 100, 2); + + if (intval($remainbw) > 0 && intval($remainbw) > 40) { + $savemsg=gettext("Custom Bandwidths are greater than 40%. Please lower them for the wizard to continue."); + header("Location: wizard.php?xml=traffic_shaper_wizard_multi_all.xml&stepid=2&message={$savemsg}"); + exit; + } else { + $remainbw = 100 - $remainbw; + } + + if (!$p2pcatchall) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qLink"; + $tmpcf['priority'] = 2; + $tmpcf['enabled'] = "on"; + $tmpcf['default'] = "on"; + $tmpcf['qlimit'] = 500; + $tmpcf['ecn'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = 20; /* 20% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $tmpcf['bandwidth'] = 20; /* 20% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, $tmpcf['name']); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qDefault <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($sched != "PRIQ") { + if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qInternet"; + //$tmpcf['priority'] = 6; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + If ($sched == "CBQ") { + $tmpcf['bandwidth'] = $lanbw/1000; + $tmpcf['bandwidthtype'] = "Kb"; + } + else if ($sched == "HFSC") { + $tmpcf['linkshare3'] = $lanbw/1000 . "Kb"; + $tmpcf['upperlimit3'] = $lanbw/1000 . "Kb"; + $tmpcf['upperlimit'] = "on"; + $tmpcf['linkshare'] = "on"; + $tmpcf['bandwidth'] = $lanbw/1000; + $tmpcf['bandwidthtype'] = "Kb"; + } + array_push($tmppath, "qInternet"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + //array_pop($tmppath); + //echo "qInternet <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + $altq =& $qtmp; + } + + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qACK"; + $tmpcf['priority'] = 6; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + If ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.2; + $tmpcf['bandwidthtype'] = "%"; + } + else if ($sched == "HFSC") { + $lkbw = 0.20 * $remainbw; + $tmpcf['linkshare3'] = "{$lkbw}%"; + $tmpcf['linkshare'] = "on"; + $tmpcf['bandwidth'] = $lkbw; + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, "qACK"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qACK <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + + if ($p2p) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qP2P"; + $tmpcf['priority'] = 1; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($p2pcatchall) { + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $p2pcatchbw; + $tmpcf['bandwidthtype'] = $p2pcatchbwunit; + } else if ($sched == "HFSC") { + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = "{$p2pcatchbw}{$p2pcatchbwunit}"; + $tmpcf['upperlimit'] = "on"; + $tmpcf['upperlimit3'] = "{$p2pcatchbw}{$p2pcatchbwunit}"; + $tmpcf['bandwidth'] = $p2pcatchbw; + $tmpcf['bandwidthtype'] = $p2pcatchbwunit; + } + $tmpcf['default'] = "on"; + $tmpcf['qlimit'] = 500; + } else { + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.05; /* 5% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $tmpbw = $remainbw * 0.05; /* 5% bandwidth */ + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = "{$tmpbw}%"; + $tmpcf['upperlimit'] = "on"; + $tmpcf['upperlimit3'] = "{$tmpbw}%"; + $tmpcf['bandwidth'] = $tmpbw; + $tmpcf['bandwidthtype'] = "%"; + } + } + array_push($tmppath, "qP2P"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qP2P <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($voip) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qVoIP"; + $tmpcf['priority'] = 7; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + if ($voipbw > 0) { + $tmpcf['bandwidth'] = $voipbw; + $tmpcf['bandwidthtype'] = $voipbwunit; + } else { + $tmpcf['bandwidth'] = $remainbw * 0.2; /* 20% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } + } else if ($sched == "HFSC") { + if ($voipbw > 0) { + $tmpcf['realtime3'] = "{$voipbw}{$voipbwunit}"; + } else { + $voipbw = $remainbw * 0.20; /* 20% bandwidth */ + $tmpcf['realtime3'] = "{$voipbw}%"; + } + $tmpcf['realtime'] = "on"; + $tmpcf['bandwidth'] = 32; + $tmpcf['bandwidthtype'] = "Kb"; + } + array_push($tmppath, "qVoIP"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qVoIP <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($games) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qGames"; + $tmpcf['priority'] = 5; + $tmpcf['enabled'] = "on"; + $tmpcf['ecn'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.2; /* 20% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $gamesbw = $remainbw * 0.2; /* 20% bandwidth */ + $tmpcf['linkshare'] = "on"; + $tmpcf['linkshare3'] = "{$gamesbw}%"; + $tmpcf['bandwidth'] = "{$gamesbw}"; + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, "qGames"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qGames <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + + if ($otherpriority) { + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qOthersHigh"; + $tmpcf['priority'] = 4; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + $tmpcf['bandwidth'] = $remainbw * 0.1; /* 10% bandwidth */ + $tmpcf['bandwidthtype'] = "%"; + } else if ($sched == "HFSC") { + $tmpcf['linkshare'] = "on"; + $otherbw = $remainbw * 0.1; /* 10% bandwidth */ + $tmpcf['linkshare3'] = "{$otherbw}%"; + $tmpcf['bandwidth'] = $otherbw; + $tmpcf['bandwidthtype'] = "%"; + } + array_push($tmppath, "qOthersHigh"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qHigh <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + + + if ($sched == "PRIQ") + $q =& new priq_queue(); + else if ($sched == "CBQ") + $q =& new cbq_queue(); + else if ($sched == "HFSC") + $q =& new hfsc_queue(); + $tmpcf = array(); + $tmpcf['name'] = "qOthersLow"; + $tmpcf['priority'] = 3; + $tmpcf['ecn'] = "on"; + $tmpcf['enabled'] = "on"; + if ($sched == "CBQ") { + $tmpcf['borrow'] = "on"; + if ($penalty) { + $tmpcf['bandwidth'] = $penaltybw; + $tmpcf['bandwidthtype'] = $penaltybwunit; + } else { + $tmpcf['bandwidthtype'] = "%"; + $tmpcf['bandwidth'] = $remainbw * 0.05; /* 5% bandwidth */ + } + } else if ($sched == "HFSC") { + if ($penalty) { + $tmpcf['linkshare3'] = "{$penaltybw}{$penaltybwunit}"; + $tmpcf['bandwidth'] = $penaltybw; + $tmpcf['bandwidthtype'] = $penaltybwunit; + } else { + $lsbw = $remainbw * 0.05; + $tmpcf['linkshare3'] = "{$lsbw}%"; /* 5% bandwidth */ + $tmpcf['bandwidth'] = $lsbw; + $tmpcf['bandwidthtype'] = "%"; + } + $tmpcf['linkshare'] = "on"; + } + array_push($tmppath, "qOthersLow"); + $qtmp =& $altq->add_queue($q, $tmpcf, $tmppath, $input_errors); + array_pop($tmppath); + //echo "qLow <br />"; + //var_dump($input_errors); + $qtmp->wconfig(); + } + array_pop($tmppath); + } + +/* End LAN bandwidth ------------------------------------------------------------------------------------- */ + + + + if (!is_array($config['filter']['rule'])) + $config['filter']['rule'] = array(); + + $interfacelist = implode(",", $interfacelist); + + /* Rules */ + if ($penalty) { + if( is_ipaddr($config['ezshaper']['step4']['address']) || is_alias($config['ezshaper']['step4']['address'])) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['descr'] = gettext("Penalty Box"); + $rule['defaultqueue'] = "qOthersLow"; + $rule['source']['address'] = $config['ezshaper']['step4']['address']; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + + } + } + + /* If user specifies an IP, we don't bother with providers */ + if ($voip) { + if( is_ipaddr($config['ezshaper']['step3']['address']) || is_alias($config['ezshaper']['step3']['address'])) { + /* create VOIP rules */ + $rule = array(); + $rule['type'] = "match"; + //$rule['interface'] = $interfacelist; + $rule['descr'] = gettext("Connections From Upstream SIP Server"); + $rule['protocol'] = "udp"; + $rule['defaultqueue'] = "qVoIP"; + $rule['source']['address'] = $config['ezshaper']['step3']['address']; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + + $rule = array(); + $rule['type'] = "match"; + //$rule['interface'] = $interfacelist; + $rule['descr'] = gettext("Connections To Upstream SIP Server"); + $rule['protocol'] = "udp"; + $rule['defaultqueue'] = "qVoIP"; + $rule['source']['any'] = TRUE; + $rule['destination']['address'] = $config['ezshaper']['step3']['address']; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + + } elseif( $config['ezshaper']['step3']['provider'] == "Generic" ) { + /* create VOIP rules */ + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['descr'] = "DiffServ/Lowdelay/Upload"; + $rule['protocol'] = "udp"; + $rule['source']['any'] = TRUE; + $rule['defaultqueue'] = "qVoIP"; + $rule['destination']['any'] = TRUE; + $rule['iptos'] = "lowdelay"; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + + } else { + /* loop through voiplist[] */ + foreach ($voiplist[$config['ezshaper']['step3']['provider']] as $voip) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['defaultqueue'] = 'qVoIP'; + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['descr'] = "m_voip {$voip[0]} outbound"; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['destination']['port'] = $voip[2]."-".$voip[3]; + if($voip[1] != '') + $rule['protocol'] = $voip[1]; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + } + } + } + + /* loop through p2plist[] */ + if ($p2p) { + foreach($config['ezshaper']['step5'] as $key => $val) { + if (!is_array($p2plist[$key])) + continue; + foreach ($p2plist[$key] as $p2pclient) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['defaultqueue'] = 'qP2P'; + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['descr'] = "m_P2P {$p2pclient[0]} outbound"; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['destination']['port'] = $p2pclient[2]."-".$p2pclient[3]; + if($p2pclient[1] != '') + $rule['protocol'] = $p2pclient[1]; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + } + } + } + + /* loop through gamesplist[] */ + if ($games) { + foreach($config['ezshaper']['step6'] as $key => $val) { + if (!is_array($gamesplist[$key])) + continue; + foreach ($gamesplist[$key] as $Gameclient) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['defaultqueue'] = 'qGames'; + if ($Gameclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['descr'] = "m_Game {$Gameclient[0]} outbound"; + $rule['destination']['port'] = $Gameclient[2]."-".$Gameclient[3]; + if($Gameclient[1] != '') + $rule['protocol'] = $Gameclient[1]; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + } + } + } + + /* loop through othersplist[] */ + if ($otherpriority) { + foreach($config['ezshaper']['step7'] as $key => $val) { + if (!is_array($othersplist[$key])) + continue; + foreach ($othersplist[$key] as $otherclient) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + switch ($val) { + case "H": + $rule['defaultqueue'] = 'qOthersHigh'; /* posted value H or L */ + if ($otherclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + $loop = 0; + break; + case "L": + $rule['defaultqueue'] = 'qOthersLow'; /* posted value H or L */ + if ($otherclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + $loop = 0; + break; + case "D": + if ($p2pcatchall) { + $loop = 0; + $rule['defaultqueue'] = 'qOthersDefault'; + if ($otherclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + } else + $loop = 1; /* It automatically goes to default queue */ + break; + default: + $loop = 1; + } + if (!$loop) { + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['descr'] = "m_Other {$otherclient[0]} outbound"; + + if($otherclient[2] or $otherclient[3]) { + $rule['destination']['port'] = $otherclient[2]."-".$otherclient[3]; + } + if($otherclient[1] != '') + $rule['protocol'] = $otherclient[1]; + $rule['created'] = make_config_revision_entry(null, gettext("Traffic Shaper Wizard")); + $config['filter']['rule'][] = $rule; + } + } + } + } + write_config(); +} + +function wizard_get_bandwidthtype_scale($type = "b") { + switch ($type) { + case "Gb": + $factor = 1024 * 1024 * 1024; + break; + case "Mb": + $factor = 1024 * 1024; + break; + case "Kb": + $factor = 1024; + break; + case "b": + default: + $factor = 1; + break; + } + return intval($factor); +} + +?> diff --git a/src/usr/local/www/wizards/traffic_shaper_wizard_multi_all.xml b/src/usr/local/www/wizards/traffic_shaper_wizard_multi_all.xml new file mode 100755 index 0000000..e94256b --- /dev/null +++ b/src/usr/local/www/wizards/traffic_shaper_wizard_multi_all.xml @@ -0,0 +1,1657 @@ +<?xml version="1.0"?> +<pfsensewizard> + <copyright><![CDATA[ + /* + traffic_shaper_wizard_multi_all.xml + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2005 Bill Marquette - bill.marquette@gmail.com. + Copyright (C) 2008-2010 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */]]></copyright> + + <totalsteps>9</totalsteps> + <step> + <id>1</id> + <title>pfSense Traffic Shaper Wizard</title> + <disableheader>true</disableheader> + <description>This wizard will guide you through setting up the pfSense traffic shaper. + Please be aware that Custom Bandwidths should not exceed 30% of the interface/link bandwidth. Keep this in mind during the wizard. + </description> + <fields> + <field> + <type>listtopic</type> + <name>Traffic shaper Wizard</name> + </field> + <field> + <displayname>Enter number of WAN type connections</displayname> + <name>numberofconnections</name> + <type>input</type> + <validate>^[0-9]+$</validate> + <description>Number of connections you have</description> + <bindstofield>ezshaper->step1->numberofconnections</bindstofield> + </field> + <field> + <displayname>Enter number of LAN type interfaces</displayname> + <name>numberoflocalinterfaces</name> + <type>input</type> + <validate>^[0-9]+$</validate> + <description>Number of local interfaces you have</description> + <bindstofield>ezshaper->step1->numberoflocalinterfaces</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepbeforeformdisplay>step1_stepbeforeformdisplay();</stepbeforeformdisplay> + <stepsubmitphpaction>step1_submitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc</includefile> + </step> + <step> + <id>2</id> + <title>pfSense Traffic Shaper Wizard</title> + <description>Shaper configuration</description> + <javascriptafterformdisplay/> + <stepbeforeformdisplay>step2_stepbeforeformdisplay();</stepbeforeformdisplay> + <stepsubmitphpaction>step2_stepsubmitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc</includefile> + <fields> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + </step> + <step> + <id>3</id> + <title>pfSense Traffic Shaper Wizard</title> + <description>Voice over IP</description> + <fields> + <field> + <name>Enable</name> + <type>checkbox</type> + <typehint>Prioritize Voice over IP traffic</typehint> + <description>This will raise the priority of VOIP traffic above all other traffic.</description> + <bindstofield>ezshaper->step3->enable</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + <field> + <name>VOIP specific settings</name> + <type>listtopic</type> + </field> + <field> + <name>Provider</name> + <type>select</type> + <description>Choose Generic if your provider isn't listed.</description> + <bindstofield>ezshaper->step3->provider</bindstofield> + <options> + <option> + <name>Generic (lowdelay)</name> + <value>Generic</value> + </option> + <option> + <name>VoicePulse</name> + <value>VoicePulse</value> + </option> + <option> + <name>Asterisk/Vonage</name> + <value>Asterisk</value> + </option> + <option> + <name>PanasonicTDA</name> + <value>Panasonic</value> + </option> + </options> + </field> + <field> + <displayname>Upstream SIP Server</displayname> + <name>upstream_sip_server</name> + <type>inputalias</type> + <description>(Optional) If this is chosen, the provider field will be overridden. This allows you to provide the IP address of the <strong>remote</strong> PBX or SIP Trunk to prioritize. <br />NOTE: You can also use a Firewall Alias in this location.</description> + <bindstofield>ezshaper->step3->address</bindstofield> + <message>IP Address field is non-blank and doesn't look like an IP address.</message> + </field> + <field> + <name>Bandwidth</name> + <type>input</type> + <validate>^[0-9]*$</validate> + <typehint>Total bandwidth in percentage(%)(should be between 5 and 40) guarantee for VOIP traffic.</typehint> + <bindstofield>ezshaper->step3->bandwidth</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepbeforeformdisplay>step3_stepbeforeformdisplay();</stepbeforeformdisplay> + <stepsubmitphpaction>step3_stepsubmitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc</includefile> + </step> + <step> + <id>4</id> + <title>pfSense Traffic Shaper Wizard</title> + <description>Penalty Box</description> + <disableallfieldsbydefault>true</disableallfieldsbydefault> + <fields> + <field> + <donotdisable>true</donotdisable> + <name>Enable</name> + <type>checkbox</type> + <typehint>Penalize IP or Alias</typehint> + <description>This will lower the priority of traffic from this IP or alias.</description> + <enablefields>Address,Bandwidth,BandwidthSpeed</enablefields> + <bindstofield>ezshaper->step4->enable</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + <field> + <name>PenaltyBox specific settings</name> + <type>listtopic</type> + </field> + <field> + <name>Address</name> + <type>inputalias</type> + <description>This allows you to just provide the IP address of the computer(s) to penalize. NOTE: You can also use a Firewall Alias in this location.</description> + <bindstofield>ezshaper->step4->address</bindstofield> + <message>IP Address field is non-blank and doesn't look like an IP address.</message> + </field> + <field> + <name>Bandwidth</name> + <type>input</type> + <validate>^[0-9]*$</validate> + <message>Speed must be numerical.</message> + <bindstofield>ezshaper->step4->bandwidth</bindstofield> + <combinefieldsbegin>true</combinefieldsbegin> + </field> + <field> + <combinefieldsend>true</combinefieldsend> + <dontdisplayname>true</dontdisplayname> + <dontcombinecells>true</dontcombinecells> + <donotdisable>true</donotdisable> + <name>BandwidthSpeed</name> + <description>The limit you want to apply.</description> + <type>select</type> + <options> + <option> + <name>%</name> + <value>%</value> + </option> + <option> + <name>bit/s</name> + <value>b</value> + </option> + <option> + <name>Kilobit/s</name> + <value>Kb</value> + </option> + <option> + <name>Megabit/s</name> + <value>Mb</value> + </option> + <option> + <name>Gigabit/s</name> + <value>Gb</value> + </option> + </options> + <bindstofield>ezshaper->step4->bandwidthunit</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepsubmitphpaction>step4_stepsubmitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc</includefile> + </step> + <step> + <id>5</id> + <title>pfSense Traffic Shaper Wizard</title> + <description>Peer to Peer networking</description> + <disableallfieldsbydefault>true</disableallfieldsbydefault> + <fields> + <field> + <donotdisable>true</donotdisable> + <name>Enable</name> + <type>checkbox</type> + <typehint>Lower priority of Peer-to-Peer traffic</typehint> + <description>This will lower the priority of P2P traffic below all other traffic. Please check the items that you would like to prioritize lower than normal traffic.</description> + <enablefields>p2pCatchAll,Bandwidth,BandwidthSpeed,Aimster,BitTorrent,BuddyShare,CuteMX,DCplusplus,dcc,DirectConnect,DirectFileExpress,EDonkey2000,FastTrack,Gnutella,grouper,hotComm,HotlineConnect,iMesh,Napster,OpenNap,Scour,Shareaza,SongSpy,WinMX</enablefields> + <bindstofield>ezshaper->step5->enable</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + <field> + <name>p2p Catch all</name> + <type>listtopic</type> + </field> + <field> + <name>p2pCatchAll</name> + <type>checkbox</type> + <typehint>When enabled, all uncategorized traffic is fed to the p2p queue.</typehint> + <bindstofield>ezshaper->step5->p2pcatchall</bindstofield> + </field> + <field> + <name>Bandwidth</name> + <type>input</type> + <validate>^[0-9]*$</validate> + <message>Speed must be numerical.</message> + <bindstofield>ezshaper->step5->bandwidth</bindstofield> + <combinefieldsbegin>true</combinefieldsbegin> + </field> + <field> + <combinefieldsend>true</combinefieldsend> + <dontdisplayname>true</dontdisplayname> + <dontcombinecells>true</dontcombinecells> + <donotdisable>true</donotdisable> + <name>BandwidthSpeed</name> + <description>The limit you want to apply.</description> + <type>select</type> + <options> + <option> + <name>%</name> + <value>%</value> + </option> + <option> + <name>bit/s</name> + <value>b</value> + </option> + <option> + <name>Kilobit/s</name> + <value>Kb</value> + </option> + <option> + <name>Megabit/s</name> + <value>Mb</value> + </option> + <option> + <name>Gigabit/s</name> + <value>Gb</value> + </option> + </options> + <bindstofield>ezshaper->step5->bandwidthunit</bindstofield> + </field> + <field> + <name>Enable/Disable specific P2P protocols</name> + <type>listtopic</type> + </field> + <field> + <name>Aimster</name> + <type>checkbox</type> + <typehint>Aimster and other P2P using the Aimster protocol and ports</typehint> + <bindstofield>ezshaper->step5->aimster</bindstofield> + </field> + <field> + <name>BitTorrent</name> + <type>checkbox</type> + <typehint>Bittorrent and other P2P using the Torrent protocol and ports</typehint> + <bindstofield>ezshaper->step5->bittorrent</bindstofield> + </field> + <field> + <name>BuddyShare</name> + <type>checkbox</type> + <typehint>BuddyShare and other P2P using the BuddyShare protocol and ports</typehint> + <bindstofield>ezshaper->step5->buddyshare</bindstofield> + </field> + <field> + <name>CuteMX</name> + <type>checkbox</type> + <typehint>CuteMX and other P2P using the CuteMX protocol and ports</typehint> + <bindstofield>ezshaper->step5->cutemx</bindstofield> + </field> + <field> + <name>DCplusplus</name> + <type>checkbox</type> + <typehint>DC++ and other P2P using the DC++ protocol and ports</typehint> + <bindstofield>ezshaper->step5->dcplusplus</bindstofield> + </field> + <field> + <name>DCC</name> + <type>checkbox</type> + <typehint>irc DCC file transfers</typehint> + <bindstofield>ezshaper->step5->dcc</bindstofield> + </field> + <field> + <name>DirectConnect</name> + <type>checkbox</type> + <typehint>DirectConnect and other P2P using the DirectConnect protocol and ports</typehint> + <bindstofield>ezshaper->step5->directconnect</bindstofield> + </field> + <field> + <name>DirectFileExpress</name> + <type>checkbox</type> + <typehint>DirectFileExpress and other P2P using the DirectFileExpress protocol and ports</typehint> + <bindstofield>ezshaper->step5->directfileexpress</bindstofield> + </field> + <field> + <name>eDonkey2000</name> + <type>checkbox</type> + <typehint>eDonkey and other P2P using the eDonkey protocol and ports</typehint> + <bindstofield>ezshaper->step5->edonkey2000</bindstofield> + </field> + <field> + <name>FastTrack</name> + <type>checkbox</type> + <typehint>FastTrack and other P2P using the FastTrack protocol and ports</typehint> + <bindstofield>ezshaper->step5->fasttrack</bindstofield> + </field> + <field> + <name>Gnutella</name> + <type>checkbox</type> + <typehint>Gnutella and other P2P using the Gnutella protocol and ports</typehint> + <bindstofield>ezshaper->step5->gnutella</bindstofield> + </field> + <field> + <name>grouper</name> + <type>checkbox</type> + <typehint>grouper and other P2P using the grouper protocol and ports</typehint> + <bindstofield>ezshaper->step5->grouper</bindstofield> + </field> + <field> + <name>hotComm</name> + <type>checkbox</type> + <typehint>hotComm and other P2P using the hotComm protocol and ports</typehint> + <bindstofield>ezshaper->step5->hotcomm</bindstofield> + </field> + <field> + <name>HotlineConnect</name> + <type>checkbox</type> + <typehint>HotlineConnect and other P2P using the HotlineConnect protocol and ports</typehint> + <bindstofield>ezshaper->step5->hotlineconnect</bindstofield> + </field> + <field> + <name>iMesh</name> + <type>checkbox</type> + <typehint>iMesh and other P2P using the iMesh protocol and ports</typehint> + <bindstofield>ezshaper->step5->imesh</bindstofield> + </field> + <field> + <name>Napster</name> + <type>checkbox</type> + <typehint>Napster and other P2P using the Napster protocol and ports</typehint> + <bindstofield>ezshaper->step5->napster</bindstofield> + </field> + <field> + <name>OpenNap</name> + <type>checkbox</type> + <typehint>OpenNap and other P2P using the OpenNap protocol and ports</typehint> + <bindstofield>ezshaper->step5->opennap</bindstofield> + </field> + <field> + <name>Scour</name> + <type>checkbox</type> + <typehint>Scour and other P2P using the Scour protocol and ports</typehint> + <bindstofield>ezshaper->step5->scour</bindstofield> + </field> + <field> + <name>Shareaza</name> + <type>checkbox</type> + <typehint>Shareaza and other P2P using the Shareaza protocol and ports</typehint> + <bindstofield>ezshaper->step5->shareaza</bindstofield> + </field> + <field> + <name>SongSpy</name> + <type>checkbox</type> + <typehint>SongSpy and other P2P using the SongSpy protocol and ports</typehint> + <bindstofield>ezshaper->step5->songspy</bindstofield> + </field> + <field> + <name>WinMX</name> + <type>checkbox</type> + <typehint>WinMX and other P2P using the WinMX protocol and ports</typehint> + <bindstofield>ezshaper->step5->winmx</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <stepsubmitphpaction>step5_stepsubmitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc</includefile> + </step> + <step> + <id>6</id> + <title>pfSense Traffic Shaper Wizard</title> + <disableallfieldsbydefault>true</disableallfieldsbydefault> + <description>Network Games</description> + <fields> + <field> + <name>Enable</name> + <type>checkbox</type> + <typehint>Prioritize network gaming traffic</typehint> + <description>This will raise the priority of gaming traffic to higher than most traffic.</description> + <enablefields>BattleNET,EAOrigin,GameForWindowsLive,PlayStationConsoles,Steam,WiiConsoles,XboxConsoles,ARMA2,ARMA3,Battlefield2,Battlefield3,BattlefieldBC2,Borderlands,CallOfDuty,Counterstrike,Crysis2,Crysis3,DeltaForce,DeadSpace2,DeadSpace3,Dirt3,DOOM3,DragonAge2,EmpireEarth,EveOnline,Everquest,Everquest2,FarCry,FarCry2,FarCry3,GunZOnline,HalfLife,LeagueofLegends,Lineage2,MassEffect3,MechwarriorOnline,Minecraft,OperationFlashpointDR,PlanetSide,PlanetSide2,QuakeIII,QuakeIV,StarWarsTOR,TigerWoods2004PS2,TribesAscend,UnrealTournament,WolfensteinEnemyTerritory,WorldOfWarcraft</enablefields> + <donotdisable>true</donotdisable> + <bindstofield>ezshaper->step6->enable</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + <field> + <name>Enable/Disable specific game consoles and services</name> + <type>listtopic</type> + </field> + <field> + <name>BattleNET</name> + <type>checkbox</type> + <typehint>Battle.net - Virtually every game from Blizzard publishing should match this. This includes the following game series: Starcraft, Diablo, Warcraft. Guild Wars also uses this port.</typehint> + <bindstofield>ezshaper->step6->battlenet</bindstofield> + </field> + <field> + <name>EAOrigin</name> + <type>checkbox</type> + <typehint>EA Origin Client - Some PC games by EA use this.</typehint> + <bindstofield>ezshaper->step6->eaorigin</bindstofield> + </field> + <field> + <name>GameForWindowsLive</name> + <type>checkbox</type> + <typehint>Games for Windows Live</typehint> + <bindstofield>ezshaper->step6->gamesforwindowslive</bindstofield> + </field> + <field> + <name>PlayStationConsoles</name> + <type>checkbox</type> + <typehint>PlayStation Consoles - This should cover all ports required for the Playstation 4, Playstation, PS Vita</typehint> + <bindstofield>ezshaper->step6->playstationconsoles</bindstofield> + </field> + <field> + <name>Steam</name> + <type>checkbox</type> + <typehint>Steam Game Client (Includes: America's Army 3, Counter-Strike: Source, Counter-Strike: Global Offensive, Half-Life 2, COD: Black Ops Series, Borderlands 2, Natural Selection 2, Left 4 Dead Series, Portal 2 and many other games on the Steam)</typehint> + <bindstofield>ezshaper->step6->steam</bindstofield> + </field> + <field> + <name>WiiConsoles</name> + <type>checkbox</type> + <typehint>Wii Consoles - Wii, Wii U, DS and 3DS</typehint> + <bindstofield>ezshaper->step6->wiiconsoles</bindstofield> + </field> + <field> + <name>XboxConsoles</name> + <type>checkbox</type> + <typehint>Xbox Consoles - Xbox 360 and Xbox One</typehint> + <bindstofield>ezshaper->step6->xboxconsoles</bindstofield> + </field> + <field> + <name>Enable/Disable specific games</name> + <type>listtopic</type> + </field> + <field> + <name>ARMA2</name> + <type>checkbox</type> + <typehint>ARMA 2</typehint> + <bindstofield>ezshaper->step6->arma2</bindstofield> + </field> + <field> + <name>ARMA3</name> + <type>checkbox</type> + <typehint>ARMA 3</typehint> + <bindstofield>ezshaper->step6->arma3</bindstofield> + </field> + <field> + <name>Battlefield2</name> + <type>checkbox</type> + <typehint>Battlefield 2 - this game uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->battlefield2</bindstofield> + </field> + <field> + <name>Battlefield3</name> + <type>checkbox</type> + <typehint>Battlefield 3 and 4 - this game uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->battlefield3</bindstofield> + </field> + <field> + <name>BattlefieldBC2</name> + <type>checkbox</type> + <typehint>Battlefield: Bad Company 2</typehint> + <bindstofield>ezshaper->step6->battlefieldbc2</bindstofield> + </field> + <field> + <name>Borderlands</name> + <type>checkbox</type> + <typehint>Borderlands</typehint> + <bindstofield>ezshaper->step6->borderlands</bindstofield> + </field> + <field> + <name>CallOfDuty</name> + <type>checkbox</type> + <typehint>Call Of Duty (United Offensive)</typehint> + <bindstofield>ezshaper->step6->callofduty</bindstofield> + </field> + <field> + <name>Counterstrike</name> + <type>checkbox</type> + <typehint>Counterstrike. The ultimate 1st person shooter.</typehint> + <bindstofield>ezshaper->step6->counterstrike</bindstofield> + </field> + <field> + <name>Crysis2</name> + <type>checkbox</type> + <typehint>Crysis 2</typehint> + <bindstofield>ezshaper->step6->crysis2</bindstofield> + </field> + <field> + <name>Crysis3</name> + <type>checkbox</type> + <typehint>Crysis 3</typehint> + <bindstofield>ezshaper->step6->crysis3</bindstofield> + </field> + <field> + <name>DeadSpace2</name> + <type>checkbox</type> + <typehint>Dead Space2 - this game uses a HUGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->deadspace2</bindstofield> + </field> + <field> + <name>DeadSpace3</name> + <type>checkbox</type> + <typehint>Dead Space 3</typehint> + <bindstofield>ezshaper->step6->deadspace3</bindstofield> + </field> + <field> + <name>DeltaForce</name> + <type>checkbox</type> + <typehint>Delta Force</typehint> + <bindstofield>ezshaper->step6->deltaforce</bindstofield> + </field> + <field> + <name>Dirt3</name> + <type>checkbox</type> + <typehint>Dirt 3</typehint> + <bindstofield>ezshaper->step6->dirt3</bindstofield> + </field> + <field> + <name>DOOM3</name> + <type>checkbox</type> + <typehint>DOOM3</typehint> + <bindstofield>ezshaper->step6->doom3</bindstofield> + </field> + <field> + <name>DragonAge2</name> + <type>checkbox</type> + <typehint>Dragon Age 2</typehint> + <bindstofield>ezshaper->step6->dragonage2</bindstofield> + </field> + <field> + <name>EmpireEarth</name> + <type>checkbox</type> + <typehint>Empire Earth</typehint> + <bindstofield>ezshaper->step6->empireearth</bindstofield> + </field> + <field> + <name>EveOnline</name> + <type>checkbox</type> + <typehint>EVE Online</typehint> + <bindstofield>ezshaper->step6->eveonline</bindstofield> + </field> + <field> + <name>Everquest</name> + <type>checkbox</type> + <typehint>Everquest - this game uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->everquest</bindstofield> + </field> + <field> + <name>Everquest2</name> + <type>checkbox</type> + <typehint>Everquest II</typehint> + <bindstofield>ezshaper->step6->everquest2</bindstofield> + </field> + <field> + <name>FarCry</name> + <type>checkbox</type> + <typehint>Far Cry</typehint> + <bindstofield>ezshaper->step6->farcry</bindstofield> + </field> + <field> + <name>FarCry2</name> + <type>checkbox</type> + <typehint>Far Cry 2</typehint> + <bindstofield>ezshaper->step6->farcry2</bindstofield> + </field> + <field> + <name>FarCry3</name> + <type>checkbox</type> + <typehint>Far Cry 3</typehint> + <bindstofield>ezshaper->step6->farcry3</bindstofield> + </field> + <field> + <name>GunZOnline</name> + <type>checkbox</type> + <typehint>GunZ Online</typehint> + <bindstofield>ezshaper->step6->gunzonline</bindstofield> + </field> + <field> + <name>HalfLife</name> + <type>checkbox</type> + <typehint>Half-Life</typehint> + <bindstofield>ezshaper->step6->halflife</bindstofield> + </field> + <field> + <name>LeagueofLegends</name> + <type>checkbox</type> + <typehint>League of Legends - this game uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->leagueoflegends</bindstofield> + </field> + <field> + <name>Lineage2</name> + <type>checkbox</type> + <typehint>Lineage II</typehint> + <bindstofield>ezshaper->step6->lineage2</bindstofield> + </field> + <field> + <name>MassEffect3</name> + <type>checkbox</type> + <typehint>Mass Effect 3</typehint> + <bindstofield>ezshaper->step6->masseffect3</bindstofield> + </field> + <field> + <name>MechwarriorOnline</name> + <type>checkbox</type> + <typehint>MechWarrior: Online - this game uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->mechwarrioronline</bindstofield> + </field> + <field> + <name>Minecraft</name> + <type>checkbox</type> + <typehint>Minecraft</typehint> + <bindstofield>ezshaper->step6->minecraft</bindstofield> + </field> + <field> + <name>PlanetSide</name> + <type>checkbox</type> + <typehint>PlanetSide</typehint> + <bindstofield>ezshaper->step6->planetside</bindstofield> + </field> + <field> + <name>PlanetSide2</name> + <type>checkbox</type> + <typehint>PlanetSide 2</typehint> + <bindstofield>ezshaper->step6->planetside2</bindstofield> + </field> + <field> + <name>OperationFlashpointDR</name> + <type>checkbox</type> + <typehint>Operation Flashpoint: Dragon Rising</typehint> + <bindstofield>ezshaper->step6->operationflashpoint-dr</bindstofield> + </field> + <field> + <name>QuakeIII</name> + <type>checkbox</type> + <typehint>Quake III</typehint> + <bindstofield>ezshaper->step6->quakeiii</bindstofield> + </field> + <field> + <name>QuakeIV</name> + <type>checkbox</type> + <typehint>Quake IV</typehint> + <bindstofield>ezshaper->step6->quakeiv</bindstofield> + </field> + <field> + <name>StarWarsTOR</name> + <type>checkbox</type> + <typehint>StarWars: The Old Republic - this game uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + <bindstofield>ezshaper->step6->starwarstor</bindstofield> + </field> + <field> + <name>TigerWoods2004PS2</name> + <type>checkbox</type> + <typehint>Tiger Woods 2004 for PS2</typehint> + <bindstofield>ezshaper->step6->tigerwoods2004ps2</bindstofield> + </field> + <field> + <name>TribesAscend</name> + <type>checkbox</type> + <typehint>Tribes Ascend</typehint> + <bindstofield>ezshaper->step6->tribesascend</bindstofield> + </field> + <field> + <name>UnrealTournament</name> + <type>checkbox</type> + <typehint>Unreal Tournament Series</typehint> + <bindstofield>ezshaper->step6->unrealtournament</bindstofield> + </field> + <field> + <name>WolfensteinEnemyTerritory</name> + <type>checkbox</type> + <typehint>Wolfenstein Enemy Territory</typehint> + <bindstofield>ezshaper->step6->wolfet</bindstofield> + </field> + <field> + <name>WorldOfWarcraft</name> + <type>checkbox</type> + <typehint>World of Warcraft</typehint> + <bindstofield>ezshaper->step6->wow</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc</includefile> + </step> + <step> + <id>7</id> + <title>pfSense Traffic Shaper Wizard</title> + <disableallfieldsbydefault>true</disableallfieldsbydefault> + <description>Raise or lower other Applications</description> + <fields> + <field> + <name>Enable</name> + <type>checkbox</type> + <typehint>Other networking protocols</typehint> + <description>This will help raise or lower the priority of other protocols higher than most traffic.</description> + <enablefields>AppleRemoteDesktop,MSRDP,PCAnywhere,VNC,AIM,Facetime,GoogleHangouts,ICQ,IRC,Jabber,MSN,TeamSpeak,TeamSpeak3,Ventrilo,PPTP,IPSEC,iTunesRadio,StreamingMP3,RTSP,RTMP,HTTP,IMAP,LotusNotes,POP3,SMTP,BattleNETDownloader,SteamDownloader,APNS,AppleMobileSync,CrashPlan,CVSUP,DNS,GIT,HBCI,ICMP,MySqlServer,NNTP,Slingbox,SMB,SNMP,Subversion</enablefields> + <donotdisable>true</donotdisable> + <bindstofield>ezshaper->step7->enable</bindstofield> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + <field> + <name>Remote Service / Terminal emulation</name> + <type>listtopic</type> + </field> + <field> + <name>AppleRemoteDesktop</name> + <bindstofield>ezshaper->step7->appleremotedesktop</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Apple Remote Desktop</typehint> + </field> + <field> + <name>MSRDP</name> + <type>select</type> + <bindstofield>ezshaper->step7->msrdp</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Microsoft Remote Desktop Protocol</typehint> + </field> + <field> + <name>PCAnywhere</name> + <bindstofield>ezshaper->step7->pcanywhere</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Symantec PC Anywhere</typehint> + </field> + <field> + <name>VNC</name> + <bindstofield>ezshaper->step7->vnc</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Virtual Network Computing</typehint> + </field> + <field> + <name>Messengers</name> + <type>listtopic</type> + </field> + <field> + <name>AIM</name> + <bindstofield>ezshaper->step7->aolinstantmessenger</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>AOL Instant Messenger</typehint> + </field> + <field> + <name>Facetime</name> + <bindstofield>ezshaper->step7->facetime</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Facetime</typehint> + </field> + <field> + <name>ICQ</name> + <bindstofield>ezshaper->step7->icq</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>ICQ</typehint> + </field> + <field> + <name>IRC</name> + <type>select</type> + <bindstofield>ezshaper->step7->irc</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Internet Relay Chat</typehint> + </field> + <field> + <name>Jabber</name> + <type>select</type> + <bindstofield>ezshaper->step7->jabber</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Jabber instant messenger</typehint> + </field> + <field> + <name>GoogleHangouts</name> + <bindstofield>ezshaper->step7->googlehangouts</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Google Hangouts</typehint> + </field> + <field> + <name>MSN</name> + <bindstofield>ezshaper->step7->msnmessenger</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>MSN Messenger</typehint> + </field> + <field> + <name>Teamspeak</name> + <bindstofield>ezshaper->step7->teamspeak</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>TeamSpeak</typehint> + </field> + <field> + <name>Teamspeak3</name> + <bindstofield>ezshaper->step7->teamspeak3</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>TeamSpeak 3</typehint> + </field> + <field> + <name>Ventrilo</name> + <bindstofield>ezshaper->step7->ventrilo</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Ventrilo</typehint> + </field> + <field> + <name>VPN</name> + <type>listtopic</type> + </field> + <field> + <name>PPTP</name> + <type>select</type> + <bindstofield>ezshaper->step7->pptp</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Microsoft Point to Point tunneling protocol</typehint> + </field> + <field> + <name>IPSEC</name> + <type>select</type> + <bindstofield>ezshaper->step7->ipsec</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>IPSEC VPN traffic</typehint> + </field> + <field> + <name>Multimedia/Streaming</name> + <type>listtopic</type> + </field> + <field> + <name>iTunesRadio</name> + <type>select</type> + <bindstofield>ezshaper->step7->itunesradio</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>iTunes Radio - this rule uses a LARGE port range, be aware that you may need to manually rearrange the resulting rules to correctly prioritize other traffic.</typehint> + </field> + <field> + <name>StreamingMP3</name> + <type>select</type> + <bindstofield>ezshaper->step7->streamingmp3</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Streaming Media</typehint> + </field> + <field> + <name>RTSP</name> + <bindstofield>ezshaper->step7->rtsp</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>RealTime streaming protocol</typehint> + </field> + <field> + <name>RTMP</name> + <bindstofield>ezshaper->step7->rtmp</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Real-Time Messaging Protocol - Used by video streaming services such as Twitch.tv.</typehint> + </field> + <field> + <name>Web</name> + <type>listtopic</type> + </field> + <field> + <name>HTTP</name> + <type>select</type> + <bindstofield>ezshaper->step7->http</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>HTTP and HTTPS aka Web Traffic</typehint> + </field> + <field> + <name>Mail</name> + <type>listtopic</type> + </field> + <field> + <name>SMTP</name> + <type>select</type> + <bindstofield>ezshaper->step7->smtp</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Mail Protocol</typehint> + </field> + <field> + <name>POP3</name> + <type>select</type> + <bindstofield>ezshaper->step7->pop3</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>POP3 Protocol</typehint> + </field> + <field> + <name>IMAP</name> + <bindstofield>ezshaper->step7->imap</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>IMAP Protocol</typehint> + </field> + <field> + <name>LotusNotes</name> + <bindstofield>ezshaper->step7->lotusnotes</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Lotus Notes</typehint> + </field> + <field> + <name>Game Downloader</name> + <type>listtopic</type> + </field> + <field> + <name>BattleNetDownloader</name> + <type>select</type> + <bindstofield>ezshaper->step7->battlenetdownloader</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Battle.NET Downloader</typehint> + </field> + <field> + <name>SteamDownloader</name> + <type>select</type> + <bindstofield>ezshaper->step7->steamdownloader</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Steam Downloader</typehint> + </field> + <field> + <name>Miscellaneous</name> + <type>listtopic</type> + </field> + <field> + <name>APNS</name> + <type>select</type> + <bindstofield>ezshaper->step7->apns</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Apple Push Notification Service</typehint> + </field> + <field> + <name>AppleMobileSync</name> + <type>select</type> + <bindstofield>ezshaper->step7->applemobilesync</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Apple Mobile Sync</typehint> + </field> + <field> + <name>CrashPlan</name> + <bindstofield>ezshaper->step7->crashplan</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>CrashPlan</typehint> + </field> + <field> + <name>CVSUP</name> + <bindstofield>ezshaper->step7->cvsup</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>CVSUP</typehint> + </field> + <field> + <name>DNS</name> + <type>select</type> + <bindstofield>ezshaper->step7->dns</bindstofield> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Domain Name Services</typehint> + </field> + <field> + <name>Git</name> + <bindstofield>ezshaper->step7->git</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Git Server</typehint> + </field> + <field> + <name>HBCI</name> + <bindstofield>ezshaper->step7->hbci</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>HBCI</typehint> + </field> + <field> + <name>ICMP</name> + <bindstofield>ezshaper->step7->icmp</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>ICMP Protocol</typehint> + </field> + <field> + <name>SMB</name> + <bindstofield>ezshaper->step7->smb</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Microsoft SMB Protocol and friends</typehint> + </field> + <field> + <name>SNMP</name> + <bindstofield>ezshaper->step7->snmp</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Simple Network Management Protocol</typehint> + </field> + <field> + <name>MySQLServer</name> + <bindstofield>ezshaper->step7->mysqlserver</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>MySQL Server</typehint> + </field> + <field> + <name>NNTP</name> + <bindstofield>ezshaper->step7->nntp</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Internet News</typehint> + </field> + <field> + <name>Slingbox</name> + <bindstofield>ezshaper->step7->slingbox</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Slingbox</typehint> + </field> + <field> + <name>Subversion</name> + <bindstofield>ezshaper->step7->subversion</bindstofield> + <type>select</type> + <options> + <option> + <name>Default priority</name> + <value>D</value> + </option> + <option> + <name>Higher priority</name> + <value>H</value> + </option> + <option> + <name>Lower priority</name> + <value>L</value> + </option> + </options> + <typehint>Subversion Server</typehint> + </field> + <field> + <name>Next</name> + <type>submit</type> + </field> + </fields> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc</includefile> + </step> + <step> + <id>8</id> + <title>pfSense Traffic Shaper Wizard</title> + <field> + <name>Reload profile notice</name> + <type>listtopic</type> + </field> + <description> After pressing Finish the system will load the new profile.<br/> Please note that this may take a moment.<br/> Also note that the traffic shaper is stateful meaning that only new connections will be shaped.<br/> If this is an issue please reset the state table after loading the profile.<br/></description> + <fields> + <field> + <name>Finish</name> + <type>submit</type> + </field> + </fields> + <stepsubmitphpaction>step8_stepsubmitphpaction();</stepsubmitphpaction> + <includefile>/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc</includefile> + </step> +</pfsensewizard> diff --git a/src/usr/local/www/xmlrpc.php b/src/usr/local/www/xmlrpc.php new file mode 100755 index 0000000..5fd022a --- /dev/null +++ b/src/usr/local/www/xmlrpc.php @@ -0,0 +1,592 @@ +<?php +/* + xmlrpc.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2009, 2010 Scott Ullrich + Copyright (C) 2005 Colin Smith + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-xmlrpclibrary +##|*NAME=XMLRPC Library page +##|*DESCR=Allow access to the 'XMLRPC Library' page. +##|*MATCH=xmlrpc.php* +##|-PRIV + +require("config.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("ipsec.inc"); +require("vpn.inc"); +require("shaper.inc"); +require("xmlrpc_server.inc"); +require("xmlrpc.inc"); + +function xmlrpc_loop_detect() { + global $config; + + /* grab sync to ip if enabled */ + if ($config['hasync']) { + $synchronizetoip = $config['hasync']['synchronizetoip']; + } + if ($synchronizetoip) { + if ($synchronizetoip == $_SERVER['REMOTE_ADDR']) { + return true; + } + } + + return false; +} + +$xmlrpc_g = array( + "return" => array( + "true" => new XML_RPC_Response(new XML_RPC_Value(true, $XML_RPC_Boolean)), + "false" => new XML_RPC_Response(new XML_RPC_Value(false, $XML_RPC_Boolean)), + "authfail" => new XML_RPC_Response(new XML_RPC_Value(gettext("Authentication failed"), $XML_RPC_String)) + ) +); + +/* + * pfSense XMLRPC errors + * $XML_RPC_erruser + 1 = Auth failure + */ +$XML_RPC_erruser = 200; + +/* EXPOSED FUNCTIONS */ +$exec_php_doc = gettext("XMLRPC wrapper for eval(). This method must be called with two parameters: a string containing the local system\'s password followed by the PHP code to evaluate."); +$exec_php_sig = array( + array( + $XML_RPC_Boolean, // First signature element is return value. + $XML_RPC_String, // password + $XML_RPC_String, // shell code to exec + ) +); + +function xmlrpc_authfail() { + log_auth("webConfigurator authentication error for 'admin' from {$_SERVER['REMOTE_ADDR']}"); +} + +function exec_php_xmlrpc($raw_params) { + global $config, $xmlrpc_g; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + $exec_php = $params[0]; + eval($exec_php); + if ($toreturn) { + $response = XML_RPC_encode($toreturn); + return new XML_RPC_Response($response); + } else { + return $xmlrpc_g['return']['true']; + } +} + +/*****************************/ +$exec_shell_doc = gettext("XMLRPC wrapper for mwexec(). This method must be called with two parameters: a string containing the local system\'s password followed by an shell command to execute."); +$exec_shell_sig = array( + array( + $XML_RPC_Boolean, // First signature element is return value. + $XML_RPC_String, // password + $XML_RPC_String, // shell code to exec + ) +); + +function exec_shell_xmlrpc($raw_params) { + global $config, $xmlrpc_g; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + $shell_cmd = $params[0]; + mwexec($shell_cmd); + + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$backup_config_section_doc = gettext("XMLRPC wrapper for backup_config_section. This method must be called with two parameters: a string containing the local system\'s password followed by an array containing the keys to be backed up."); +$backup_config_section_sig = array( + array( + $XML_RPC_Struct, // First signature element is return value. + $XML_RPC_String, + $XML_RPC_Array + ) +); + +function backup_config_section_xmlrpc($raw_params) { + global $config, $xmlrpc_g; + + if (xmlrpc_loop_detect()) { + log_error("Disallowing CARP sync loop"); + return; + } + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + $val = array_intersect_key($config, array_flip($params[0])); + + return new XML_RPC_Response(XML_RPC_encode($val)); +} + +/*****************************/ +$restore_config_section_doc = gettext("XMLRPC wrapper for restore_config_section. This method must be called with two parameters: a string containing the local system\'s password and an array to merge into the system\'s config. This function returns true upon completion."); +$restore_config_section_sig = array( + array( + $XML_RPC_Boolean, + $XML_RPC_String, + $XML_RPC_Struct + ) +); + +function restore_config_section_xmlrpc($raw_params) { + global $config, $xmlrpc_g; + + $old_config = $config; + + if (xmlrpc_loop_detect()) { + log_error("Disallowing CARP sync loop"); + return; + } + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + + /* + * Make sure it doesn't end up with both dnsmasq and unbound enabled + * simultaneously in secondary + * */ + if (isset($params[0]['unbound']['enable']) && isset($config['dnsmasq']['enable'])) { + unset($config['dnsmasq']['enable']); + services_dnsmasq_configure(); + } else if (isset($params[0]['dnsmasq']['enable']) && isset($config['unbound']['enable'])) { + unset($config['unbound']['enable']); + services_unbound_configure(); + } + + // Some sections should just be copied and not merged or we end + // up unable to sync the deletion of the last item in a section + $sync_full = array('dnsmasq', 'unbound', 'ipsec', 'aliases', 'wol', 'load_balancer', 'openvpn', 'cert', 'ca', 'crl', 'schedules', 'filter', 'nat', 'dhcpd', 'dhcpv6'); + $sync_full_done = array(); + foreach ($sync_full as $syncfull) { + if (isset($params[0][$syncfull])) { + $config[$syncfull] = $params[0][$syncfull]; + unset($params[0][$syncfull]); + $sync_full_done[] = $syncfull; + } + } + + $vipbackup = array(); + $oldvips = array(); + if (isset($params[0]['virtualip'])) { + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $vipindex => $vip) { + if ($vip['mode'] == "carp") { + $oldvips["{$vip['interface']}_vip{$vip['vhid']}"]['content'] = "{$vip['password']}{$vip['advskew']}{$vip['subnet']}{$vip['subnet_bits']}{$vip['advbase']}"; + $oldvips["{$vip['interface']}_vip{$vip['vhid']}"]['interface'] = $vip['interface']; + $oldvips["{$vip['interface']}_vip{$vip['vhid']}"]['subnet'] = $vip['subnet']; + } else if ($vip['mode'] == "ipalias" && (substr($vip['interface'], 0, 4) == '_vip' || strpos($vip['interface'], "lo0"))) { + $oldvips[$vip['subnet']]['content'] = "{$vip['interface']}{$vip['subnet']}{$vip['subnet_bits']}"; + $oldvips[$vip['subnet']]['interface'] = $vip['interface']; + $oldvips[$vip['subnet']]['subnet'] = $vip['subnet']; + } else if (($vip['mode'] == "ipalias" || $vip['mode'] == 'proxyarp') && !(substr($vip['interface'], 0, 4) == '_vip') || strpos($vip['interface'], "lo0")) { + $vipbackup[] = $vip; + } + } + } + } + + // For vip section, first keep items sent from the master + $config = array_merge_recursive_unique($config, $params[0]); + + /* Then add ipalias and proxyarp types already defined on the backup */ + if (is_array($vipbackup) && !empty($vipbackup)) { + if (!is_array($config['virtualip'])) { + $config['virtualip'] = array(); + } + if (!is_array($config['virtualip']['vip'])) { + $config['virtualip']['vip'] = array(); + } + foreach ($vipbackup as $vip) { + array_unshift($config['virtualip']['vip'], $vip); + } + } + + /* Log what happened */ + $mergedkeys = implode(",", array_merge(array_keys($params[0]), $sync_full_done)); + write_config(sprintf(gettext("Merged in config (%s sections) from XMLRPC client."), $mergedkeys)); + + /* + * The real work on handling the vips specially + * This is a copy of intefaces_vips_configure with addition of not reloading existing/not changed carps + */ + if (isset($params[0]['virtualip']) && is_array($config['virtualip']) && is_array($config['virtualip']['vip'])) { + $carp_setuped = false; + $anyproxyarp = false; + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['mode'] == "carp" && isset($oldvips["{$vip['interface']}_vip{$vip['vhid']}"])) { + if ($oldvips["{$vip['interface']}_vip{$vip['vhid']}"]['content'] == "{$vip['password']}{$vip['advskew']}{$vip['subnet']}{$vip['subnet_bits']}{$vip['advbase']}") { + if (does_vip_exist($vip)) { + unset($oldvips["{$vip['interface']}_vip{$vip['vhid']}"]); + continue; // Skip reconfiguring this vips since nothing has changed. + } + } + } else if ($vip['mode'] == "ipalias" && strstr($vip['interface'], "_vip") && isset($oldvips[$vip['subnet']])) { + if ($oldvips[$vip['subnet']]['content'] == "{$vip['interface']}{$vip['subnet']}{$vip['subnet_bits']}") { + if (does_vip_exist($vip)) { + unset($oldvips[$vip['subnet']]); + continue; // Skip reconfiguring this vips since nothing has changed. + } + } + unset($oldvips[$vip['subnet']]); + } + + switch ($vip['mode']) { + case "proxyarp": + $anyproxyarp = true; + break; + case "ipalias": + interface_ipalias_configure($vip); + break; + case "carp": + if ($carp_setuped == false) { + $carp_setuped = true; + } + interface_carp_configure($vip); + break; + } + } + /* Cleanup remaining old carps */ + foreach ($oldvips as $oldvipar) { + $oldvipif = get_real_interface($oldvipar['interface']); + if (!empty($oldvipif)) { + if (is_ipaddrv6($oldvipar['subnet'])) { + mwexec("/sbin/ifconfig " . escapeshellarg($oldvipif) . " inet6 " . escapeshellarg($oldvipar['subnet']) . " delete"); + } else { + pfSense_interface_deladdress($oldvipif, $oldvipar['subnet']); + } + } + } + if ($carp_setuped == true) { + interfaces_sync_setup(); + } + if ($anyproxyarp == true) { + interface_proxyarp_configure(); + } + } + + if (isset($old_config['ipsec']['enable']) !== isset($config['ipsec']['enable'])) { + vpn_ipsec_configure(); + } + + unset($old_config); + + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$merge_config_section_doc = gettext("XMLRPC wrapper for merging package sections. This method must be called with two parameters: a string containing the local system\'s password and an array to merge into the system\'s config. This function returns true upon completion."); +$merge_config_section_sig = array( + array( + $XML_RPC_Boolean, + $XML_RPC_String, + $XML_RPC_Struct + ) +); + +function merge_installedpackages_section_xmlrpc($raw_params) { + global $config, $xmlrpc_g; + + if (xmlrpc_loop_detect()) { + log_error("Disallowing CARP sync loop"); + return; + } + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + $config['installedpackages'] = array_merge($config['installedpackages'], $params[0]); + $mergedkeys = implode(",", array_keys($params[0])); + write_config(sprintf(gettext("Merged in config (%s sections) from XMLRPC client."), $mergedkeys)); + + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$merge_config_section_doc = gettext("XMLRPC wrapper for merge_config_section. This method must be called with two parameters: a string containing the local system\'s password and an array to merge into the system\'s config. This function returns true upon completion."); +$merge_config_section_sig = array( + array( + $XML_RPC_Boolean, + $XML_RPC_String, + $XML_RPC_Struct + ) +); + +function merge_config_section_xmlrpc($raw_params) { + global $config, $xmlrpc_g; + + if (xmlrpc_loop_detect()) { + log_error("Disallowing CARP sync loop"); + return; + } + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + $config_new = array_overlay($config, $params[0]); + $config = $config_new; + $mergedkeys = implode(",", array_keys($params[0])); + write_config(sprintf(gettext("Merged in config (%s sections) from XMLRPC client."), $mergedkeys)); + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$filter_configure_doc = gettext("Basic XMLRPC wrapper for filter_configure. This method must be called with one parameter: a string containing the local system\'s password. This function returns true upon completion."); +$filter_configure_sig = array( + array( + $XML_RPC_Boolean, + $XML_RPC_String + ) +); + +function filter_configure_xmlrpc($raw_params) { + global $xmlrpc_g, $config; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + filter_configure(); + system_routing_configure(); + setup_gateways_monitor(); + relayd_configure(); + require_once("openvpn.inc"); + openvpn_resync_all(); + if (isset($config['dnsmasq']['enable'])) { + services_dnsmasq_configure(); + } elseif (isset($config['unbound']['enable'])) { + services_unbound_configure(); + } else { + # Both calls above run services_dhcpd_configure(), then we just + # need to call it when they are not called to avoid restarting dhcpd + # twice, as described on ticket #3797 + services_dhcpd_configure(); + } + local_sync_accounts(); + + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$carp_configure_doc = gettext("Basic XMLRPC wrapper for configuring CARP interfaces."); +$carp_configure_sig = array( + array( + $XML_RPC_Boolean, + $XML_RPC_String + ) +); + +function interfaces_carp_configure_xmlrpc($raw_params) { + global $xmlrpc_g; + + if (xmlrpc_loop_detect()) { + log_error("Disallowing CARP sync loop"); + return; + } + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + interfaces_vips_configure(); + + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$check_firmware_version_doc = gettext("Basic XMLRPC wrapper for check_firmware_version. This function will return the output of check_firmware_version upon completion."); + +$check_firmware_version_sig = array( + array( + $XML_RPC_String, + $XML_RPC_String + ) +); + +function check_firmware_version_xmlrpc($raw_params) { + global $xmlrpc_g, $XML_RPC_String; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + return new XML_RPC_Response(new XML_RPC_Value(check_firmware_version(false), $XML_RPC_String)); +} + +/*****************************/ +$pfsense_firmware_version_doc = gettext("Basic XMLRPC wrapper for check_firmware_version. This function will return the output of check_firmware_version upon completion."); + +$pfsense_firmware_version_sig = array ( + array ( + $XML_RPC_Struct, + $XML_RPC_String + ) +); + +function pfsense_firmware_version_xmlrpc($raw_params) { + global $xmlrpc_g; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + return new XML_RPC_Response(XML_RPC_encode(host_firmware_version())); +} + +/*****************************/ +$reboot_doc = gettext("Basic XMLRPC wrapper for rc.reboot."); +$reboot_sig = array(array($XML_RPC_Boolean, $XML_RPC_String)); +function reboot_xmlrpc($raw_params) { + global $xmlrpc_g; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + mwexec_bg("/etc/rc.reboot"); + + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$get_notices_sig = array( + array( + $XML_RPC_Array, + $XML_RPC_String + ), + array( + $XML_RPC_Array + ) +); + +function get_notices_xmlrpc($raw_params) { + global $g, $xmlrpc_g; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + if (!function_exists("get_notices")) { + require("notices.inc"); + } + if (!$params) { + $toreturn = get_notices(); + } else { + $toreturn = get_notices($params); + } + $response = new XML_RPC_Response(XML_RPC_encode($toreturn)); + + return $response; +} + +$xmlrpclockkey = lock('xmlrpc', LOCK_EX); + +/*****************************/ +$server = new XML_RPC_Server( + array( + 'pfsense.exec_shell' => array('function' => 'exec_shell_xmlrpc', + 'signature' => $exec_shell_sig, + 'docstring' => $exec_shell_doc), + 'pfsense.exec_php' => array('function' => 'exec_php_xmlrpc', + 'signature' => $exec_php_sig, + 'docstring' => $exec_php_doc), + 'pfsense.filter_configure' => array('function' => 'filter_configure_xmlrpc', + 'signature' => $filter_configure_sig, + 'docstring' => $filter_configure_doc), + 'pfsense.interfaces_carp_configure' => array('function' => 'interfaces_carp_configure_xmlrpc', + 'docstring' => $carp_configure_sig), + 'pfsense.backup_config_section' => array('function' => 'backup_config_section_xmlrpc', + 'signature' => $backup_config_section_sig, + 'docstring' => $backup_config_section_doc), + 'pfsense.restore_config_section' => array('function' => 'restore_config_section_xmlrpc', + 'signature' => $restore_config_section_sig, + 'docstring' => $restore_config_section_doc), + 'pfsense.merge_config_section' => array('function' => 'merge_config_section_xmlrpc', + 'signature' => $merge_config_section_sig, + 'docstring' => $merge_config_section_doc), + 'pfsense.merge_installedpackages_section_xmlrpc' => array('function' => 'merge_installedpackages_section_xmlrpc', + 'signature' => $merge_config_section_sig, + 'docstring' => $merge_config_section_doc), + 'pfsense.check_firmware_version' => array('function' => 'check_firmware_version_xmlrpc', + 'signature' => $check_firmware_version_sig, + 'docstring' => $check_firmware_version_doc), + 'pfsense.host_firmware_version' => array('function' => 'pfsense_firmware_version_xmlrpc', + 'signature' => $pfsense_firmware_version_sig, + 'docstring' => $host_firmware_version_doc), + 'pfsense.reboot' => array('function' => 'reboot_xmlrpc', + 'signature' => $reboot_sig, + 'docstring' => $reboot_doc), + 'pfsense.get_notices' => array('function' => 'get_notices_xmlrpc', + 'signature' => $get_notices_sig) + ) +); + +unlock($xmlrpclockkey); + +function array_overlay($a1, $a2) { + foreach ($a1 as $k => $v) { + if (!array_key_exists($k, $a2)) { + continue; + } + if (is_array($v) && is_array($a2[$k])) { + $a1[$k] = array_overlay($v, $a2[$k]); + } else { + $a1[$k] = $a2[$k]; + } + } + return $a1; +} + +?> |