diff options
Diffstat (limited to 'config/haproxy-devel')
-rw-r--r-- | config/haproxy-devel/haproxy.inc | 50 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy.xml | 5 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_files.php | 176 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_htmllist.inc | 50 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_listeners_edit.php | 5 | ||||
-rw-r--r-- | config/haproxy-devel/haproxy_pool_edit.php | 40 | ||||
-rw-r--r-- | config/haproxy-devel/pkg_haproxy_tabs.inc | 2 |
7 files changed, 319 insertions, 9 deletions
diff --git a/config/haproxy-devel/haproxy.inc b/config/haproxy-devel/haproxy.inc index 5b56595a..c839cd4c 100644 --- a/config/haproxy-devel/haproxy.inc +++ b/config/haproxy-devel/haproxy.inc @@ -196,6 +196,19 @@ $a_sticky_type['stick_rdp_cookie'] = array('name' => 'Stick on RDP-cookie', 'descr' => "Uses a RDP-Cookie send by the mstsc client, note that not all clients send this.", 'cookiedescr' => 'EXAMPLE: msts or mstshash'); + +global $a_error; +$a_error = array(); +$a_error['200'] = array('descr' => "stats or monitoring requests"); +$a_error['400'] = array('descr' => "request invalid or too large"); +$a_error['401'] = array('descr' => "authentication is required to perform the action"); +$a_error['403'] = array('descr' => "request is forbidden"); +$a_error['408'] = array('descr' => "timeout before the request is complete"); +$a_error['500'] = array('descr' => "internal error"); +$a_error['502'] = array('descr' => "server response invalid or blocked"); +$a_error['503'] = array('descr' => "no server was available to handle the request"); +$a_error['504'] = array('descr' => "timeout before the server responds"); + if(!function_exists('group_ports')){ // function group_ports() is present in pfSense 2.2 in util.inc /* create ranges of sequential port numbers (200:215) and remove duplicates */ @@ -308,6 +321,20 @@ function haproxy_hostoralias_to_list($host_or_alias) { return $result; } +function haproxy_get_fileslist() { + // returns the files array with 'keys'. + $result = array(); + global $config; + // create a copy to not modify the original 'keyless' array + $a_files = $config['installedpackages']['haproxy']['files']['item']; + if (!is_array($a_files)) $a_files = array(); + foreach($a_files as $file) { + $key = $file['name']; + $result[$key] = $file; + } + return $result; +} + function haproxy_custom_php_deinstall_command() { global $static_output; $static_output .= "HAProxy, running haproxy_custom_php_deinstall_command()\n"; @@ -603,7 +630,7 @@ function haproxy_find_acl($name) { function write_backend($configpath, $fd, $name, $pool, $frontend) { if(!is_array($pool['ha_servers']['item']) && !$pool['stats_enabled']=='yes') return; - global $a_checktypes, $a_cookiemode; + global $a_checktypes, $a_cookiemode, $a_files, $a_error; $a_servers = &$pool['ha_servers']['item']; $frontendtype = $frontend['type']; @@ -667,6 +694,23 @@ function write_backend($configpath, $fd, $name, $pool, $frontend) { fwrite ($fd, "\tstats\t\t\tscope " . $scope_item . "\n"); } } + + if (is_arrayset($pool,'errorfiles','item')) { + foreach($pool['errorfiles']['item'] as $errorfile) { + if (!is_array($a_files))// load only once + $a_files = haproxy_get_fileslist(); + $file = $errorfile['errorfile']; + $errorcodes = explode(",",$errorfile['errorcode']); + foreach($errorcodes as $errorcode) { + $filename = "$configpath/errorfile_{$name}_{$errorcode}_{$file}"; + $content = base64_decode($a_files[$file]['content']); + $content = str_replace('{errormsg}', $a_error[$errorcode]['descr'], $content); + $content = str_replace('{errorcode}', $errorcode, $content); + file_put_contents($filename, $content); + fwrite ($fd, "\terrorfile\t\t\t" . $errorcode ." " . $filename . "\n"); + } + } + } } switch($pool["persist_sticky_type"]) { @@ -1668,7 +1712,7 @@ function get_frontend_ipport($frontend, $userfriendly=false) { $mainfrontend = get_primaryfrontend($frontend); $newline = ""; $result = array(); - if (!isset($mainfrontend)) + if (!is_arrayset($mainfrontend,"a_extaddr","item")) return $result; foreach($mainfrontend['a_extaddr']['item'] as $extaddr) { if ($extaddr['extaddr'] == 'custom'){ @@ -1725,6 +1769,8 @@ function get_haproxy_frontends($excludeitem="") { global $config; $a_frontend = &$config['installedpackages']['haproxy']['ha_backends']['item']; $result = array(); + if(!is_array($a_frontend)) + return $result; foreach($a_frontend as &$frontend) { if ($frontend['secondary']) diff --git a/config/haproxy-devel/haproxy.xml b/config/haproxy-devel/haproxy.xml index acd934a7..6156c174 100644 --- a/config/haproxy-devel/haproxy.xml +++ b/config/haproxy-devel/haproxy.xml @@ -90,6 +90,11 @@ <additional_files_needed> <prefix>/usr/local/www/</prefix> <chmod>077</chmod> + <item>https://packages.pfsense.org/packages/config/haproxy-devel/haproxy_files.php</item> + </additional_files_needed> + <additional_files_needed> + <prefix>/usr/local/www/</prefix> + <chmod>077</chmod> <item>https://packages.pfsense.org/packages/config/haproxy-devel/haproxy_pools.php</item> </additional_files_needed> <additional_files_needed> diff --git a/config/haproxy-devel/haproxy_files.php b/config/haproxy-devel/haproxy_files.php new file mode 100644 index 00000000..4946a7be --- /dev/null +++ b/config/haproxy-devel/haproxy_files.php @@ -0,0 +1,176 @@ +<?php +/* $Id: load_balancer_virtual_server.php,v 1.6.2.1 2006/01/02 23:46:24 sullrich Exp $ */ +/* + haproxy_pools.php + part of pfSense (https://www.pfsense.org/) + Copyright (C) 2014 PiBa-NL + 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. +*/ +$shortcut_section = "haproxy"; +require_once("guiconfig.inc"); +require_once("haproxy.inc"); +require_once("pkg_haproxy_tabs.inc"); +require_once("haproxy_htmllist.inc"); + +$a_files = &$config['installedpackages']['haproxy']['files']['item']; +if (!is_array($a_files)) $a_files = array(); +$a_pools = &$config['installedpackages']['haproxy']['ha_pools']['item']; +if (!is_array($a_pools)) $a_pools = array(); + + +$fields_files = array(); +$fields_files[0]['name']="name"; +$fields_files[0]['columnheader']="Name"; +$fields_files[0]['colwidth']="30%"; +$fields_files[0]['type']="textbox"; +$fields_files[0]['size']="20"; + +$fields_files[1]['name']="content"; +$fields_files[1]['columnheader']="content"; +$fields_files[1]['colwidth']="70%"; +$fields_files[1]['type']="textarea"; +$fields_files[1]['size']="70"; + +$fileslist = new HaproxyHtmlList("table_files", $fields_files); +$fileslist->keyfield = "name"; + +if ($_POST) { + $pconfig = $_POST; + + if ($_POST['apply']) { + $result = haproxy_check_and_run($savemsg, true); + if ($result) + unlink_if_exists($d_haproxyconfdirty_path); + } else { + $a_files = $fileslist->haproxy_htmllist_get_values($fields_files); + $filedupcheck = array(); + + foreach($a_files as $key => $file) { + $name = $file['name']; + if (!preg_match("/^[a-zA-Z][a-zA-Z0-9\.\-_]*$/", $file['name'])) + $input_errors[] = "The field 'Name' (".htmlspecialchars($file['name']).") contains invalid characters. Use only: a-zA-Z0-9.-_ and start with a letter"; + if (isset($filedupcheck[$name])) + $input_errors[] = "Duplicate names are not allowed: " . htmlspecialchars($name); + $filedupcheck[$name] = true; + } + + // replace references in backends to renamed 'files' + foreach($a_pools as &$backend) { + if (is_arrayset($backend,'errorfiles','item')) + foreach($backend['errorfiles']['item'] as &$errorfile) { + $found = false; + foreach($a_files as $key => $file) { + if ($errorfile['errorfile'] == $key) { + $errorfile['errorfile'] = $file['name']; + $found = true; + } + } + if (!$found) + $input_errors[] = "Errorfile marked for deletion: " . $errorfile['errorfile'] . " which is used in backend " . $backend['name']; + } + } + if (!$input_errors) { + // save config when no errors found + touch($d_haproxyconfdirty_path); + write_config($changedesc); + header("Location: haproxy_files.php"); + exit; + } + } +} + +$pf_version=substr(trim(file_get_contents("/etc/version")),0,3); + +$pgtitle = "Services: HAProxy: Files"; +include("head.inc"); + +?> +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> +<form action="haproxy_files.php" method="post"> +<?php if ($input_errors) print_input_errors($input_errors); ?> +<?php if ($savemsg) print_info_box($savemsg); ?> +<?php if (file_exists($d_haproxyconfdirty_path)): ?> +<?php print_info_box_np("The haproxy configuration has been changed.<br/>You must apply the changes in order for them to take effect.");?><br/> +<?php endif; ?> +<table width="100%" border="0" cellpadding="0" cellspacing="0"> + <tr><td class="tabnavtbl"> + <?php + haproxy_display_top_tabs_active($haproxy_tab_array['haproxy'], "files"); + ?> + </td></tr> + <tr> + <td> + <div id="mainarea"> + <table class="tabcont" width="100%" border="0" cellpadding="0" cellspacing="0"> + <tr> + <td> + Files can be used for errorfiles, that can return custom error pages in + case haproxy reports a error (like no available backend). The content needs + to be less than the buffer size which is typically 8kb. + There are 2 possible variables to use inside the template: + Put these variables in the content of the errorfile templates and they will be replaced by the actual errorcode / message. (include the curly braces around the text)<br/> + <b>{errorcode}</b> this represents the errorcode<br/> + <b>{errormsg}</b> this represents the human readable error<br/> + </td> + </tr> + <tr> + <td> + + </td> + </tr> + <tr> + <td> + <? + $counter=0; + $fileslist->Draw($a_files); + ?> + </td> + </tr> + <tr> + <td> + + </td> + </tr> + <tr> + <td width="78%"> + <input name="Submit" type="submit" class="formbtn" value="Save" onClick="enable_change(true)" /> + </td> + </tr> + </table> + </div> + </table> + </form> +<script type="text/javascript"> + totalrows = <?php echo $counter; ?>; +<? + phparray_to_javascriptarray($fields_files,"fields_files",Array('/*','/*/name','/*/type','/*/size','/*/items','/*/items/*','/*/items/*/*','/*/items/*/*/name')); +?> +</script> + +<?php +haproxy_htmllist_js(); +include("fend.inc"); ?> +</body> +</html> diff --git a/config/haproxy-devel/haproxy_htmllist.inc b/config/haproxy-devel/haproxy_htmllist.inc index 98f7ab94..f873028e 100644 --- a/config/haproxy-devel/haproxy_htmllist.inc +++ b/config/haproxy-devel/haproxy_htmllist.inc @@ -46,6 +46,7 @@ class HaproxyHtmlList private $fields = array(); public $editmode = false; public $fields_details = null; + public $keyfield = ""; public function HaproxyHtmlList($tablename, $fields){ $this->tablename = $tablename; @@ -64,10 +65,25 @@ class HaproxyHtmlList foreach($this->fields as $item){ $itemname = $item['name']; $value[$itemname] = $_POST[$itemname.$x]; + if ($item['type'] == 'textarea') + $value[$itemname] = base64_encode($value[$itemname]); $add_item |= isset($_POST[$itemname.$x]); } - if ($add_item) - $values[] = $value; + if ($add_item) { + if ($this->keyfield != "") { + if (isset($_POST[$this->tablename."_key".$x])) + $key = $_POST[$this->tablename."_key".$x]; + else + $key = $_POST[$this->keyfield.$x]; + + } else + $key = ""; + + if (isset($values[$key])) + $values[] = $value; + else + $values[$key] = $value; + } } return $values; } @@ -78,13 +94,17 @@ class HaproxyHtmlList if ($editable) { $itemtype = $item['type']; if ($itemtype == "select"){ - //updatevisibility() echo_html_select($itemnamenr, $item['items'], $itemvalue,"","html_listitem_change(\"{$this->tablename}\",\"{$itemname}\",\"{$counter}\",this);", "width:{$item['size']}"); } else if ($itemtype == "checkbox"){ $checked = $itemvalue=='yes' ? " checked" : ""; echo "<input onclick='html_listitem_change(\"{$this->tablename}\",\"{$itemname}\",\"{$counter}\",this);' name='$itemnamenr' id='$itemnamenr' type='checkbox'$checked value='yes' size='{$item['size']}' />"; } else + if ($itemtype == "textarea"){ + echo "<textarea name='$itemnamenr' id='$itemnamenr' type='text' cols='{$item['size']}' rows='10'>"; + echo htmlspecialchars(base64_decode($itemvalue)); + echo "</textarea>"; + } else echo "<input name='$itemnamenr' id='$itemnamenr' type='text' value='{$itemvalue}' size='{$item['size']}' />"; } else { if ($itemtype == "select"){ @@ -93,7 +113,10 @@ class HaproxyHtmlList if ($itemtype == "checkbox"){ echo $itemvalue=='yes' ? gettext('yes') : gettext('no'); } else - echo $itemvalue; + if ($itemtype == "textarea"){ + echo htmlspecialchars(base64_decode($itemvalue)); + } else + echo htmlspecialchars($itemvalue); } } @@ -108,7 +131,16 @@ class HaproxyHtmlList echo "<td width='5%' class=''></td> </tr>"; if (is_array($rowvalues)){ - foreach($rowvalues as $value){ + foreach($rowvalues as $keyid => $value){ + if ($this->keyfield != "") { + if (preg_match("/[^0-9]/", $keyid)) + $itemvalue = $keyid; + else + $itemvalue = $value[$this->keyfield]; + $key = "<input name='{$tablename}_key{$counter}' id='{$tablename}_key{$counter}' type='hidden' value='{$itemvalue}'>"; + } else + $key = ""; + if (!$editstate) { echo "<tr id='tr_view_$counter' ondblclick='editRow($counter); return false;' >"; $leftitem = true; @@ -123,6 +155,7 @@ class HaproxyHtmlList $this->haproxy_htmllist_drawcell($item, $itemvalue, false, $itemname, $counter); echo "</td>"; $leftitem = false; + } echo " <td class='list'> @@ -144,12 +177,13 @@ class HaproxyHtmlList foreach($items as $item){ $itemname = $item['name']; $itemvalue = $value[$itemname]; - echo "<td class='vtable'>"; + echo "<td class='vtable'>".$key; if (isset($item['customdrawcell'])) { $item['customdrawcell']($item, $itemvalue, true, $item['name'].$counter); } else $this->haproxy_htmllist_drawcell($item, $itemvalue, true, $itemname, $counter); echo "</td>"; + $key = ""; } echo " <td class='list'> @@ -288,6 +322,10 @@ function haproxy_htmllist_js(){ td.innerHTML="<input size='" + items[i]['size'] + "' name='" + items[i]['name'] + totalrows + "' id='" + items[i]['name'] + totalrows + "'><\/input> "; + } else if(items[i]['type'] == 'textarea') { + td.innerHTML="<textarea cols='" + items[i]['size'] + "' rows='30' name='" + items[i]['name'] + totalrows + + "' id='" + items[i]['name'] + totalrows + + "'><\/textarea> "; } else if(items[i]['type'] == 'select') { seltext = htmllist_get_select_options(tableId, items[i]['name']); td.innerHTML="<select style='width:" + items[i]['size'] + "' name='" + items[i]['name'] + totalrows + diff --git a/config/haproxy-devel/haproxy_listeners_edit.php b/config/haproxy-devel/haproxy_listeners_edit.php index bfc39f87..a818fcfb 100644 --- a/config/haproxy-devel/haproxy_listeners_edit.php +++ b/config/haproxy-devel/haproxy_listeners_edit.php @@ -63,6 +63,8 @@ if (!is_array($config['installedpackages']['haproxy']['ha_backends']['item'])) { $a_backend = &$config['installedpackages']['haproxy']['ha_backends']['item']; $a_pools = &$config['installedpackages']['haproxy']['ha_pools']['item']; +if (!is_array($a_pools)) + $a_pools = array(); uasort($a_pools, haproxy_compareByName); global $simplefields; @@ -772,7 +774,8 @@ $primaryfrontends = get_haproxy_frontends($excludefrontend); </td> </tr> <tr class="haproxy_ssloffloading_enabled haproxy_primary"> - <td class="vncell" colspan="2"><b>Client certificate verification options, leave this empty if you do want to ask for a client certificate</b></td> + <td class="vncell" colspan="2"><b>Client certificate verification options, leave this empty if you do not want to ask for a client certificate</b><br/> + The users that visit this site will need to load the client cert signed by the ca's listed below imported into their browser.</td> </tr> <tr class="haproxy_ssloffloading_enabled haproxy_primary"> <td width="22%" valign="top" class="vncell">Client verification CA certificates</td> diff --git a/config/haproxy-devel/haproxy_pool_edit.php b/config/haproxy-devel/haproxy_pool_edit.php index 30079847..5c7f66b9 100644 --- a/config/haproxy-devel/haproxy_pool_edit.php +++ b/config/haproxy-devel/haproxy_pool_edit.php @@ -42,6 +42,8 @@ if (!is_array($config['installedpackages']['haproxy']['ha_pools']['item'])) { $a_pools = &$config['installedpackages']['haproxy']['ha_pools']['item']; +$a_files = haproxy_get_fileslist(); + if (isset($_POST['id'])) $id = $_POST['id']; else @@ -172,6 +174,19 @@ $fields_servers_details[7]['colwidth']="15%"; $fields_servers_details[7]['type']="textbox"; $fields_servers_details[7]['size']="80"; +$fields_errorfile = array(); +$fields_errorfile[0]['name']="errorcode"; +$fields_errorfile[0]['columnheader']="errorcode(s)"; +$fields_errorfile[0]['colwidth']="15%"; +$fields_errorfile[0]['type']="textbox"; +$fields_errorfile[0]['size']="70px"; +$fields_errorfile[1]['name']="errorfile"; +$fields_errorfile[1]['columnheader']="Error Page"; +$fields_errorfile[1]['colwidth']="30%"; +$fields_errorfile[1]['type']="select"; +$fields_errorfile[1]['size']="170px"; +$fields_errorfile[1]['items']=&$a_files; + if (isset($id) && $a_pools[$id]) { $pconfig['advanced'] = base64_decode($a_pools[$id]['advanced']); $pconfig['advanced_backend'] = base64_decode($a_pools[$id]['advanced_backend']); @@ -179,6 +194,9 @@ if (isset($id) && $a_pools[$id]) { foreach($simplefields as $stat) $pconfig[$stat] = $a_pools[$id][$stat]; + + $a_errorfiles = &$a_pools[$id]['errorfiles']['item']; + if (!is_array($a_errorfiles)) $a_errorfiles = array(); } if (isset($_GET['dup'])) @@ -269,6 +287,8 @@ if ($_POST) { $input_errors[] = "The field 'Port' value is not a number."; } + $a_errorfiles = haproxy_htmllist_get_values($fields_errorfile); + if ($_POST['strict_transport_security'] !== "" && !is_numeric($_POST['strict_transport_security'])) $input_errors[] = "The field 'Strict-Transport-Security' is not empty or a number."; @@ -356,6 +376,9 @@ foreach($simplefields as $field){ if (fieldname == 'forwardto') return "<?=haproxy_js_select_options($primaryfrontends);?>"; else + if (fieldname == 'errorfile') + return "<?=haproxy_js_select_options($a_files);?>"; + else return "<?=haproxy_js_select_options($a_servermodes);?>"; } @@ -920,6 +943,21 @@ set by the 'retries' parameter.</div> </tr> <tr><td> </td></tr> <tr> + <td colspan="2" valign="top" class="listtopic">Error files</td> + </tr> + <tr class="" align="left" id='errorfiles'> + <td colspan="2" valign="top" class="vtable"> + Use these to replace the error pages that haproxy can generate by custom pages created on the files tab. + For example haproxy will generate a 503 error page when no backend is available, you can replace that page here. + <br/> + <br/> + <? + haproxy_htmllist("table_errorfile", $a_errorfiles, $fields_errorfile); + ?> + </td> + </tr> + <tr><td> </td></tr> + <tr> <td colspan="2" valign="top" class="listtopic">Advanced</td> </tr> <tr class="" align="left" id='Strict-Transport-Security'> @@ -961,9 +999,11 @@ set by the 'retries' parameter.</div> <? phparray_to_javascriptarray($fields_servers,"fields_servers",Array('/*','/*/name','/*/type','/*/size','/*/items','/*/items/*','/*/items/*/*','/*/items/*/*/name')); phparray_to_javascriptarray($fields_servers_details,"fields_details_servers",Array('/*','/*/name','/*/type')); + phparray_to_javascriptarray($fields_errorfile,"fields_errorfile",Array('/*','/*/name','/*/type','/*/size','/*/items','/*/items/*','/*/items/*/*','/*/items/*/*/name')); phparray_to_javascriptarray($a_checktypes,"checktypes",Array('/*','/*/name','/*/descr')); phparray_to_javascriptarray($a_cookiemode,"cookiemode",Array('/*','/*/name','/*/descr')); phparray_to_javascriptarray($a_sticky_type,"sticky_type",Array('/*','/*/descr','/*/cookiedescr')); + phparray_to_javascriptarray($a_files,"a_files",Array('/*','/*/name','/*/descr')); ?> browser_InnerText_support = (document.getElementsByTagName("body")[0].innerText != undefined) ? true : false; diff --git a/config/haproxy-devel/pkg_haproxy_tabs.inc b/config/haproxy-devel/pkg_haproxy_tabs.inc index 8cb280f8..f49d2b67 100644 --- a/config/haproxy-devel/pkg_haproxy_tabs.inc +++ b/config/haproxy-devel/pkg_haproxy_tabs.inc @@ -12,7 +12,9 @@ $haproxy_tab_array['haproxy'] = array(); $haproxy_tab_array['haproxy']['settings'] = Array(name => "Settings", url => "haproxy_global.php"); $haproxy_tab_array['haproxy']['frontend'] = Array(name => "Frontend", url => "haproxy_listeners.php"); $haproxy_tab_array['haproxy']['backend'] = Array(name => "Backend", url => "haproxy_pools.php"); +$haproxy_tab_array['haproxy']['files'] = Array(name => "Files", url => "haproxy_files.php"); $haproxy_tab_array['haproxy']['stats'] = Array(name => "Stats", url => "haproxy_stats.php"); +$haproxy_tab_array['haproxy']['statsfs'] = Array(name => "Stats FS", url => "haproxy_stats.php?haproxystats=1"); function haproxy_display_top_tabs_active($top_tabs, $activetab) { $tab_array = array(); |