%PDF- %PDF-
| Direktori : /proc/thread-self/root/backups/router/usr/local/www/ |
| Current File : //proc/thread-self/root/backups/router/usr/local/www/vpn_openvpn_client.php |
<?php
/*
* Copyright (C) 2014-2016 Deciso B.V.
* Copyright (C) 2008 Shrew Soft Inc. <mgrooms@shrew.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.
*/
require_once("guiconfig.inc");
require_once("interfaces.inc");
require_once("plugins.inc.d/openvpn.inc");
$a_client = &config_read_array('openvpn', 'openvpn-client');
$vpnid = 0;
$act = null;
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if (isset($_GET['dup']) && isset($a_client[$_GET['dup']])) {
$configId = $_GET['dup'];
} elseif (isset($_GET['id']) && isset($a_client[$_GET['id']])) {
$id = $_GET['id'];
$configId = $id;
}
if (isset($_GET['act'])) {
$act = $_GET['act'];
}
$pconfig = array();
// set defaults
$pconfig['autokey_enable'] = "yes"; // just in case the modes switch
$pconfig['autotls_enable'] = "yes"; // just in case the modes switch
$pconfig['tlsmode'] = "auth";
$pconfig['digest'] = "SHA1";
$pconfig['verbosity_level'] = 1; // Default verbosity is 1
// edit existing.
if (isset($configId)) {
// 1 on 1 copy of config attributes
$copy_fields = "auth_user,auth_pass,disable,mode,protocol,interface
,local_port,server_addr,server_port,resolve_retry,remote_random,reneg-sec
,proxy_addr,proxy_port,proxy_user,proxy_passwd,proxy_authtype,description
,custom_options,ns_cert_type,dev_mode,tlsmode,caref,certref,crypto,digest
,tunnel_network,tunnel_networkv6,remote_network,remote_networkv6,use_shaper
,compression,passtos,route_no_pull,route_no_exec,verbosity_level";
foreach (explode(",", $copy_fields) as $fieldname) {
$fieldname = trim($fieldname);
if (isset($a_client[$configId][$fieldname])) {
$pconfig[$fieldname] = $a_client[$configId][$fieldname];
} elseif (!isset($pconfig[$fieldname])) {
// initialize element
$pconfig[$fieldname] = null;
}
}
// load / convert
if (!empty($a_client[$configId]['ipaddr'])) {
$pconfig['interface'] = $pconfig['interface'] . '|' . $a_client[$configId]['ipaddr'];
}
if (isset($a_client[$configId]['tls'])) {
$pconfig['tls'] = base64_decode($a_client[$configId]['tls']);
} else {
$pconfig['tls'] = null;
$pconfig['tlsmode'] = null;
}
if (isset($a_client[$configId]['shared_key'])) {
$pconfig['shared_key'] = base64_decode($a_client[$configId]['shared_key']);
} else {
$pconfig['shared_key'] = null ;
}
if (isset($id)) {
$vpnid = $a_client[$id]['vpnid'];
}
} elseif ($act=="new") {
// create new
$pconfig['interface'] = 'any';
$init_fields = "auth_user,auth_pass,disable,mode,protocol,interface
,local_port,server_addr,server_port,resolve_retry,remote_random,reneg-sec
,proxy_addr,proxy_port,proxy_user,proxy_passwd,proxy_authtype,description
,custom_options,ns_cert_type,dev_mode,caref,certref,crypto,digest,tlsmode
,tunnel_network,tunnel_networkv6,remote_network,remote_networkv6,use_shaper
,compression,passtos,route_no_pull,route_no_exec,verbosity_level";
foreach (explode(",", $init_fields) as $fieldname) {
$fieldname = trim($fieldname);
if (!isset($pconfig[$fieldname])) {
$pconfig[$fieldname] = null;
}
}
}
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pconfig = $_POST;
$input_errors = array();
if (isset($_POST['id']) && isset($a_client[$_POST['id']])) {
$id = $_POST['id'];
}
if (isset($_POST['act'])) {
$act = $_POST['act'];
}
if ($act == "del") {
// remove client
$response = ["status" => "failed", "message" => gettext("not found")];
if (isset($id)) {
$vpn_id = !empty($a_client[$id]) ? $a_client[$id]['vpnid'] : null;
if ($vpn_id !== null && is_interface_assigned("ovpnc{$vpn_id}")) {
$response = [
"status" => "failed",
"message" => gettext("This tunnel cannot be deleted because it is still being used as an interface.")
];
} elseif ($vpn_id !== null) {
openvpn_delete('client', $a_client[$id]);
unset($a_client[$id]);
write_config();
$response = ["status" => "ok"];
}
}
echo json_encode($response);
exit;
} elseif ($act == "del_x") {
if (!empty($pconfig['rule']) && is_array($pconfig['rule'])) {
foreach ($pconfig['rule'] as $rulei) {
$vpn_id = !empty($a_client[$rulei]) ? $a_client[$rulei]['vpnid'] : null;
// XXX: silently ignore entries that can't be removed, no clean option to pass messages in form result
if ($vpn_id !== null && !is_interface_assigned("ovpnc{$vpn_id}")) {
openvpn_delete('client', $a_client[$rulei]);
unset($a_client[$rulei]);
}
}
write_config();
}
header(url_safe('Location: /vpn_openvpn_client.php'));
exit;
} elseif ($act == "move"){
// move selected items
if (!isset($id)) {
// if id not set/found, move to end
$id = count($a_client);
}
$a_client = legacy_move_config_list_items($a_client, $id, $pconfig['rule']);
write_config();
header(url_safe('Location: /vpn_openvpn_client.php'));
exit;
} elseif ($act == "toggle") {
if (isset($id)) {
if (isset($a_client[$id]['disable'])) {
unset($a_client[$id]['disable']);
} else {
$a_client[$id]['disable'] = true;
}
write_config();
openvpn_configure_single($a_client[$id]['vpnid']);
}
header(url_safe('Location: /vpn_openvpn_client.php'));
exit;
} else {
// update client (after validation)
if (isset($id)) {
$vpnid = $a_client[$id]['vpnid'];
}
if (isset($pconfig['mode']) && $pconfig['mode'] != "p2p_shared_key") {
$tls_mode = true;
} else {
$tls_mode = false;
}
// generate new key
if (!empty($pconfig['autokey_enable'])) {
$pconfig['shared_key'] = openvpn_create_key();
}
/* input validation */
if (strpos($pconfig['interface'], '|') !== false) {
list($iv_iface, $iv_ip) = explode("|", $pconfig['interface']);
} else {
$iv_iface = $pconfig['interface'];
$iv_ip = null;
}
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 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 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 (!empty($pconfig['local_port'])) {
if (!is_numeric($pconfig['local_port']) || $pconfig['local_port'] < 0 || ($pconfig['local_port'] > 65535)) {
$input_errors[] = gettext("The field 'Local port' must contain a valid port, ranging from 0 to 65535.");
}
$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");
}
}
$server_addr_a = array();
$server_port_a = array();
foreach (array_keys($pconfig['server_addr']) as $i) {
if (empty($pconfig['server_addr'][$i]) && empty($pconfig['server_port'][$i])) {
continue;
}
if (empty($pconfig['server_addr'][$i]) || (!is_domain($pconfig['server_addr'][$i]) && !is_ipaddr($pconfig['server_addr'][$i]))) {
$input_errors[] = gettext("The field 'Server host or address' must contain a valid IP address or domain name.") ;
}
if (empty($pconfig['server_port'][$i]) || !is_numeric($pconfig['server_port'][$i]) || $pconfig['server_port'][$i] < 0 || $pconfig['server_port'][$i] > 65535) {
$input_errors[] = gettext("The field 'Server port' must contain a valid port, ranging from 0 to 65535.");
}
$server_addr_a[] = $pconfig['server_addr'][$i];
$server_port_a[] = $pconfig['server_port'][$i];
}
$pconfig['server_addr'] = implode(',', $server_addr_a);
$pconfig['server_port'] = implode(',', $server_port_a);
if (empty($pconfig['server_addr']) || empty($pconfig['server_port'])) {
$input_errors[] = gettext("At least one remote server must be specified.");
}
if (isset($pconfig['reneg-sec']) && $pconfig['reneg-sec'] != "" && (string)((int)$pconfig['reneg-sec']) != $pconfig['reneg-sec']) {
$input_errors[] = gettext("Renegotiate time should contain a valid number of seconds.");
}
if (!empty($pconfig['proxy_addr'])) {
if (empty($pconfig['proxy_addr']) || (!is_domain($pconfig['proxy_addr']) && !is_ipaddr($pconfig['proxy_addr']))) {
$input_errors[] = gettext("The field 'Proxy host or address' must contain a valid IP address or domain name.");
}
if (empty($pconfig['proxy_port']) || !is_numeric($pconfig['proxy_port']) || $pconfig['proxy_port'] < 0 || ($pconfig['proxy_port'] > 65535)) {
$input_errors[] = gettext("The field 'Proxy port' must contain a valid port, ranging from 0 to 65535.");
}
if (isset($pconfig['proxy_authtype']) && $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 ($result = openvpn_validate_cidr($pconfig['tunnel_network'], gettext('IPv4 Tunnel Network'), false, 'ipv4')) {
$input_errors[] = $result;
}
if ($result = openvpn_validate_cidr($pconfig['tunnel_networkv6'], gettext('IPv6 Tunnel Network'), false, 'ipv6')) {
$input_errors[] = $result;
}
if ($result = openvpn_validate_cidr($pconfig['remote_network'], gettext('IPv4 Remote Network'), true, 'ipv4')) {
$input_errors[] = $result;
}
if ($result = openvpn_validate_cidr($pconfig['remote_networkv6'], gettext('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 (!$tls_mode && empty($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 && !empty($pconfig['tlsmode']) && empty($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 Shared Key' does not appear to be valid");
}
}
/* If we are not in shared key mode, then we need the CA/Cert. */
if (isset($pconfig['mode']) && $pconfig['mode'] != "p2p_shared_key") {
$reqdfields = explode(" ", "caref");
$reqdfieldsn = array(gettext("Certificate Authority"));
} elseif (empty($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($pconfig, $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 password must be entered.");
}
$prev_opt = (isset($id) && !empty($a_client[$id])) ? $a_client[$id]['custom_options'] : "";
if ($prev_opt != str_replace("\r\n", "\n", $pconfig['custom_options']) && !userIsAdmin($_SESSION['Username'])) {
$input_errors[] = gettext('Advanced options may only be edited by system administrators due to the increased possibility of privilege escalation.');
}
if (count($input_errors) == 0) {
// save data
$client = array();
// 1 on 1 copy of config attributes
$copy_fields = "auth_user,auth_pass,protocol,dev_mode,local_port,reneg-sec
,server_addr,server_port,resolve_retry,proxy_addr,proxy_port,remote_random
,proxy_authtype,proxy_user,proxy_passwd,description,mode,crypto,digest
,tunnel_network,tunnel_networkv6,remote_network,remote_networkv6
,use_shaper,compression,passtos,route_no_pull,route_no_exec,tlsmode
,verbosity_level,interface";
foreach (explode(",", $copy_fields) as $fieldname) {
$fieldname = trim($fieldname);
if (!empty($pconfig[$fieldname]) || $pconfig[$fieldname] == '0') {
$client[$fieldname] = $pconfig[$fieldname];
}
}
// attributes containing some kind of logic
if ($vpnid) {
$client['vpnid'] = $vpnid;
} else {
$client['vpnid'] = openvpn_vpnid_next();
}
if (isset($pconfig['disable']) && $pconfig['disable'] == "yes") {
$client['disable'] = true;
}
if (strpos($pconfig['interface'], "|") !== false) {
list($client['interface'], $client['ipaddr']) = explode("|", $pconfig['interface']);
}
$client['custom_options'] = str_replace("\r\n", "\n", $pconfig['custom_options']);
if ($tls_mode) {
$client['caref'] = $pconfig['caref'];
$client['certref'] = $pconfig['certref'];
if (!empty($pconfig['tlsmode'])) {
if (!empty($pconfig['autotls_enable'])) {
$pconfig['tls'] = openvpn_create_key();
}
$client['tls'] = base64_encode($pconfig['tls']);
}
} else {
$client['shared_key'] = base64_encode($pconfig['shared_key']);
}
if (isset($id)) {
$a_client[$id] = $client;
} else {
$a_client[] = $client;
}
write_config();
openvpn_configure_single($client['vpnid']);
header(url_safe('Location: /vpn_openvpn_client.php'));
exit;
}
}
}
// escape form output before processing
legacy_html_escape_form_data($pconfig);
include("head.inc");
?>
<body>
<?php include("fbegin.inc"); ?>
<script>
//<![CDATA[
$( document ).ready(function() {
// link delete buttons
$(".act_delete").click(function(){
var id = $(this).data("id");
if (id != 'x') {
BootstrapDialog.show({
type:BootstrapDialog.TYPE_DANGER,
title: "<?= gettext("OpenVPN");?>",
message: "<?= gettext("Do you really want to delete this client?"); ?>",
buttons: [{
label: "<?= gettext("No");?>",
action: function(dialogRef) {
dialogRef.close();
}}, {
label: "<?= gettext("Yes");?>",
action: function(dialogRef) {
$.post(window.location, {act: 'del', id:id}, function(data) {
if (data.status == 'failed' && data.message !== undefined) {
dialogRef.close();
BootstrapDialog.show({
type:BootstrapDialog.TYPE_DANGER,
title: "<?= gettext("OpenVPN");?>",
message: data.message,
buttons: [
{
label: "<?= gettext("Close");?>",
action: function(dialogRef) {
dialogRef.close();
}
}
]
});
return;
} else {
location.reload();
}
}, 'json');
dialogRef.close();
}
}]
});
} else {
// delete selected
BootstrapDialog.show({
type:BootstrapDialog.TYPE_DANGER,
title: "<?=gettext("OpenVPN");?>",
message: "<?=gettext("Do you really want to delete the selected clients?");?>",
buttons: [{
label: "<?= gettext("No");?>",
action: function(dialogRef) {
dialogRef.close();
}}, {
label: "<?= gettext("Yes");?>",
action: function(dialogRef) {
$("#id").val("");
$("#action").val("del_x");
$("#iform2").submit()
}
}]
});
}
});
function removeRow() {
if ( $('#maintable > tbody > tr').length == 1 ) {
$('#maintable > tbody > tr:last > td > input').each(function () { $(this).val(""); });
} else {
$(this).parent().parent().remove();
}
}
function addRow() {
// copy last row and reset values
$('#maintable > tbody > tr:last > td > label').removeClass('act-addrow').addClass('act-removerow');
$('#maintable > tbody > tr:last > td > label').unbind('click');
$('#maintable > tbody > tr:last > td > label').click(removeRow);
$('#maintable > tbody > tr:last > td > label > span:first').removeClass('fa-plus').addClass('fa-minus');
$('#maintable > tbody').append('<tr>'+$('#maintable > tbody > tr:last').html()+'</tr>');
$('#maintable > tbody > tr:last > td > input').each(function () { $(this).val(""); });
$('#maintable > tbody > tr:last > td > label').removeClass('act-removerow').addClass('act-addrow');
$('#maintable > tbody > tr:last > td > label').unbind('click');
$('#maintable > tbody > tr:last > td > label').click(addRow);
$('#maintable > tbody > tr:last > td > label > span:first').removeClass('fa-minus').addClass('fa-plus');
}
$(".act-removerow").click(removeRow);
$(".act-addrow").click(addRow);
// link toggle buttons
$(".act_toggle").click(function(event){
event.preventDefault();
$.post(window.location, {act: 'toggle', id:$(this).data("id")}, function(data) {
location.reload();
});
});
// link move buttons
$(".act_move").click(function(event){
event.preventDefault();
$("#id").val($(this).data("id"));
$("#action").val("move");
$("#iform2").submit();
});
// watch scroll position and set to last known on page load
watchScrollPosition();
$("#mode").change(function(){
switch($(this).val()) {
case "p2p_tls":
$(".tls_option").show();
$(".psk_option").hide();
break;
case "p2p_shared_key":
$(".psk_option").show();
$(".tls_option").hide();
break;
}
});
$("#mode").change();
$("#autokey_enable").change(function(){
if ($("#autokey_enable:checked").val() != undefined) {
$("#autokey_opts").hide();
} else {
$("#autokey_opts").show();
}
});
$("#autokey_enable").change();
$("#proxy_authtype").change(function(){
if ($('#proxy_authtype').val() != 'none') {
$('#proxy_authtype_opts').show();
} else {
$('#proxy_authtype_opts').hide();
}
});
$("#proxy_authtype").change();
$("#autotls_enable,#tlsmode").change(function(){
if ($("#autotls_enable").length !== 0 && $("#autotls_enable").is(":checked")) {
$("#autotls_opts").hide();
} else {
$("#autotls_opts").show();
}
if ($("#tlsmode").val() === "") {
$(".tls_input_field").prop("disabled", true);
} else {
$(".tls_input_field").prop("disabled", false);
}
});
$("#autotls_enable").change();
});
//]]>
</script>
<section class="page-content-main">
<div class="container-fluid">
<div class="row">
<?php
if (isset($input_errors) && count($input_errors) > 0) {
print_input_errors($input_errors);
}
if (isset($savemsg)) {
print_info_box($savemsg);
}?>
<?php
if ($act=="new" || $act=="edit") :?>
<form method="post" name="iform" id="iform">
<section class="col-xs-12">
<div class="tab-content content-box col-xs-12">
<div class="table-responsive">
<table class="table table-striped opnsense_standard_table_form">
<tr>
<td style="width:22%"><strong><?=gettext("General information"); ?></strong></td>
<td style="width:78%; text-align:right">
<small><?=gettext("full help"); ?> </small>
<i class="fa fa-toggle-off text-danger" style="cursor: pointer;" id="show_all_help_page"></i>
</td>
</tr>
<tr>
<td><a id="help_for_disable" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Disabled"); ?></td>
<td>
<input name="disable" type="checkbox" value="yes" <?= !empty($pconfig['disable']) ? "checked=\"checked\"" : "";?> />
<div class="hidden" data-for="help_for_disable">
<small><?=gettext("Set this option to disable this client without removing it from the list"); ?>.</small>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_description" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Description"); ?></td>
<td>
<input name="description" type="text" class="form-control unknown" size="30" value="<?=$pconfig['description'];?>" />
<div class="hidden" data-for="help_for_description">
<?=gettext("You may enter a description here for your reference (not parsed)."); ?>
</div>
</td>
</tr>
<tr>
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Server Mode");?></td>
<td>
<select name="mode" id="mode">
<?php
$openvpn_client_modes = array(
'p2p_tls' => gettext("Peer to Peer ( SSL/TLS )"),
'p2p_shared_key' => gettext("Peer to Peer ( Shared Key )") );
foreach ($openvpn_client_modes as $name => $desc) :
$selected = "";
if ($pconfig['mode'] == $name) {
$selected = "selected=\"selected\"";
}?>
<option value="<?=$name;?>" <?=$selected;?>><?=$desc;?></option>
<?php
endforeach; ?>
</select>
</td>
</tr>
<tr>
<td><a id="help_for_protocol" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Protocol");?></td>
<td>
<select name='protocol' class="form-control">
<?php
foreach (openvpn_get_protocols() as $prot):
$selected = "";
if ($pconfig['protocol'] == $prot) {
$selected = "selected=\"selected\"";
}?>
<option value="<?=$prot;?>" <?=$selected;?>><?=$prot;?></option>
<?php
endforeach; ?>
</select>
<div class="hidden" data-for="help_for_protocol">
<?= gettext('Select the protocol family to be used. Note that using both families with UDP/TCP ' .
'does not work with an explicit interface as OpenVPN does not support listening to more ' .
'than one specified IP address. In this case IPv4 is currently assumed.') ?>
</div>
</td>
</tr>
<tr>
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Device mode");?></td>
<td>
<select name="dev_mode" id="dev_mode">
<?php
foreach (array("tun", "tap") as $mode) :
$selected = "";
if ($pconfig['dev_mode'] == $mode) {
$selected = "selected=\"selected\"";
}?>
<option value="<?=$mode;?>" <?=$selected;?>><?=$mode;?></option>
<?php
endforeach; ?>
</select>
</td>
</tr>
<tr>
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Interface"); ?></td>
<td>
<select name="interface" class="form-control">
<?php
$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).")";
}
$interfaces['lo0'] = "Localhost";
$interfaces['any'] = "any";
foreach ($interfaces as $iface => $ifacename) :
$selected = "";
if ($iface == $pconfig['interface']) {
$selected = "selected=\"selected\"";
}?>
<option value="<?=$iface;?>" <?=$selected;?>><?=htmlspecialchars($ifacename);?></option>
<?php
endforeach; ?>
</select>
</td>
</tr>
<tr>
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Remote server");?></td>
<td>
<table class="table table-striped table-condensed" id="maintable">
<thead>
<tr>
<th></th>
<th><?= gettext('Host or address') ?></th>
<th><?= gettext('Port') ?></th>
</tr>
</thead>
<tbody>
<?php
$pconfig['server_addr'] = !empty($pconfig['server_addr']) ? explode(',', $pconfig['server_addr']) : array();
$pconfig['server_port'] = !empty($pconfig['server_port']) ? explode(',', $pconfig['server_port']) : array();
$pconfig['server_addr'][] = '';
$pconfig['server_port'][] = '';
foreach ($pconfig['server_addr'] as $i => $item): ?>
<tr>
<td>
<?php
if (!empty($item)): ?>
<label class="act-removerow btn btn-default btn-xs">
<span class="fa fa-minus fa-fw"></span>
<span class="sr-only"><?= gettext('Remove') ?></span>
</label>
<?php
else: ?>
<label class="act-addrow btn btn-default btn-xs">
<span class="fa fa-plus fa-fw"></span>
<span class="sr-only"><?= gettext('Add') ?></span>
</label>
<?php
endif ?>
</td>
<td>
<input name="server_addr[]" type="text" value="<?= $pconfig['server_addr'][$i] ?>" />
</td>
<td>
<input name="server_port[]" type="text" value="<?= $pconfig['server_port'][$i] ?>" />
</td>
</tr>
<?php
endforeach ?>
</tbody>
</table>
<br/>
<input name="remote_random" type="checkbox" value="yes" <?= !empty($pconfig['remote_random']) ? 'checked="checked"' : '' ?>/>
<?= gettext('Select remote server at random') ?>
</td>
</tr>
<tr>
<td><a id="help_for_resolve_retry" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Retry DNS resolution"); ?></td>
<td>
<input name="resolve_retry" type="checkbox" value="yes" <?= !empty($pconfig['resolve_retry']) ? 'checked="checked"' : '' ?>/>
<?= gettext('Infinitely resolve remote server') ?>
<div class="hidden" data-for="help_for_resolve_retry">
<div><small><?=gettext("Continuously attempt to resolve the server host name. Useful when communicating with a server that is not permanently connected to the Internet"); ?></small></div>
</div>
</td>
</tr>
<tr>
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Proxy host or address");?></td>
<td>
<input name="proxy_addr" type="text" class="form-control unknown" size="30" value="<?=$pconfig['proxy_addr'];?>" />
</td>
</tr>
<tr>
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Proxy port");?></td>
<td>
<input name="proxy_port" type="text" class="form-control unknown" size="5" value="<?=$pconfig['proxy_port'];?>" />
</td>
</tr>
<tr>
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Proxy authentication extra options");?></td>
<td>
<?=gettext("Authentication method"); ?>
<select name="proxy_authtype" id="proxy_authtype" class="form-control select">
<option value="none" <?=$pconfig['proxy_authtype'] == "none" ? "selected=\"selected\"" : "" ?> > <?=gettext("none"); ?></option>
<option value="basic" <?=$pconfig['proxy_authtype'] == "basic" ? "selected=\"selected\"" : "" ?> > <?=gettext("basic"); ?></option>
<option value="basic" <?=$pconfig['proxy_authtype'] == "ntlm" ? "selected=\"selected\"" : "" ?> > <?=gettext("ntlm"); ?></option>
</select>
<div style="display:none" id="proxy_authtype_opts">
<div><?=gettext("Username"); ?> <br/></div>
<div><input name="proxy_user" id="proxy_user" class="form-control unknown" type="text" size="20" value="<?=$pconfig['proxy_user'];?>" /></div>
<div><?=gettext("Password"); ?> </div>
<div><input name="proxy_passwd" id="proxy_passwd" type="password" autocomplete="new-password" class="form-control pwd" size="20" value="<?=$pconfig['proxy_passwd'];?>" /></div>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_local_port" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Local port");?></td>
<td>
<input name="local_port" type="text" class="form-control unknown" size="5" value="<?=$pconfig['local_port'];?>" />
<div class="hidden" data-for="help_for_local_port">
<em><small><?=gettext("Set this option if you would like to bind to a specific port. Leave this blank or enter 0 for a random dynamic port."); ?></small></em>
</div>
</td>
</tr>
</table>
</div>
</div>
</section>
<section class="col-xs-12">
<div class="tab-content content-box col-xs-12">
<div class="table-responsive">
<table class="table table-striped opnsense_standard_table_form">
<tr>
<td colspan="2"><strong><?=gettext("User Authentication Settings"); ?></strong></td>
</tr>
<tr>
<td style="width:22%"><a id="help_for_auth_user_pass" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("User name/pass"); ?></td>
<td style="width:78%">
<div><?=gettext("Username"); ?></div>
<div><input name="auth_user" id="auth_user" class="form-control unknown" type="text" size="20" value="<?=$pconfig['auth_user'];?>" /></div>
<div><?=gettext("Password"); ?></div>
<div><input name="auth_pass" id="auth_pass" type="password" autocomplete="new-password" class="form-control pwd" size="20" value="<?=$pconfig['auth_pass'];?>" /></div>
<div class="hidden" data-for="help_for_auth_user_pass">
<?=gettext("Leave empty when no user name and password are needed."); ?>
</div>
<br/>
</td>
</tr>
<tr>
<td><a id="help_for_reneg-sec" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Renegotiate time"); ?></td>
<td>
<input type="text" name="reneg-sec" value="<?=$pconfig['reneg-sec'];?>">
<div class="hidden" data-for="help_for_reneg-sec">
<?= gettext('Renegotiate data channel key after n seconds (default=3600). Set to 0 to disable.') ?>
</div>
</td>
</tr>
</table>
</div>
</div>
</section>
<section class="col-xs-12">
<div class="tab-content content-box col-xs-12">
<div class="table-responsive">
<table class="table table-striped opnsense_standard_table_form">
<tr>
<td style="width:22%"><strong><?=gettext("Cryptographic Settings"); ?></strong></td>
<td style="width:78%"></td>
</tr>
<tr class="tls_option">
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("TLS Authentication"); ?></td>
<td>
<select id="tlsmode" name='tlsmode' class="form-control">
<option value="" <?= empty($pconfig['tlsmode']) ? "selected=\"selected\"" : "";?>>
<?=gettext("Disabled");?>
</option>
<option value="auth" <?= $pconfig['tlsmode'] === "auth" ? "selected=\"selected\"" : "";?>>
<?=gettext("Enabled - Authentication only");?>
</option>
<option value="crypt" <?= $pconfig['tlsmode'] === "crypt" ? "selected=\"selected\"" : "";?>>
<?=gettext("Enabled - Authentication & encryption");?>
</option>
</select>
</td>
</tr>
<tr class="tls_option">
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("TLS Shared Key"); ?></td>
<td>
<?php
if (empty($pconfig['tls'])) :?>
<input name="autotls_enable" id="autotls_enable" class="tls_input_field" type="checkbox" value="yes" <?= !empty($pconfig['autotls_enable']) ? "checked=\"checked\"" : "";?> >
<?=gettext("Automatically generate a shared TLS authentication key"); ?>.
<?php
endif; ?>
<div id="autotls_opts">
<textarea id="tls" name="tls" cols="65" rows="7" class="tls_input_field formpre"><?=isset($pconfig['tls'])?$pconfig['tls']:"";?></textarea>
<p class="text-muted"><em><small><?=gettext("Paste your shared key here"); ?>.</small></em></p>
</div>
</td>
</tr>
<tr class="tls_option">
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Peer Certificate Authority"); ?></td>
<td>
<?php
if (isset($config['ca'])) :?>
<select name='caref' class="form-control">
<?php
foreach ($config['ca'] as $ca) :
$selected = "";
if (isset($pconfig['caref']) && $pconfig['caref'] == $ca['refid']) {
$selected = "selected=\"selected\"";
}?>
<option value="<?=$ca['refid'];?>" <?=$selected;?>><?=$ca['descr'];?></option>
<?php
endforeach; ?>
</select>
<?php
else :?>
<b><?=gettext("No Certificate Authorities defined.");?></b> <br />
<?=gettext("Create one under");?> <a href="system_camanager.php"><?=gettext("System: Certificates");?></a>.
<?php
endif; ?>
</td>
</tr>
<tr class="tls_option">
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Client Certificate"); ?></td>
<td>
<select name='certref' class="form-control">
<?php
foreach (isset($config['cert']) ? $config['cert'] : array() as $cert) :
$selected = "";
$caname = "";
$inuse = "";
$revoked = "";
if (isset($cert['caref'])) {
$ca = lookup_ca($cert['caref']);
if (!empty($ca)) {
$caname = " (CA: {$ca['descr']})";
}
}
if (isset($pconfig['certref']) && $pconfig['certref'] == $cert['refid']) {
$selected = "selected=\"selected\"";
}
if (isset($cert['refid']) && cert_in_use($cert['refid'])) {
$inuse = " *In Use";
}
if (is_cert_revoked($cert)) {
$revoked = " *Revoked";
}?>
<option value="<?=$cert['refid'];?>" <?=$selected;?>><?=$cert['descr'] . $caname . $inuse . $revoked;?></option>
<?php
endforeach; ?>
<option value="" <?=empty($pconfig['certref'])? "selected=\"selected\"" : "";?>>
<?=gettext("None");?> <?=gettext("(Username and Password required)");?>
</option>
</select>
<?php
if (!isset($config['cert']) || count($config['cert']) == 0) :?>
<b><?=gettext("No Certificates defined.");?></b> <br /><?=gettext("Create one under");?>
<a href="system_certmanager.php"><?=gettext("System: Certificates");?></a> <?=gettext("if one is required for this connection.");?>
<?php
endif; ?>
</td>
</tr>
<tr class="psk_option">
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Shared Key"); ?></td>
<td>
<?php
if (empty($pconfig['shared_key'])) :?>
<input name="autokey_enable" id="autokey_enable" type="checkbox" value="yes" <?= !empty($pconfig['autokey_enable']) ? "checked=\"checked\"" : "";?> />
<?=gettext("Automatically generate a shared key"); ?>.
<?php
endif; ?>
<div id="autokey_opts">
<textarea name="shared_key" cols="65" rows="7" class="formpre"><?=isset($pconfig['shared_key']) ? $pconfig['shared_key'] : "";?></textarea>
<em><small><?=gettext("Paste your shared key here"); ?>.</small></em>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_crypto" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Encryption algorithm (deprecated)"); ?></td>
<td>
<select name="crypto" class="form-control">
<?php
foreach (openvpn_get_cipherlist() as $name => $desc) :
$selected = $name == $pconfig['crypto'] ? " selected=\"selected\"" : "";?>
<option value="<?=$name;?>"<?=$selected?>><?=htmlspecialchars($desc);?></option>
<?php
endforeach; ?>
</select>
<div class="hidden" data-for="help_for_crypto">
<?= gettext('Cipher selection for older clients. Only preserved for backwards compatibility reasons.') ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_digest" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Auth Digest Algorithm"); ?></td>
<td>
<select name="digest" class="form-control">
<?php
$digestlist = openvpn_get_digestlist();
foreach ($digestlist as $name => $desc) :
$selected = "";
if ($name == $pconfig['digest']) {
$selected = " selected=\"selected\"";
}?>
<option value="<?=$name;?>"<?=$selected?>><?=htmlspecialchars($desc);?></option>
<?php
endforeach; ?>
</select>
<div class="hidden" data-for="help_for_digest">
<?=gettext("NOTE: Leave this set to SHA1 unless the server is set to match. SHA1 is the default for OpenVPN."); ?>
</div>
</td>
</tr>
</table>
</div>
</div>
</section>
<section class="col-xs-12">
<div class="tab-content content-box col-xs-12">
<div class="table-responsive">
<table class="table table-striped opnsense_standard_table_form">
<tr>
<td colspan="2"><strong><?=gettext("Tunnel Settings"); ?></strong></td>
</tr>
<tr>
<td style="width:22%"><a id="help_for_tunnel_network" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("IPv4 Tunnel Network"); ?></td>
<td style="width:78%">
<input name="tunnel_network" type="text" class="form-control unknown" size="20" value="<?=$pconfig['tunnel_network'];?>" />
<div class="hidden" data-for="help_for_tunnel_network">
<?=gettext("This is the IPv4 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"); ?>.
</div>
</td>
</tr>
<tr>
<td><a id="help_for_tunnel_networkv6" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("IPv6 Tunnel Network"); ?></td>
<td>
<input name="tunnel_networkv6" type="text" class="form-control unknown" size="20" value="<?=$pconfig['tunnel_networkv6'];?>" />
<div class="hidden" data-for="help_for_tunnel_networkv6">
<?=gettext("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 is assumed to be the " .
"server address and the second network address " .
"will be assigned to the client virtual " .
"interface"); ?>.
</div>
</td>
</tr>
<tr>
<td><a id="help_for_remote_network" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("IPv4 Remote Network"); ?></td>
<td>
<input name="remote_network" type="text" class="form-control unknown" size="40" value="<?=$pconfig['remote_network'];?>" />
<div class="hidden" data-for="help_for_remote_network">
<?=gettext("These are the 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 to " .
"only communicate with other clients"); ?>.
</div>
</td>
</tr>
<tr>
<td><a id="help_for_remote_networkv6" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("IPv6 Remote Network"); ?></td>
<td>
<input name="remote_networkv6" type="text" class="form-control unknown" size="40" value="<?=$pconfig['remote_networkv6'];?>" />
<div class="hidden" data-for="help_for_remote_networkv6">
<?=gettext("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 to " .
"only communicate with other clients"); ?>.
</div>
</td>
</tr>
<tr>
<td><a id="help_for_use_shaper" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Limit outgoing bandwidth");?></td>
<td>
<input name="use_shaper" type="text" class="form-control unknown" size="5" value="<?=$pconfig['use_shaper'];?>" />
<div class="hidden" data-for="help_for_use_shaper">
<?=gettext("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)"); ?>.
</div>
</td>
</tr>
<tr>
<td><a id="help_for_compression" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Compression"); ?></td>
<td>
<select name="compression" class="form-control">
<?php
foreach (openvpn_compression_modes() as $cmode => $cmodedesc):
$selected = "";
if ($cmode == $pconfig['compression']) {
$selected = " selected=\"selected\"";
}
?>
<option value="<?= $cmode ?>" <?= $selected ?>><?= $cmodedesc ?></option>
<?php
endforeach; ?>
</select>
<div class="hidden" data-for="help_for_compression">
<?=gettext("Compress tunnel packets using the LZ4/LZO algorithm. The LZ4 generally offers the best performance with least CPU usage. For backwards compatibility use the LZO (which is identical to the older option --comp-lzo yes). In the partial mode (the option --compress with an empty algorithm) compression is turned off, but the packet framing for compression is still enabled, allowing a different setting to be pushed later. The legacy LZO algorithm with adaptive compression mode will dynamically disable compression for a period of time if OpenVPN detects that the data in the packets is not being compressed efficiently."); ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_passtos" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Type-of-Service"); ?></td>
<td>
<input name="passtos" type="checkbox" value="yes" <?=!empty($pconfig['passtos']) ? "checked=\"checked\"" : "" ;?> />
<div class="hidden" data-for="help_for_passtos">
<?=gettext("Set the TOS IP header value of tunnel packets to match the encapsulated packet value"); ?>.
</div>
</td>
</tr>
<tr id="chkboxRouteNoPull">
<td><a id="help_for_route_no_pull" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Don't pull routes"); ?></td>
<td>
<input name="route_no_pull" type="checkbox" value="yes" <?=!empty($pconfig['route_no_pull']) ? "checked=\"checked\"" : "" ;?> />
<div class="hidden" data-for="help_for_route_no_pull">
<?=gettext("This option effectively bars the server from adding routes to the client's routing table, however note that this option still allows the server to set the TCP/IP properties of the client's TUN/TAP interface"); ?>.
</div>
</td>
</tr>
<tr id="chkboxRouteNoExec">
<td><a id="help_for_route_no_exec" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Don't add/remove routes"); ?></td>
<td>
<input name="route_no_exec" type="checkbox" value="yes" <?=!empty($pconfig['route_no_exec']) ? "checked=\"checked\"" : "" ;?> />
<div class="hidden" data-for="help_for_route_no_exec">
<?= gettext('Do not add or remove routes automatically. Instead pass routes to "--route-up" script using environmental variables.') ?>
</div>
</td>
</tr>
</table>
</div>
</div>
</section>
<section class="col-xs-12">
<div class="tab-content content-box col-xs-12">
<div class="table-responsive">
<table class="table table-striped opnsense_standard_table_form">
<tr>
<td colspan="2"><strong><?=gettext("Advanced configuration"); ?></strong></td>
</tr>
<tr>
<td style="width:22%"><a id="help_for_custom_options" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Advanced"); ?></td>
<td style="width:78%">
<textarea rows="6" cols="78" name="custom_options" id="custom_options"><?=$pconfig['custom_options'];?></textarea>
<?=gettext("This option will be removed in the future due to being insecure by nature. In the mean time only full administrators are allowed to change this setting.");?>
<div class="hidden" data-for="help_for_custom_options">
<?=gettext("Enter any additional options you would like to add to the configuration file here."); ?>
</div>
</td>
</tr>
<tr id="comboboxVerbosityLevel">
<td><a id="help_for_verbosity_level" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Verbosity level");?></td>
<td>
<select name="verbosity_level" class="form-control">
<?php
foreach (openvpn_verbosity_level() as $verb_value => $verb_desc):
$selected = '';
if ($pconfig['verbosity_level'] == $verb_value) {
$selected = 'selected="selected"';
}
?>
<option value="<?=$verb_value; ?>" <?=$selected; ?>><?=$verb_desc;?></option>
<?php endforeach; ?>
</select>
<div class="hidden" data-for="help_for_verbosity_level">
<?=gettext("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 />
<?=sprintf(gettext("%snone%s -- No output except fatal errors."),'<strong>','</strong>') ?> <br />
<?=sprintf(gettext("%sdefault%s-%s4%s -- Normal usage range."),'<strong>','</strong>','<strong>','</strong>'); ?> <br />
<?=sprintf(gettext("%s5%s -- 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."),'<strong>','</strong>') ?> <br />
<?=sprintf(gettext("%s6%s-%s11%s -- Debug info range."),'<strong>','</strong>','<strong>','</strong>') ?>
</div>
</td>
</tr>
</table>
</div>
</div>
</section>
<section class="col-xs-12">
<div class="tab-content content-box col-xs-12">
<div class="table-responsive">
<table class="table table-striped opnsense_standard_table_form">
<tr>
<td> </td>
<td style="width:78%">
<input name="save" type="submit" class="btn btn-primary" value="<?=html_safe(gettext('Save')); ?>" />
<input name="act" type="hidden" value="<?=$act;?>" />
<?php
if (isset($id) && $a_client[$id]) :?>
<input name="id" type="hidden" value="<?=htmlspecialchars($id);?>" />
<?php
endif; ?>
</td>
</tr>
</table>
</div>
</div>
</section>
</form>
<?php
else:?>
<form method="post" name="iform2" id="iform2">
<section class="col-xs-12">
<div class="tab-content content-box col-xs-12">
<input type="hidden" id="id" name="id" value="" />
<input type="hidden" id="action" name="act" value="" />
<table class="table table-striped">
<thead>
<tr>
<td></td>
<td><?=gettext("Protocol"); ?></td>
<td><?=gettext("Server"); ?></td>
<td><?=gettext("Description"); ?></td>
<td class="text-nowrap">
<a href="vpn_openvpn_client.php?act=new" class="btn btn-primary btn-xs" data-toggle="tooltip" title="<?= html_safe(gettext('Add')) ?>">
<i class="fa fa-plus fa-fw"></i>
</a>
<a data-id="<?= count($a_client) ?>" data-toggle="tooltip" title="<?=gettext("Move selected items to end");?>" class="act_move btn btn-default btn-xs">
<span class="fa fa-arrow-down fa-fw"></span>
</a>
<a data-id="x" title="<?=gettext("delete selected rules"); ?>" data-toggle="tooltip" class="act_delete btn btn-default btn-xs">
<span class="fa fa-trash-o fa-fw"></span>
</a>
</td>
</tr>
</thead>
<tbody>
<?php
$i = 0;
foreach ($a_client as $client) :
$server_addr_a = explode(',', $client['server_addr']);
$server_port_a = explode(',', $client['server_port']);
$server = array();
foreach (array_keys($server_addr_a) as $j) {
$server[] = "{$server_addr_a[$j]}:{$server_port_a[$j]}";
} ?>
<tr>
<td>
<input type="checkbox" name="rule[]" value="<?=$i;?>" />
<a href="#" class="act_toggle" data-id="<?=$i;?>" data-toggle="tooltip" title="<?=(empty($client['disable'])) ? gettext("Disable") : gettext("Enable");?>">
<span class="fa fa-play fa-fw <?=(empty($client['disable'])) ? "text-success" : "text-muted";?>"></span>
</a>
</td>
<td><?= htmlspecialchars($client['protocol']) ?></td>
<td><?= htmlspecialchars(implode(', ', $server)) ?></td>
<td><?= htmlspecialchars($client['description']) ?></td>
<td class="text-nowrap">
<a data-id="<?=$i;?>" data-toggle="tooltip" title="<?=gettext("Move selected before this item");?>" class="act_move btn btn-default btn-xs">
<span class="fa fa-arrow-left fa-fw"></span>
</a>
<a href="vpn_openvpn_client.php?act=edit&id=<?=$i;?>" class="btn btn-default btn-xs">
<span class="fa fa-pencil fa-fw"></span>
</a>
<a data-id="<?=$i;?>" title="<?=gettext("delete client"); ?>" class="act_delete btn btn-default btn-xs">
<span class="fa fa-trash-o fa-fw"></span>
</a>
<a href="vpn_openvpn_client.php?act=new&dup=<?=$i;?>" class="btn btn-default btn-xs" data-toggle="tooltip" title="<?=gettext("clone client");?>">
<span class="fa fa-clone fa-fw"></span>
</a>
</td>
</tr>
<?php
$i++;
endforeach;?>
</tbody>
</table>
</div>
</section>
</form>
<?php
endif; ?>
</div>
</div>
</section>
<?php include("foot.inc"); ?>