summaryrefslogtreecommitdiffstats
path: root/etc/inc
diff options
context:
space:
mode:
authorgnhb <gnoahb@gmail.com>2010-05-29 10:11:33 +0700
committergnhb <gnoahb@gmail.com>2010-05-29 10:11:33 +0700
commit68f0c07a0adab08b3fba2bc6749215e10d4d43ce (patch)
tree4c698cbfdbae5b2f37f6cb8f6051efde92cba840 /etc/inc
parentd6366dd7beefba982c1e04a1f43f20cfe306808a (diff)
parent40e18a72af0d2633251ef197ff6311325ce4eae9 (diff)
downloadpfsense-68f0c07a0adab08b3fba2bc6749215e10d4d43ce.zip
pfsense-68f0c07a0adab08b3fba2bc6749215e10d4d43ce.tar.gz
Merge branch 'master' of http://gitweb.pfsense.org/pfsense/mainline into mlppp
Conflicts: etc/inc/pfsense-utils.inc usr/local/www/interfaces.php
Diffstat (limited to 'etc/inc')
-rw-r--r--etc/inc/auth.inc31
-rw-r--r--etc/inc/captiveportal.inc74
-rw-r--r--etc/inc/config.inc2
-rw-r--r--etc/inc/dyndns.class2
-rw-r--r--etc/inc/filter.inc6
-rw-r--r--etc/inc/gwlb.inc12
-rw-r--r--etc/inc/interfaces.inc17
-rw-r--r--etc/inc/pfsense-utils.inc86
-rw-r--r--etc/inc/pkg-utils.inc2
-rw-r--r--etc/inc/util.inc3
-rw-r--r--etc/inc/vpn.inc9
-rw-r--r--etc/inc/xmlparse.inc22
12 files changed, 182 insertions, 84 deletions
diff --git a/etc/inc/auth.inc b/etc/inc/auth.inc
index 826582f..2e580b1 100644
--- a/etc/inc/auth.inc
+++ b/etc/inc/auth.inc
@@ -340,10 +340,10 @@ function local_user_del($user) {
if($debug)
log_error("Running: {$cmd}");
- $fd = popen($cmd, "w");
- fwrite($fd, $user['password']);
- pclose($fd);
+ mwexec($cmd);
+ /* Delete user from groups needs a call to write_config() */
+ local_group_del_user($user);
}
function local_user_set_password(& $user, $password) {
@@ -421,6 +421,22 @@ function local_user_set_groups($user, $new_groups = NULL ) {
local_group_set($group);
}
+function local_group_del_user($user) {
+ global $config;
+
+ if (!is_array($config['system']['group']))
+ return;
+
+ foreach ($config['system']['group'] as $group) {
+ if (is_array($group['member'])) {
+ foreach ($group['member'] as $idx => $uid) {
+ if ($user['uid'] == $uid)
+ unset($config['system']['group']['member'][$idx]);
+ }
+ }
+ }
+}
+
function local_group_set($group, $reset = false) {
global $debug;
@@ -446,9 +462,7 @@ function local_group_set($group, $reset = false) {
if($debug)
log_error("Running: {$cmd}");
- $fd = popen($cmd, "w");
- fwrite($fd, $user['password']);
- pclose($fd);
+ mwexec($cmd);
}
@@ -460,10 +474,7 @@ function local_group_del($group) {
if($debug)
log_error("Running: {$cmd}");
- $fd = popen($cmd, "w");
- fwrite($fd, $user['password']);
- pclose($fd);
-
+ mwexec($cmd);
}
function ldap_test_connection($authcfg) {
diff --git a/etc/inc/captiveportal.inc b/etc/inc/captiveportal.inc
index 28dab57..885040b 100644
--- a/etc/inc/captiveportal.inc
+++ b/etc/inc/captiveportal.inc
@@ -155,7 +155,7 @@ function captiveportal_configure() {
<body>
<center>
<h2>{$g['product_name']} captive portal</h2>
-Welcome to the {$g['product_name']} Captive Portal! This is the default page since a custom page has not been defined.
+Welcome to the {$g['product_name']} Captive Portal!
<p>
<form method="post" action="\$PORTAL_ACTION\$">
<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
@@ -181,6 +181,14 @@ EOD;
$fd = @fopen("{$g['varetc_path']}/captiveportal.html", "w");
if ($fd) {
+ // Special case handling. Convert so that we can pass this page
+ // through the PHP interpreter later without clobbering the vars.
+ $htmltext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $htmltext);
+ $htmltext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $htmltext);
+ $htmltext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $htmltext);
+ $htmltext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $htmltext);
+ $htmltext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $htmltext);
+ $htmltext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $htmltext);
fwrite($fd, $htmltext);
fclose($fd);
}
@@ -210,10 +218,62 @@ EOD;
$fd = @fopen("{$g['varetc_path']}/captiveportal-error.html", "w");
if ($fd) {
+ // Special case handling. Convert so that we can pass this page
+ // through the PHP interpreter later without clobbering the vars.
+ $errtext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $errtext);
+ $errtext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $errtext);
+ $errtext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $errtext);
+ $errtext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $errtext);
+ $errtext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $errtext);
+ $errtext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $errtext);
fwrite($fd, $errtext);
fclose($fd);
}
+ /* write error page */
+ if ($config['captiveportal']['page']['logouttext'])
+ $logouttext = base64_decode($config['captiveportal']['page']['logouttext']);
+ else {
+ /* example page */
+ $logouttext = <<<EOD
+<HTML>
+<HEAD><TITLE>Redirecting...</TITLE></HEAD>
+<BODY>
+<SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
+<B>Redirecting to <A HREF="{$my_redirurl}">{$my_redirurl}</A>...</B>
+</SPAN>
+<SCRIPT LANGUAGE="JavaScript">
+<!--
+LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
+if (LogoutWin) {
+ LogoutWin.document.write('<HTML>');
+ LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ;
+ LogoutWin.document.write('<BODY BGCOLOR="#435370">');
+ LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
+ LogoutWin.document.write('<B>Click the button below to disconnect</B><P>');
+ LogoutWin.document.write('<FORM METHOD="POST" ACTION="{$logouturl}">');
+ LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="{$sessionid}">');
+ LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">');
+ LogoutWin.document.write('</FORM>');
+ LogoutWin.document.write('</DIV></BODY>');
+ LogoutWin.document.write('</HTML>');
+ LogoutWin.document.close();
+}
+
+document.location.href="{$my_redirurl}";
+-->
+</SCRIPT>
+</BODY>
+</HTML>
+
+EOD;
+ }
+
+ $fd = @fopen("{$g['varetc_path']}/captiveportal-logout.html", "w");
+ if ($fd) {
+ fwrite($fd, $logouttext);
+ fclose($fd);
+ }
/* write elements */
captiveportal_write_elements();
@@ -769,6 +829,18 @@ function captiveportal_passthrumac_configure($lock = false) {
return $rules;
}
+function captiveportal_passthrumac_findbyname($username) {
+ global $config;
+
+ if (is_array($config['captiveportal']['passthrumac'])) {
+ foreach ($config['captiveportal']['passthrumac'] as $macent) {
+ if ($macent['username'] == $username)
+ return $macent;
+ }
+ }
+ return NULL;
+}
+
/*
* table (3=IN)/(4=OUT) hold allowed ip's without bw limits
* table (5=IN)/(6=OUT) hold allowed ip's with bw limit.
diff --git a/etc/inc/config.inc b/etc/inc/config.inc
index 5ec98be..aa5fb69 100644
--- a/etc/inc/config.inc
+++ b/etc/inc/config.inc
@@ -184,7 +184,7 @@ else if ($g['booting'] and !file_exists($g['cf_conf_path'] . "/config.xml") ) {
/* write out an fstab */
$fd = fopen("{$g['etc_path']}/fstab", "w");
- $fstab = "/dev/{$cfgpartition} {$g['cf_path']} {$cfgfstype} ro 1 1\n";
+ $fstab = "/dev/{$cfgpartition} {$g['cf_path']} {$cfgfstype} ro,noatime 1 1\n";
$fstab .= "proc /proc procfs rw 0 0\n";
fwrite($fd, $fstab);
diff --git a/etc/inc/dyndns.class b/etc/inc/dyndns.class
index bf442fb..2e90386 100644
--- a/etc/inc/dyndns.class
+++ b/etc/inc/dyndns.class
@@ -763,7 +763,7 @@
$error = 'phpDynDNS: (ERROR!) No Update URL Provided.';
break;
case 10:
- $error = 'phpDynDNS: No Change In My IP Address and/or 25 Days Has Not Past. Not Updating Dynamic DNS Entry.';
+ $error = 'phpDynDNS: No change in my IP address and/or 25 days has not passed. Not updating dynamic DNS entry.';
break;
default:
$error = "phpDynDNS: (ERROR!) Unknown Response.";
diff --git a/etc/inc/filter.inc b/etc/inc/filter.inc
index f01faee..ee9317e 100644
--- a/etc/inc/filter.inc
+++ b/etc/inc/filter.inc
@@ -142,11 +142,11 @@ function filter_configure() {
function delete_states_for_down_gateways() {
global $config;
- $a_gateways = return_gateways_array();
+ $a_gateways = return_gateways_status();
foreach ($a_gateways as $gateway) {
- if($gateway['monitor'] == "down") {
+ if ($gateway['status'] == "down") {
$int_ip = get_interface_ip($gateway['interface']);
- if($int_ip)
+ if(is_ipaddr($int_ip))
mwexec("/sbin/pfctl -b {$int_ip}");
}
}
diff --git a/etc/inc/gwlb.inc b/etc/inc/gwlb.inc
index 2411892..846d33d 100644
--- a/etc/inc/gwlb.inc
+++ b/etc/inc/gwlb.inc
@@ -148,7 +148,7 @@ EOD;
foreach($gateways_arr as $name => $gateway) {
$gwref = $a_gateway_item[$gateway['attribute']];
/* for dynamic gateways without an IP address we subtitute a local one */
- if((is_numeric($gateway['attribute'])) && is_ipaddr($gwref['monitor'])) {
+ if(is_ipaddr($gwref['monitor'])) {
$gateway['monitor'] = $gwref['monitor'];
} else {
if ($gateway['gateway'] == "dynamic") {
@@ -477,11 +477,11 @@ function dhclient_update_gateway_groups_defaultroute($interface = "wan") {
function lookup_gateway_ip_by_name($name) {
global $config;
- $gateways_arr = return_gateways_array();
- if (!empty($gateways_arr[$name])) {
- $gatewayip = $gateway['gateway'];
- //$interfacegw = $gateway['interface'];
- return ($gatewayip);
+ if(is_array($config['gateways']['gateway_item'])) {
+ foreach($config['gateways']['gateway_item'] as $gateway) {
+ if($gateway['name'] == $name)
+ return ($gateway['gateway']);
+ }
} else
return (false);
}
diff --git a/etc/inc/interfaces.inc b/etc/inc/interfaces.inc
index 976e103..ed1eaca 100644
--- a/etc/inc/interfaces.inc
+++ b/etc/inc/interfaces.inc
@@ -2188,7 +2188,7 @@ EOD;
/* find which clones are up and bring them down */
$clones_up = array();
foreach ($clone_list as $clone_if) {
- $clone_status = pfSense_get_interface_stats($clone_if);
+ $clone_status = pfSense_get_interface_addresses($clone_if);
if ($clone_status['status'] == 'up') {
$clones_up[] = $clone_if;
mwexec("{$ifconfig} " . escapeshellarg($clone_if) . " down");
@@ -2313,10 +2313,25 @@ function interface_configure($interface = "wan", $reloadall = false) {
mwexec("/sbin/ifconfig " . escapeshellarg($realif) .
" " . escapeshellarg($wancfg['ipaddr'] . "/" .
$wancfg['subnet']));
+ } else if (substr($realif, 0, 3) == "gre") {
+ if (is_array($config['gres']['gre'])) {
+ foreach ($config['gres']['gre'] as $gre)
+ if ($gre['greif'] == $realif)
+ interface_gre_configure($gre);
+ }
+ } else if (substr($realif, 0, 3) == "gif") {
+ if (is_array($config['gifs']['gif'])) {
+ foreach ($config['gifs']['gif'] as $gif)
+ if($gif['gifif'] == $interface)
+ interface_gif_configure($gif);
+ }
+ } else if (substr($realif, 0, 4) == "ovpn") {
+ /* XXX: Should be done anything?! */
}
if (is_ipaddr($wancfg['gateway']))
file_put_contents("{$g['tmp_path']}/{$realif}_router", $wancfg['gateway']);
+ break;
}
if(does_interface_exist($wancfg['if']))
diff --git a/etc/inc/pfsense-utils.inc b/etc/inc/pfsense-utils.inc
index b653010..d77b320 100644
--- a/etc/inc/pfsense-utils.inc
+++ b/etc/inc/pfsense-utils.inc
@@ -642,6 +642,8 @@ function restore_config_section($section, $new_contents) {
if ($section_xml != -1)
$config[$section] = &$section_xml;
@unlink($g['tmp_path'] . "/tmpxml");
+ if(file_exists("{$g['tmp_path']}/config.cache"))
+ unlink("{$g['tmp_path']}/config.cache");
write_config("Restored {$section} of config file (maybe from CARP partner)");
conf_mount_ro();
return;
@@ -1183,49 +1185,30 @@ function get_ppp_uptime($port){
//returns interface information
function get_interface_info($ifdescr) {
- global $config, $linkinfo, $netstatrninfo, $g;
+ global $config, $g;
$ifinfo = array();
- /* if list */
- $iflist = get_configured_interface_with_descr(false,true);
-
- $found = false;
- foreach ($iflist as $if => $ifname) {
- if ($ifdescr == $if || $ifdescr == $ifname) {
- $ifinfo['hwif'] = $config['interfaces'][$if]['if'];
- $ifinfo['if'] = get_real_interface($if);
- $found = true;
- break;
- }
- }
- if ($found == false)
+ if (empty($config['interfaces'][$ifdescr]))
return;
+ $ifinfo['hwif'] = $config['interfaces'][$if]['if'];
+ $ifinfo['if'] = get_real_interface($ifdescr);
- /* run netstat to determine link info */
-
- unset($linkinfo);
$chkif = $ifinfo['if'];
-
- exec("/usr/bin/netstat -I {$chkif} -nWb -f link", $linkinfo);
- $linkinfo = preg_split("/\s+/", $linkinfo[1]);
-
$ifinfotmp = pfSense_get_interface_addresses($chkif);
$ifinfo['status'] = $ifinfotmp['status'];
+ if (empty($ifinfo['status']))
+ $ifinfo['status'] = "down";
$ifinfo['macaddr'] = $ifinfotmp['macaddr'];
$ifinfo['ipaddr'] = $ifinfotmp['ipaddr'];
$ifinfo['subnet'] = $ifinfotmp['subnet'];
if (isset($ifinfotmp['link0']))
$link0 = "down";
-
-
- if (preg_match("/^enc|^tun|^ppp|^pptp|^ovpn/i", $chkif)) {
- $ifinfo['inpkts'] = $linkinfo[3];
- $ifinfo['outpkts'] = $linkinfo[6];
- } else {
- $ifinfo['inerrs'] = $linkinfo[5];
- $ifinfo['outerrs'] = $linkinfo[9];
- $ifinfo['collisions'] = $linkinfo[11];
- }
+ $ifinfotmp = pfSense_get_interface_stats($chkif);
+ $ifinfo['inpkts'] = $ifinfotmp['inpkts'];
+ $ifinfo['outpkts'] = $ifinfotmp['outpkts'];
+ $ifinfo['inerrs'] = $ifinfotmp['inerrs'];
+ $ifinfo['outerrs'] = $ifinfotmp['outerrs'];
+ $ifinfo['collisions'] = $ifinfotmp['collisions'];
/* Use pfctl for non wrapping 64 bit counters */
/* Pass */
@@ -1260,10 +1243,6 @@ function get_interface_info($ifdescr) {
$ifconfiginfo = "";
- unset($linkinfo);
- exec("/usr/bin/netstat -I " . $ifinfo['if'] . " -nWb -f link", $linkinfo);
- $linkinfo = preg_split("/\s+/", $linkinfo[1]);
-
switch ($config['interfaces'][$ifdescr]['ipaddr']) {
/* DHCP? -> see if dhclient is up */
case "dhcp":
@@ -1277,33 +1256,28 @@ function get_interface_info($ifdescr) {
break;
/* PPPoE interface? -> get status from virtual interface */
case "pppoe":
- if ("{$ifinfo['if']}*" == $linkinfo[0])
- $ifinfo['pppoelink'] = "down";
- else if ($ifinfo['if'] == $linkinfo[0] && !isset($link0))
- /* get PPPoE link status for dial on demand */
- $ifinfo['pppoelink'] = "up";
+ if ($ifinfo['status'] == "up" && !isset($link0))
+ /* get PPPoE link status for dial on demand */
+ $ifinfo['pppoelink'] = "up";
else
$ifinfo['pppoelink'] = "down";
break;
/* PPTP interface? -> get status from virtual interface */
case "pptp":
- if ("{$ifinfo['if']}*" == $linkinfo[0])
- $ifinfo['pptplink'] = "down";
- else if ($ifinfo['if'] == $linkinfo[0] && !isset($link0))
- /* get PPTP link status for dial on demand */
- $ifinfo['pptplink'] = "up";
+ if ($ifinfo['status'] == "up" && !isset($link0))
+ /* get PPTP link status for dial on demand */
+ $ifinfo['pptplink'] = "up";
else
$ifinfo['pptplink'] = "down";
break;
/* PPP interface? -> get uptime for this session and cumulative uptime from the persistant log file in conf */
case "ppp":
- if ("{$ifinfo['if']}*" == $linkinfo[0])
- $ifinfo['ppplink'] = "down";
- else if ($ifinfo['if'] == $linkinfo[0])
+ if ($ifinfo['status'] == "up")
$ifinfo['ppplink'] = "up";
else
$ifinfo['ppplink'] = "down" ;
+
if (empty($ifinfo['status']))
$ifinfo['status'] = "down";
@@ -2000,8 +1974,8 @@ function nanobsd_update_fstab($gslice, $complete_path, $oldufs, $newufs) {
if (!file_exists($fstabpath)) {
$fstab = <<<EOF
-/dev/ufs/{$gslice} / ufs ro 1 1
-/dev/ufs/cf /cf ufs ro 1 1
+/dev/ufs/{$gslice} / ufs ro,noatime 1 1
+/dev/ufs/cf /cf ufs ro,noatime 1 1
EOF;
if (file_put_contents($fstabpath, $fstab))
$status = true;
@@ -2045,4 +2019,16 @@ function nanobsd_detect_slice_info() {
$COMPLETE_BOOT_PATH="{$BOOT_DRIVE}s{$OLDSLICE}";
$BOOTFLASH="{$BOOT_DRIVE}s{$OLDSLICE}";
}
+
+function get_include_contents($filename) {
+ if (is_file($filename)) {
+ ob_start();
+ include $filename;
+ $contents = ob_get_contents();
+ ob_end_clean();
+ return $contents;
+ }
+ return false;
+}
+
?>
diff --git a/etc/inc/pkg-utils.inc b/etc/inc/pkg-utils.inc
index f8c187c..f06b98c 100644
--- a/etc/inc/pkg-utils.inc
+++ b/etc/inc/pkg-utils.inc
@@ -740,6 +740,8 @@ function delete_package($pkg, $pkgid) {
$pkg_info = $config['installedpackages']['package'][$pkgid];
$configfile = $pkg_info['configurationfile'];
+ if(empty($configfile))
+ return;
if(file_exists("/usr/local/pkg/" . $configfile)) {
$static_output .= "\nLoading package configuration $configfile... ";
update_output_window($static_output);
diff --git a/etc/inc/util.inc b/etc/inc/util.inc
index 0828bbf..092efde 100644
--- a/etc/inc/util.inc
+++ b/etc/inc/util.inc
@@ -676,7 +676,8 @@ function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "")
'tun',
'carp',
'lagg',
- 'vip'
+ 'vip',
+ 'ipfw'
);
}
switch($mode) {
diff --git a/etc/inc/vpn.inc b/etc/inc/vpn.inc
index c874a83..f38a0ff 100644
--- a/etc/inc/vpn.inc
+++ b/etc/inc/vpn.inc
@@ -291,10 +291,17 @@ function vpn_ipsec_configure($ipchg = false)
}
}
+ /* Add user PSKs */
+ foreach ($config['system']['user'] as $user) {
+ if (!empty($user['ipsecpsk'])) {
+ $pskconf .= "{$user['name']}\t{$user['ipsecpsk']}\n";
+ }
+ }
+
/* add PSKs for mobile clients */
if (is_array($ipseccfg['mobilekey'])) {
foreach ($ipseccfg['mobilekey'] as $key) {
- $pskconf .= "{$key['ident']} {$key['pre-shared-key']}\n";
+ $pskconf .= "{$key['ident']}\t{$key['pre-shared-key']}\n";
}
}
diff --git a/etc/inc/xmlparse.inc b/etc/inc/xmlparse.inc
index c19a3ae..59b0eb4 100644
--- a/etc/inc/xmlparse.inc
+++ b/etc/inc/xmlparse.inc
@@ -246,27 +246,31 @@ function dump_xml_config_sub($arr, $indent) {
if (in_array(strtolower($ent), $listtags)) {
foreach ($val as $cval) {
if (is_array($cval)) {
- $xmlconfig .= str_repeat("\t", $indent);
- $xmlconfig .= "<$ent>\n";
- $xmlconfig .= dump_xml_config_sub($cval, $indent + 1);
- $xmlconfig .= str_repeat("\t", $indent);
- $xmlconfig .= "</$ent>\n";
+ if (empty($cval)) {
+ $xmlconfig .= str_repeat("\t", $indent);
+ $xmlconfig .= "<$ent/>\n";
+ } else {
+ $xmlconfig .= str_repeat("\t", $indent);
+ $xmlconfig .= "<$ent>\n";
+ $xmlconfig .= dump_xml_config_sub($cval, $indent + 1);
+ $xmlconfig .= str_repeat("\t", $indent);
+ $xmlconfig .= "</$ent>\n";
+ }
} else {
- $xmlconfig .= str_repeat("\t", $indent);
if($cval === false) continue;
- if(($cval === true) || ($cval === "")) {
+ $xmlconfig .= str_repeat("\t", $indent);
+ if((is_bool($cval) && $cval == true) || ($cval === "")) {
$xmlconfig .= "<$ent/>\n";
} else if (substr($ent, 0, 5) == "descr") {
$xmlconfig .= "<$ent><![CDATA[" . htmlentities($cval) . "]]></$ent>\n";
} else {
$xmlconfig .= "<$ent>" . htmlentities($cval) . "</$ent>\n";
+ }
}
}
- }
} else if (empty($val)) {
$xmlconfig .= str_repeat("\t", $indent);
$xmlconfig .= "<$ent/>\n";
- $xmlconfig .= str_repeat("\t", $indent);
} else {
/* it's an array */
$xmlconfig .= str_repeat("\t", $indent);
OpenPOWER on IntegriCloud