diff options
author | Renato Botelho <renato@netgate.com> | 2015-09-08 08:18:41 -0300 |
---|---|---|
committer | Renato Botelho <renato@netgate.com> | 2015-09-08 08:18:41 -0300 |
commit | af08522a6f705b64c1d0ba91b10c63c0ac8ec46d (patch) | |
tree | 31b7de02355b2037ab9a2d16870ca4758d8a5db8 /config | |
parent | 85e9beb74e3184a72af621f9a4cf4b42d62de743 (diff) | |
parent | d7152f8d965ed3b22f7b64eaadd6a4f77dfe58d8 (diff) | |
download | pfsense-packages-af08522a6f705b64c1d0ba91b10c63c0ac8ec46d.tar.gz pfsense-packages-af08522a6f705b64c1d0ba91b10c63c0ac8ec46d.tar.bz2 pfsense-packages-af08522a6f705b64c1d0ba91b10c63c0ac8ec46d.zip |
Merge pull request #1033 from BBcan177/pfBlockerNG083015
Diffstat (limited to 'config')
-rw-r--r-- | config/pfblockerng/countrycodes.tar.bz2 | bin | 571736 -> 594089 bytes | |||
-rw-r--r-- | config/pfblockerng/pfblockerng.inc | 49 | ||||
-rw-r--r-- | config/pfblockerng/pfblockerng.php | 2 | ||||
-rw-r--r-- | config/pfblockerng/pfblockerng.sh | 13 | ||||
-rw-r--r-- | config/pfblockerng/pfblockerng.xml | 10 | ||||
-rw-r--r-- | config/pfblockerng/pfblockerng_alerts.php | 5 | ||||
-rw-r--r-- | config/pfblockerng/pfblockerng_install.inc | 82 | ||||
-rw-r--r-- | config/pfblockerng/pfblockerng_top20.xml | 11 | ||||
-rw-r--r-- | config/pfblockerng/pfblockerng_update.php | 89 |
9 files changed, 147 insertions, 114 deletions
diff --git a/config/pfblockerng/countrycodes.tar.bz2 b/config/pfblockerng/countrycodes.tar.bz2 Binary files differindex 12cc1b5b..ea189de6 100644 --- a/config/pfblockerng/countrycodes.tar.bz2 +++ b/config/pfblockerng/countrycodes.tar.bz2 diff --git a/config/pfblockerng/pfblockerng.inc b/config/pfblockerng/pfblockerng.inc index 379ce223..646e54ca 100644 --- a/config/pfblockerng/pfblockerng.inc +++ b/config/pfblockerng/pfblockerng.inc @@ -2724,53 +2724,6 @@ function pfblockerng_validate_input($post, &$input_errors) { } } - -function pfblockerng_php_install_command() { - require_once("/usr/local/www/pfblockerng/pfblockerng.php"); - global $config,$pfb; - pfb_global(); - - // Remove previously used CC folder location if exists - @rmdir_recursive("{$pfb['dbdir']}/cc"); - - // Uncompress Country Code File - @copy("{$pfb['dbdir']}/countrycodes.tar.bz2", "{$pfb['ccdir']}/countrycodes.tar.bz2"); - exec("/usr/bin/tar -jx -C {$pfb['ccdir']} -f {$pfb['ccdir']}/countrycodes.tar.bz2"); - // Download MaxMind Files and Create Country Code files and Build Continent XML Files - update_output_window(gettext("Downloading MaxMind Country Databases. This may take a minute...")); - exec("/bin/sh /usr/local/pkg/pfblockerng/geoipupdate.sh all >> {$pfb['geolog']} 2>&1"); - - update_output_window(gettext("MaxMind Country Database downloads completed...")); - update_output_window(gettext("Converting MaxMind Country Databases for pfBlockerNG. This may take a few minutes...")); - pfblockerng_uc_countries(); - update_output_window(gettext("Creating pfBlockerNG Continenet XML Files...")); - pfblockerng_get_countries(); - update_output_window(gettext("Completed Creating pfBlockerNG Continenet XML Files...")); - - // Remove Original Maxmind Database Files - @unlink_if_exists("{$pfb['dbdir']}/GeoIPCountryCSV.zip"); - @unlink_if_exists("{$pfb['dbdir']}/GeoIPCountryWhois.csv"); - @unlink_if_exists("{$pfb['dbdir']}/GeoIPv6.csv"); - @unlink_if_exists("{$pfb['dbdir']}/country_continent.csv"); - - // Add Widget to Dashboard - update_output_window(gettext("Adding pfBlockerNG Widget to Dashboard.")); - if ($pfb['keep'] == "on" && !empty($pfb['widgets'])) { - // Restore previous Widget setting if "Keep" is enabled. - $config['widgets']['sequence'] = $pfb['widgets']; - } else { - $widgets = $config['widgets']['sequence']; - if (!preg_match("/pfblockerng-container/", $widgets)) { - if (empty($widgets)) { - $config['widgets']['sequence'] = "pfblockerng-container:col2:show"; - } else { - $config['widgets']['sequence'] .= ",pfblockerng-container:col2:show"; - } - } - } -} - - function pfblockerng_php_deinstall_command() { require_once("config.inc"); global $config,$pfb; @@ -3030,4 +2983,4 @@ function pfblockerng_do_xmlrpc_sync($sync_to_ip, $port, $protocol, $username, $p } return $success; } -?>
\ No newline at end of file +?> diff --git a/config/pfblockerng/pfblockerng.php b/config/pfblockerng/pfblockerng.php index f69983e2..83b0ed8d 100644 --- a/config/pfblockerng/pfblockerng.php +++ b/config/pfblockerng/pfblockerng.php @@ -189,7 +189,7 @@ function pfb_update_check($header_url, $list_url, $url_format, $pfbfolder) { if (file_exists($local_file)) { // Determine if URL is Remote or Local if ($host['host'] == "127.0.0.1" || $host['host'] == $pfb['iplocal'] || empty($host['host'])) { - $remote_tds = gmdate ("D, d M Y H:i:s T", filemtime($local_file)); + $remote_tds = gmdate ("D, d M Y H:i:s T", filemtime($list_url)); } else { $remote_tds = @implode(preg_grep("/Last-Modified/", get_headers($list_url))); $remote_tds = preg_replace("/^Last-Modified: /","", $remote_tds); diff --git a/config/pfblockerng/pfblockerng.sh b/config/pfblockerng/pfblockerng.sh index 13e14760..5858b08b 100644 --- a/config/pfblockerng/pfblockerng.sh +++ b/config/pfblockerng/pfblockerng.sh @@ -235,11 +235,11 @@ cp $pfbdeny$alias".txt" $tempfile; > $dedupfile data255="$(cut -d '.' -f 1-3 $tempfile | awk '{a[$0]++}END{for(i in a){if(a[i] > 255){print i}}}')" if [ ! -z "$data255" ]; then for ip in $data255; do - ii=$(echo "^$ip" | sed 's/\./\\\./g') + ii=$(echo "^$ip." | sed 's/\./\\\./g') grep $ii $tempfile >> $dedupfile done awk 'FNR==NR{a[$0];next}!($0 in a)' $dedupfile $tempfile > $pfbdeny$alias".txt" - for ip in $data255; do echo $ip"0/24" >> $pfbdeny$alias".txt"; done + for ip in $data255; do echo $ip".0/24" >> $pfbdeny$alias".txt"; done fi } @@ -252,6 +252,8 @@ dupcheck=yes hcheck=$(grep -c ^ $masterfile); if [ "$hcheck" -eq "0" ]; then dupcheck=no; fi # Check if Alias exists in Masterfile lcheck=$(grep -m 1 "$alias " $masterfile ); if [ "$lcheck" == "" ]; then dupcheck=no; fi +# Check for single alias in masterfile +aliaslist=$(cut -d' ' -f1 $masterfile | sort | uniq); if [ "$alias" == "$aliaslist" ]; then hcheck="0"; fi if [ "$dupcheck" == "yes" ]; then # Grep Alias with a trailing Space character @@ -332,7 +334,6 @@ if [ -e "$pfbsuppression" ] && [ -s "$pfbsuppression" ]; then octet4=$(echo $ip | cut -d '.' -f 4 | sed 's/\/.*//') dcheck=$(grep $iptrim".0/24" $dupfile) if [ "$dcheck" == "" ]; then - echo $iptrim".0" >> $tempfile echo $iptrim".0/24" >> $dupfile counter=$(($counter + 1)) # Add Individual IP addresses from Range excluding Suppressed IP @@ -424,6 +425,8 @@ dupcheck=yes hcheck=$(grep -cv "^$" $masterfile); if [ "$hcheck" -eq "0" ]; then dupcheck=no; fi # Check if Alias exists in Masterfile lcheck=$(grep -m1 "$alias " $masterfile); if [ "$lcheck" == "" ]; then dupcheck=no; fi +# Check for single alias in masterfile +aliaslist=$(cut -d' ' -f1 $masterfile | sort | uniq); if [ "$alias" == "$aliaslist" ]; then hcheck="0"; fi if [ "$dupcheck" == "yes" ]; then # Grep Alias with a trailing Space character @@ -478,7 +481,7 @@ fi > $tempfile; > $tempfile2; > $dupfile; > $addfile; > $dedupfile; > $matchfile; > $tempmatchfile; count=0; dcount=0; mcount=0; mmcount=0 echo; echo "Querying for Repeat Offenders" -data="$(find $pfbdeny ! -name "pfB*.txt" ! -name "*_v6.txt" -type f | cut -d '.' -f 1-3 $pfbdeny*.txt | +data="$(find $pfbdeny ! -name "pfB*.txt" ! -name "*_v6.txt" -type f | xargs cut -d '.' -f 1-3 | awk -v max="$max" '{a[$0]++}END{for(i in a){if(a[i] > max){print i}}}' | grep -v "^1\.1\.1")" count=$(echo "$data" | grep -c ^) if [ "$data" == "" ]; then count=0; fi @@ -605,7 +608,7 @@ fi > $tempfile; > $tempfile2; > $dupfile; > $addfile; > $dedupfile; count=0; dcount=0 echo; echo "=====================================================================" echo; echo "Querying for Repeat Offenders" -data="$(find $pfbdeny ! -name "pfB*.txt" ! -name "*_v6.txt" -type f | cut -d '.' -f 1-3 $pfbdeny*.txt | +data="$(find $pfbdeny ! -name "pfB*.txt" ! -name "*_v6.txt" -type f | xargs cut -d '.' -f 1-3 | awk -v max="$max" '{a[$0]++}END{for(i in a){if(a[i] > max){print i}}}' | grep -v "^1\.1\.1")" count=$(echo "$data" | grep -c ^) if [ "$data" == "" ]; then count=0; fi diff --git a/config/pfblockerng/pfblockerng.xml b/config/pfblockerng/pfblockerng.xml index 218b22e1..d3b2cb16 100644 --- a/config/pfblockerng/pfblockerng.xml +++ b/config/pfblockerng/pfblockerng.xml @@ -71,6 +71,10 @@ <chmod>0644</chmod> </additional_files_needed> <additional_files_needed> + <item>https://packages.pfsense.org/packages/config/pfblockerng/pfblockerng_install.inc</item> + <prefix>/usr/local/pkg/pfblockerng/</prefix> + </additional_files_needed> + <additional_files_needed> <item>https://packages.pfsense.org/packages/config/pfblockerng/pfblockerng.php</item> <prefix>/usr/local/www/pfblockerng/</prefix> <chmod>0644</chmod> @@ -542,10 +546,14 @@ </field> </fields> <custom_php_install_command> - pfblockerng_php_install_command(); + <![CDATA[ + include_once('/usr/local/pkg/pfblockerng/pfblockerng_install.inc'); + ]]> </custom_php_install_command> <custom_php_deinstall_command> + <![CDATA[ pfblockerng_php_deinstall_command(); + ]]> </custom_php_deinstall_command> <custom_php_validation_command> pfblockerng_validate_input($_POST, $input_errors); diff --git a/config/pfblockerng/pfblockerng_alerts.php b/config/pfblockerng/pfblockerng_alerts.php index bfb15c07..7253d04d 100644 --- a/config/pfblockerng/pfblockerng_alerts.php +++ b/config/pfblockerng/pfblockerng_alerts.php @@ -451,7 +451,7 @@ function conv_log_filter_lite($logfile, $nentries, $tail, $pfbdenycnt, $pfbpermi } // Skip Repeated Alerts - if (($pfbalert[3] . $pfbalert[8] . $pfbalert[10]) == $previous_dstip || ($pfbalert[3] . $pfbalert[7] . $pfbalert[9]) == $previous_srcip) { + if (($pfbalert[1] . $pfbalert[3] . $pfbalert[7] . $pfbalert[8] . $pfbalert[10]) == $previous_alert) { continue; } @@ -489,8 +489,7 @@ function conv_log_filter_lite($logfile, $nentries, $tail, $pfbdenycnt, $pfbpermi } // Collect Details for Repeated Alert Comparison - $previous_srcip = $pfbalert[3] . $pfbalert[7] . $pfbalert[9]; - $previous_dstip = $pfbalert[3] . $pfbalert[8] . $pfbalert[10]; + $previous_alert = $pfbalert[1] . $pfbalert[3] . $pfbalert[7] . $pfbalert[8] . $pfbalert[10]; } unset ($pfbalert, $logarr); return $fields_array; diff --git a/config/pfblockerng/pfblockerng_install.inc b/config/pfblockerng/pfblockerng_install.inc new file mode 100644 index 00000000..28fe373f --- /dev/null +++ b/config/pfblockerng/pfblockerng_install.inc @@ -0,0 +1,82 @@ +<?php +/* + pfBlockerNG_install.inc + + pfBlockerNG + Copyright (C) 2015 BBcan177@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. + +*/ + +// Install pfBlockerNG package, launched from pfblockerng.xml + +require_once('/usr/local/pkg/pfblockerng/pfblockerng.inc'); +require_once('/usr/local/www/pfblockerng/pfblockerng.php'); + +global $config, $pfb; +pfb_global(); + +// Remove previously used CC folder location if exists +@rmdir_recursive("{$pfb['dbdir']}/cc"); + +// Uncompress Country Code File +@copy("{$pfb['dbdir']}/countrycodes.tar.bz2", "{$pfb['ccdir']}/countrycodes.tar.bz2"); +exec("/usr/bin/tar -jx -C {$pfb['ccdir']} -f {$pfb['ccdir']}/countrycodes.tar.bz2"); +// Download MaxMind Files and Create Country Code files and Build Continent XML Files +update_output_window(gettext("Downloading MaxMind Country Databases. This may take a minute...")); +exec("/bin/sh /usr/local/pkg/pfblockerng/geoipupdate.sh all >> {$pfb['geolog']} 2>&1"); + +update_output_window(gettext("MaxMind Country Database downloads completed...")); +update_output_window(gettext("Converting MaxMind Country Databases for pfBlockerNG. This may take a few minutes...")); +pfblockerng_uc_countries(); +update_output_window(gettext("Creating pfBlockerNG Continent XML Files...")); +pfblockerng_get_countries(); +update_output_window(gettext("Completed Creating pfBlockerNG Continent XML Files...")); + +// Remove Original Maxmind Database Files +@unlink_if_exists("{$pfb['dbdir']}/GeoIPCountryCSV.zip"); +@unlink_if_exists("{$pfb['dbdir']}/GeoIPCountryWhois.csv"); +@unlink_if_exists("{$pfb['dbdir']}/GeoIPv6.csv"); +@unlink_if_exists("{$pfb['dbdir']}/country_continent.csv"); + +// Add Widget to Dashboard +update_output_window(gettext("Adding pfBlockerNG Widget to Dashboard.")); +if ($pfb['keep'] == "on" && !empty($pfb['widgets'])) { + // Restore previous Widget setting if "Keep" is enabled. + $config['widgets']['sequence'] = $pfb['widgets']; +} else { + $widgets = $config['widgets']['sequence']; + if (!preg_match("/pfblockerng-container/", $widgets)) { + if (empty($widgets)) { + $config['widgets']['sequence'] = "pfblockerng-container:col2:show"; + } else { + $config['widgets']['sequence'] .= ",pfblockerng-container:col2:show"; + } + } +} +return TRUE; + +?>
\ No newline at end of file diff --git a/config/pfblockerng/pfblockerng_top20.xml b/config/pfblockerng/pfblockerng_top20.xml index 32ed52e8..030c1385 100644 --- a/config/pfblockerng/pfblockerng_top20.xml +++ b/config/pfblockerng/pfblockerng_top20.xml @@ -132,6 +132,17 @@ <type>listtopic</type> </field> <field> + <description><![CDATA[<font color='red'>Note:</font> pfSense by default implicitly blocks all unsolicited inbound traffic to the WAN + interface. Therefore adding GeoIP based firewall rules to the WAN will <strong>not</strong> provide any benefit, unless there are + open WAN ports. Also consider protecting just the specific open WAN ports. It's also <strong>not</strong> recommended to + block the 'world', instead consider rules to 'Permit' traffic from selected Countries only. Finally, it's just as important + to protect the outbound LAN traffic.]]> + </description> + <type>info</type> + <dontdisplayname/> + <usecolspan2/> + </field> + <field> <fielddescr>LINKS</fielddescr> <description><![CDATA[<a href="/firewall_aliases.php">Firewall Alias</a> <a href="/firewall_rules.php">Firewall Rules</a> <a href="diag_logs_filter.php">Firewall Logs</a>]]> diff --git a/config/pfblockerng/pfblockerng_update.php b/config/pfblockerng/pfblockerng_update.php index e63d04dc..7911a4e6 100644 --- a/config/pfblockerng/pfblockerng_update.php +++ b/config/pfblockerng/pfblockerng_update.php @@ -207,9 +207,9 @@ include_once("head.inc"); <tr> <td colspan="2" class="listr"> <?php - if ($pfb['enable'] == "on") { + if ($pfb['enable'] == 'on') { - /* Legend - Time Variables + /* Legend - Time variables $pfb['interval'] Hour interval setting (1,2,3,4,6,8,12,24) $pfb['min'] Cron minute start time (0-23) @@ -218,92 +218,70 @@ include_once("head.inc"); $currenthour Current hour $currentmin Current minute + $currentsec Current second + $currentdaysec Total number of seconds elapsed so far in the day $cron_hour_begin First cron hour setting (interval 2-24) $cron_hour_next Next cron hour setting (interval 2-24) - $max_min_remain Max minutes to next cron (not including currentmin) - $min_remain Total minutes remaining to next cron - $min_final The minute component in hour:min - $nextcron Next cron event in hour:mins - $cronreal Time remaining to next cron in hours:mins */ + $cronreal Time remaining to next cron in hours:mins:secs */ $currenthour = date('G'); $currentmin = date('i'); + $currentsec = date('s'); + $currentdaysec = ($currenthour * 3600) + ($currentmin * 60) + $currentsec; if ($pfb['interval'] == 1) { - if (($currenthour + ($currentmin/60)) <= ($pfb['hour'] + ($pfb['min']/60))) { + if ($currentmin < $pfb['min']) { $cron_hour_next = $currenthour; } else { - $cron_hour_next = $currenthour + 1; - } - if (($currenthour + ($pfb['min']/60)) >= 24) { - $cron_hour_next = $pfb['hour']; + $cron_hour_next = ($currenthour + 1) % 24; } - $max_min_remain = 60 + $pfb['min']; } elseif ($pfb['interval'] == 24) { - $cron_hour_next = $cron_hour_begin = $pfb['24hour'] != '' ? $pfb['24hour'] : '00'; + $cron_hour_next = $cron_hour_begin = !empty($pfb['24hour']) ?: '00'; } else { - // Find Next Cron hour schedule + // Find next cron hour schedule $crondata = pfb_cron_base_hour(); + $cron_hour_begin = 0; + $cron_hour_next = ''; if (!empty($crondata)) { foreach ($crondata as $key => $line) { if ($key == 0) { $cron_hour_begin = $line; } - if ($line > $currenthour) { + if (($line * 3600) + ($pfb['min'] * 60) > $currentdaysec) { $cron_hour_next = $line; break; } } } - - // Roll over to First cron hour setting - if (!isset($cron_hour_next)) { - if (empty($cron_hour_begin)) { - // $cron_hour_begin is hour '0' - $cron_hour_next = (24 - $currenthour); - } else { - $cron_hour_next = $cron_hour_begin; - } - } - } - - if ($pfb['interval'] != 1) { - if (($currenthour + ($currentmin/60)) <= ($cron_hour_next + ($pfb['min']/60))) { - $max_min_remain = (($cron_hour_next - $currenthour) * 60) + $pfb['min']; - } else { - $max_min_remain = ((24 - $currenthour + $cron_hour_begin) * 60) + $pfb['min']; + // Roll over to the first cron hour setting + if (empty($cron_hour_next)) { $cron_hour_next = $cron_hour_begin; } } - $min_remain = ($max_min_remain - $currentmin); - $min_final = ($min_remain % 60); - $sec_final = (60 - date('s')); - - if (strlen($sec_final) == 1) { - $sec_final = '0' . $sec_final; - } - if (strlen($min_final) == 1) { - $min_final = '0' . $min_final; - } - if (strlen($cron_hour_next) == 1) { - $cron_hour_next = '0' . $cron_hour_next; - } - - if ($min_remain > 59) { - $nextcron = floor($min_remain / 60) . ':' . $min_final . ':' . $sec_final; + $cron_seconds_next = ($cron_hour_next * 3600) + ($pfb['min'] * 60); + if ($currentdaysec < $cron_seconds_next) { + // The next cron job is ahead of us in the day + $sec_remain = $cron_seconds_next - $currentdaysec; } else { - $nextcron = '00:' . $min_final . ':' . $sec_final; + // The next cron job is tomorrow + $sec_remain = (24*60*60) + $cron_seconds_next - $currentdaysec; } - if ($pfb['min'] == 0) { - $pfb['min'] = '00'; - } + // Ensure hour:min:sec variables are two digit + $pfb['min'] = str_pad($pfb['min'], 2, '0', STR_PAD_LEFT); + $sec_final = str_pad(($sec_remain % 60), 2, '0', STR_PAD_LEFT); + $min_remain = str_pad(floor($sec_remain / 60), 2, '0', STR_PAD_LEFT); + $min_final = str_pad(($min_remain % 60), 2, '0', STR_PAD_LEFT); + $hour_final = str_pad(floor($min_remain / 60), 2, '0', STR_PAD_LEFT); + $cron_hour_next = str_pad($cron_hour_next, 2, '0', STR_PAD_LEFT); + $cronreal = "{$cron_hour_next}:{$pfb['min']}"; + $nextcron = "{$hour_final}:{$min_final}:{$sec_final}"; } if (empty($pfb['enable']) || empty($cron_hour_next)) { @@ -314,9 +292,8 @@ include_once("head.inc"); echo "NEXT Scheduled CRON Event will run at <font size=\"3\"> {$cronreal}</font> with <font size=\"3\"><span class=\"red\"> {$nextcron} </span></font> time remaining."; - // Query for any Active pfBlockerNG CRON Jobs - $result_cron = array(); - $cron_event = exec ("/bin/ps -wax", $result_cron); + // Query for any active pfBlockerNG CRON jobs + exec ('/bin/ps -wax', $result_cron); if (preg_grep("/pfblockerng[.]php\s+cron/", $result_cron)) { echo "<font size=\"2\"><span class=\"red\"> Active pfBlockerNG CRON Job </span></font> "; |