diff options
Diffstat (limited to 'config/lightsquid')
-rw-r--r-- | config/lightsquid/lightsquid.inc | 121 | ||||
-rw-r--r-- | config/lightsquid/lightsquid.xml | 26 | ||||
-rw-r--r-- | config/lightsquid/sqstat.class.php | 582 | ||||
-rw-r--r-- | config/lightsquid/sqstat.css | 68 | ||||
-rw-r--r-- | config/lightsquid/sqstat.php | 417 | ||||
-rw-r--r-- | config/lightsquid/zhabascript.js | 118 |
6 files changed, 1280 insertions, 52 deletions
diff --git a/config/lightsquid/lightsquid.inc b/config/lightsquid/lightsquid.inc index 5e16587a..5fd89470 100644 --- a/config/lightsquid/lightsquid.inc +++ b/config/lightsquid/lightsquid.inc @@ -33,7 +33,21 @@ require_once('pfsense-utils.inc'); require_once('pkg-utils.inc'); require_once('filter.inc'); require_once('service-utils.inc'); -require_once('squid.inc'); + +if (file_exists('squid.inc')) { + require_once('squid.inc'); +} +else update_log("File 'squid.inc' not found."); + +$pfs_version = substr(trim(file_get_contents("/etc/version")),0,3); +switch ($pfs_version) { + case "1.2": + case "2.0": + define('LIGHTSQUID_BASE','/usr/local'); + break; + default: + define('LIGHTSQUID_BASE', '/usr/pbi/lightsquid-' . php_uname("m")); +} define ('CMD_PKGDELETE', 'pkg_delete lightsquid-1.7.1'); @@ -42,19 +56,26 @@ define('LS_GUI_DEBUG', 'on'); define('LS_LOG_FILE', '/tmp/lightsquid_gui.log'); // configuration settings !-- CHECK THIS --! -define('LS_CONFIGPATH', '/usr/local/etc/lightsquid'); +define('LS_CONFIGPATH', LIGHTSQUID_BASE . '/etc/lightsquid'); define('LS_CONFIGFILE', 'lightsquid.cfg'); define('LS_CONFIGFILE_DIST', 'lightsquid.cfg.dist'); -define('LS_WWWPATH', '/usr/local/www/lightsquid'); -define('LS_TEMPLATEPATH', '/usr/local/www/lightsquid/tpl'); -define('LS_LANGPATH', '/usr/local/share/lightsquid/lang'); +define('LS_WWWPATH', LIGHTSQUID_BASE . '/www/lightsquid'); +define('LS_TEMPLATEPATH', LIGHTSQUID_BASE . '/www/lightsquid/tpl'); +define('LS_LANGPATH', LIGHTSQUID_BASE . '/share/lightsquid/lang'); define('LS_REPORTPATH', '/var/lightsquid/report'); -define('LS_SQUIDLOGPATH', '/var/squid/log'); + +global $config; +if (isset($config['installedpackages']['squid']['config'][0])) { + if (!empty($config['installedpackages']['squid']['config'][0]['log_dir'])) + define('LS_SQUIDLOGPATH', $config['installedpackages']['squid']['config'][0]['log_dir']); + else + define('LS_SQUIDLOGPATH', '/var/squid/logs'); +} define('LS_SQUIDLOG', 'access.log'); -define('LS_IP2NAMEPATH', '/usr/local/libexec/lightsquid'); +define('LS_IP2NAMEPATH', LIGHTSQUID_BASE . '/libexec/lightsquid'); define('CRONTAB_FILE', '/var/cron/tabs/root'); -define('CRONTAB_LS_TEMPLATE', '/usr/bin/perl /usr/local/www/lightsquid/lightparser.pl'); +define('CRONTAB_LS_TEMPLATE', '/usr/bin/perl ' . LIGHTSQUID_BASE . '/www/lightsquid/lightparser.pl'); define('CRONTAB_LS_JOBKEY', '/lightparser.pl'); define('CRONTAB_SQUID_TEMPLATE', '/usr/local/sbin/squid -k rotate > /dev/null'); define('CRONTAB_SQUID_JOBKEY', '/squid -k rotate'); @@ -119,11 +140,6 @@ function lightsquid_deinstall() { // delete cron task's ls_setup_cron("lightsquid_squid_rotate", "", "", false); ls_setup_cron("lightsquid_parser", "", "", false); - // control shoot to head :) -# mwexec(CMD_PKGDELETE); - // remove '/usr/local/www/lightsquid' dir - any time can't be removed by templates -# if (file_exists(LS_WWWPATH)) -# mwexec("rm -rf " . LS_WWWPATH); update_log("lightsquid_deinstall: stopped"); } @@ -143,7 +159,7 @@ function lightsquid_resync() { mwexec("mkdir -p " . LS_REPORTPATH); } - mwexec("/bin/chmod -R u+w /usr/local/etc/lightsquid"); + mwexec("/bin/chmod -R u+w " . LIGHTSQUID_BASE . "/etc/lightsquid"); // debug $light_test = array(); @@ -213,10 +229,10 @@ function lightsquid_resync() { foreach ($lsconf_var as $key => $val) { for($i = 0; $i < count($lsconf); $i++) { $s = trim($lsconf[$i]); - $e_key = "^[$]" . $key . "[ ]*[=]+"; -# update_log("Regular: eregi(\"$e_key," . "'$s')"); // debug regular template - if (eregi($e_key, $s)) { -# update_log("Regular PASSED: eregi(\"$e_key," . "'$s')"); // debug regular template + $e_key = "/^[$]" . $key . "[ ]*[=]+/i"; +# update_log("Regular: preg_match(\"$e_key," . "'$s')"); // debug regular template + if (preg_match($e_key, $s)) { +# update_log("Regular PASSED: preg_match(\"$e_key," . "'$s')"); // debug regular template $lsconf[$i] = '$' . "$key = $val;"; update_log("Update config: $key=$val"); } @@ -259,43 +275,35 @@ function lightsquid_resync() { ls_setup_cron("lightsquid_parser_yesterday", "", "", false); } - // set shedule - squid rotate -# if ($tm_squid) { -# $on = false; -# $opt = array("0", "0", "*", "*", "*", "root", CRONTAB_SQUID_TEMPLATE); -# switch($tm_squid) { -# // day of month -# case 'lsr_none':$on = false; break; -# case 'lsr_d1': $on = true; $opt[2]= "*/1"; break; -# case 'lsr_d2': $on = true; $opt[2]= "*/2"; break; -# case 'lsr_d3': $on = true; $opt[2]= "*/3"; break; -# case 'lsr_d4': $on = true; $opt[2]= "*/4"; break; -# case 'lsr_d5': $on = true; $opt[2]= "*/5"; break; -# case 'lsr_d6': $on = true; $opt[2]= "*/6"; break; -# case 'lsr_d10': $on = true; $opt[2]= "*/10"; break; -# case 'lsr_d15': $on = true; $opt[2]= "*/15"; break; -# case 'lsr_d20': $on = true; $opt[2]= "*/20"; break; -# case 'lsr_d25': $on = true; $opt[2]= "*/25"; break; -# case 'lsr_d30': $on = true; $opt[2]= "*/30"; break; -# // day of week -# case 'lsr_w1': $on = true; $opt[4]= "*/1"; break; -# case 'lsr_w2': $on = true; $opt[4]= "*/1"; break; -# case 'lsr_w3': $on = true; $opt[4]= "*/1"; break; -# case 'lsr_w4': $on = true; $opt[4]= "*/1"; break; -# case 'lsr_w5': $on = true; $opt[4]= "*/1"; break; -# case 'lsr_w6': $on = true; $opt[4]= "*/1"; break; -# case 'lsr_w7': $on = true; $opt[4]= "*/1"; break; -# } -# ls_setup_cron("lightsquid_squid_rotate", $opt, CRONTAB_SQUID_JOBKEY, $on); -# } else - ls_setup_cron("lightsquid_squid_rotate", "", "", false); + ls_setup_cron("lightsquid_squid_rotate", "", "", false); // update squid conf if (isset($config['installedpackages']['squid']['config'][0])) { - $config['installedpackages']['squid']['config'][0]['log_enabled'] = 'on'; - $config['installedpackages']['squid']['config'][0]['log_dir'] = LS_SQUIDLOGPATH; + $squid_settings = $config['installedpackages']['squid']['config'][0]; + $squid_settings['log_enabled'] = 'on'; + if (empty($squid_settings['log_dir'])) + $squid_settings['log_dir'] = LS_SQUIDLOGPATH; + + # sqstat + $ifmgr = "127.0.0.1;"; + $iface = ($squid_settings['active_interface'] ? $squid_settings['active_interface'] : 'lan'); + $iface = explode(",", $iface); + foreach ($iface as $i => $if) { + $realif = ls_get_real_interface_address($if); + if ($realif[0]) + $ifmgr = $ifmgr . $realif[0] . ";"; + } + + # ? delete ? + $config['installedpackages']['squidcache']['config'][0]['ext_cachemanager'] = $ifmgr; + # now right + $config['installedpackages']['squidnac']['config'][0]['ext_cachemanager'] = $ifmgr; + write_config(); - squid_resync(); + if (function_exists('squid_resync')) { + squid_resync(); + } + else update_log("Function 'squid_resync' not found."); } } @@ -418,4 +426,15 @@ function refresh_full() { update_log("refresh_full: stop"); } +function ls_get_real_interface_address($iface) +{ + global $config; + + $iface = convert_friendly_interface_to_real_interface_name($iface); + $line = trim(shell_exec("ifconfig $iface | grep inet | grep -v inet6")); + list($dummy, $ip, $dummy2, $netmask) = explode(" ", $line); + + return array($ip, long2ip(hexdec($netmask))); +} + ?>
\ No newline at end of file diff --git a/config/lightsquid/lightsquid.xml b/config/lightsquid/lightsquid.xml index cb481943..b8ce2bc8 100644 --- a/config/lightsquid/lightsquid.xml +++ b/config/lightsquid/lightsquid.xml @@ -47,7 +47,7 @@ <faq>Currently there are no FAQ items provided.</faq> <name>lightsquid</name> <version>1.7.1</version> - <title>Services: Proxy server Report(LightSquid) -> Settings</title> + <title>Services: Proxy Reports (LightSquid, SQStat) -> Settings</title> <category>Status</category> <include_file>/usr/local/pkg/lightsquid.inc</include_file> <menu> @@ -66,6 +66,10 @@ <text>Lightsquid Report</text> <url>/lightsquid/index.cgi</url> </tab> + <tab> + <text>Proxy State</text> + <url>/sqstat/sqstat.php</url> + </tab> </tabs> <additional_files_needed> <prefix>/usr/local/pkg/</prefix> @@ -77,6 +81,26 @@ <chmod>0755</chmod> <item>http://files.pfsense.org/packages/All/lightsquid_tpl.tbz</item> </additional_files_needed> + <additional_files_needed> + <prefix>/usr/local/www/sqstat/</prefix> + <chmod>0644</chmod> + <item>http://www.pfsense.org/packages/config/lightsquid/sqstat.class.php</item> + </additional_files_needed> + <additional_files_needed> + <prefix>/usr/local/www/sqstat/</prefix> + <chmod>0644</chmod> + <item>http://www.pfsense.org/packages/config/lightsquid/sqstat.php</item> + </additional_files_needed> + <additional_files_needed> + <prefix>/usr/local/www/sqstat/</prefix> + <chmod>0644</chmod> + <item>http://www.pfsense.org/packages/config/lightsquid/sqstat.css</item> + </additional_files_needed> + <additional_files_needed> + <prefix>/usr/local/www/sqstat/</prefix> + <chmod>0644</chmod> + <item>http://www.pfsense.org/packages/config/lightsquid/zhabascript.js</item> + </additional_files_needed> <fields> <field> <fielddescr>Language</fielddescr> diff --git a/config/lightsquid/sqstat.class.php b/config/lightsquid/sqstat.class.php new file mode 100644 index 00000000..228aecfe --- /dev/null +++ b/config/lightsquid/sqstat.class.php @@ -0,0 +1,582 @@ +<?php +/* $Id$ */ +/* + sqstat.class.php + Squid Proxy Server realtime stat + + (c) Alex Samorukov, samm@os2.kiev.ua + modification by 2011 Serg Dvoriancev, dv_serg@mail.ru + Squid Proxy Server realtime stat + + part of pfSense (www.pfSense.com) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 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. +*/ + +// sqstat class +DEFINE('SQSTAT_VERSION', '1.20'); +DEFINE('SQSTAT_SHOWLEN', 60); + +class squidstat{ + var $fp; + + # conection + var $squidhost; + var $squidport; + + # hosts + var $hosts_file; + var $hosts; + + # versions + var $server_version; + var $sqstat_version; + + # other + var $group_by; + var $resolveip; + var $autorefresh; + var $use_sessions = false; + + # cache manager + var $cachemgr_passwd; + + # errors + var $errno; + var $errstr; + + function squidstat(){ + $this->sqstat_version = SQSTAT_VERSION; + + $this->squidhost = '127.0.0.1'; + $this->squidport = '3128'; + + $This->group_by = 'host'; + $this->resolveip = true; + $this->hosts_file = ''; + $this->autorefresh = 0; + $this->cachemgr_passwd = ''; + + $errno = 0; + $errstr = ''; + + if (!function_exists("preg_match")) { $this->errorMsg(5, 'You need to install <a href="http://www.php.net/pcre/" target="_blank">PHP pcre extension</a> to run this script'); + $this->showError(); + exit(5); + } + + // we need session support to gather avg. speed + if (function_exists("session_start")){ + $this->use_sessions=true; + } + + } + + function formatXHTML($body, $refresh, $use_js = false){ + $text='<?xml version="1.0" encoding="UTF-8"?>'."\n". + '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n" + .'<html>' + .'<head>' + .'<link href="sqstat.css" rel="stylesheet" type="text/css"/>'; + if($refresh) $text.='<META HTTP-EQUIV=Refresh CONTENT="'.$refresh.'; URL='.$_SERVER["PHP_SELF"].'?refresh='.$refresh.'&config='.$GLOBALS["config"].'"/>'; + $text.='<title>SqStat '.SQSTAT_VERSION.'</title>' + .($use_js?'<script src="zhabascript.js" type="text/javascript"></script>':'').'</head>' + .($use_js?'<body onload="jsInit();"><div id="dhtmltooltip"></div><img id="dhtmlpointer" src="arrow.gif">':'<body>') + .$body.'</body></html>'; + return $text; + } + + function showError(){ + $text='<h1>SqStat error</h1>'. + '<h2 style="color:red">Error ('.$this->errno.'): '.$this->errstr.'</span>'; + echo $this->formatXHTML($text,0); + } + + function connect($squidhost, $squidport){ + $this->fp = false; + # connecting to the squidhost + $this->fp = @fsockopen($squidhost, $squidport, $this->errno, $this->errstr, 10); + if (!$this->fp) { + # failed to connect + return false; + } + return true; + } + + # based @ (c) moritz at barafranca dot com + function duration ($seconds) { + $takes_time = array(604800,86400,3600,60,0); + $suffixes = array("w","d","h","m","s"); + $output = ""; + foreach ($takes_time as $key=>$val) { + ${$suffixes[$key]} = ($val == 0) ? $seconds : floor(($seconds/$val)); + $seconds -= ${$suffixes[$key]} * $val; + if (${$suffixes[$key]} > 0) { + $output .= ${$suffixes[$key]}; + $output .= $suffixes[$key]." "; + } + } + return trim($output); + } + + /** + * Format a number of bytes into a human readable format. + * Optionally choose the output format and/or force a particular unit + * + * @param int $bytes The number of bytes to format. Must be positive + * @param string $format Optional. The output format for the string + * @param string $force Optional. Force a certain unit. B|KB|MB|GB|TB + * @return string The formatted file size + */ + function filesize_format($bytes, $format = '', $force = '') + { + $force = strtoupper($force); + $defaultFormat = '%01d %s'; + if (strlen($format) == 0) + $format = $defaultFormat; + $bytes = max(0, (int) $bytes); + $units = array('b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb'); + $power = array_search($force, $units); + if ($power === false) + $power = $bytes > 0 ? floor(log($bytes)/log(1024)) : 0; + return sprintf($format, $bytes / pow(1024, $power), $units[$power]); + } + + function makeQuery($pass = ""){ + $raw = array(); + # sending request + if(!$this->fp) + die("Please connect to server"); + + $out = "GET cache_object://localhost/active_requests HTTP/1.0\r\n"; + if ($pass != "") + $out .= "Authorization: Basic ".base64_encode("cachemgr:$pass")."\r\n"; + $out .= "\r\n"; + + fwrite($this->fp, $out); + + while (!feof($this->fp)) { + $raw[] = trim(fgets($this->fp, 2048)); + } + fclose($this->fp); + + if ($raw[0]!="HTTP/1.0 200 OK") { $this->errorMsg(1, "Cannot get data. Server answered: $raw[0]"); + return false; + } + + # parsing output; + $header = 1; + $connection = 0; + $parsed["server_version"] = "Unknown"; + foreach($raw as $key=>$v){ + # cutoff http header + if ($header==1 && $v=="") $header=0; + if ($header) { + if(substr(strtolower($v),0,7) == "server:") { # parsing server version + $parsed["server_version"] = substr($v,8); + } + } + else { + if(substr($v,0,11) == "Connection:") { # parsing connection + $connection = substr($v,12); + } + if ($connection) { + # username field is avaible in Squid 2.6 stable + if(substr($v,0,9) == "username ") $parsed["con"][$connection]["username"] = substr($v, 9); + if(substr($v,0,5) == "peer:") $parsed["con"][$connection]["peer"] = substr($v, 6); + if(substr($v,0,3) == "me:") $parsed["con"][$connection]["me"] = substr($v, 4); + if(substr($v,0,4) == "uri ") $parsed["con"][$connection]["uri"] = substr($v, 4); + if(substr($v,0,10) == "delay_pool") $parsed["con"][$connection]["delay_pool"] = substr($v, 11); + + if (preg_match('/out.offset \d+, out.size (\d+)/', $v, $matches)) { + $parsed["con"][$connection]["bytes"] = $matches[1]; + } + if (preg_match('/start \d+\.\d+ \((\d+).\d+ seconds ago\)/', $v, $matches)){ + $parsed["con"][$connection]["seconds"] = $matches[1]; + } + } + } + } + return $parsed; + } + + function implode_with_keys($array, $glue) { + foreach ($array as $key => $v){ + $ret[] = $key . '=' . htmlspecialchars($v); + } + return implode($glue, $ret); + } + + function makeHtmlReport($data, $resolveip = false, $hosts_array = array(), $use_js = true) { + global $group_by; + if($this->use_sessions){ + session_name('SQDATA'); + session_start(); + } + + $total_avg = $total_curr = 0; + // resort data array + $users=array(); + switch($group_by){ + case "host": + $group_by_name="Host"; + $group_by_key='return $ip;'; + break; + case "username": + $group_by_name="User"; + $group_by_key='return $v["username"];'; + break; + default: + die("wrong group_by!"); + } + + foreach($data["con"] as $key => $v){ + if(substr($v["uri"],0,13)=="cache_object:") continue; // skip myself + $ip=substr($v["peer"],0,strpos($v["peer"],":")); + if(isset($hosts_array[$ip])){ + $ip=$hosts_array[$ip]; + } + // i use ip2long() to make ip sorting work correctly + elseif($resolveip){ + $hostname=gethostbyaddr($ip); + if($hostname==$ip) $ip=ip2long($ip);// resolve failed + else $ip=$hostname; + } + else{ + $ip=ip2long(substr($v["peer"],0,strpos($v["peer"],":"))); + } + $v['connection'] = $key; + if(!isset($v["username"])) $v["username"]="N/A"; + $users[eval($group_by_key)][]=$v; + } + ksort($users); + $refresh=0; + if(isset($_GET["refresh"]) && !isset($_GET["stop"])) $refresh=(int)$_GET["refresh"]; + $text=''; + if(count($GLOBALS["configs"])==1) $servers=$GLOBALS["squidhost"].':'.$GLOBALS["squidport"]; + else{ + $servers='<select onchange="this.form.submit();" name="config">'; + foreach ($GLOBALS["configs"] as $key=>$v){ + $servers.='<option '.($GLOBALS["config"]==$key?' selected="selected" ':'').' value="'.$key.'">'.htmlspecialchars($v).'</option>'; + } + $servers.='</select>'; + } + $text.='<div class="header"><form method="get" action="'.$_SERVER["PHP_SELF"].'">'. + 'Squid RealTime stat for the '.$servers.' proxy server ('.$data["server_version"].').<br/>'. + 'Auto refresh: <input name="refresh" type="text" size="4" value="'.$refresh.'"/> sec. <input type="submit" value="Update"/> <input name="stop" type="submit" value="Stop"/> Created at: <tt>'.date("h:i:s d/m/Y").'</tt><br/>'. + '</div>'. + '<table class="result" align="center" width="100%" border="0">'. + '<tr>'. + '<th>'.$group_by_name.'</th><th>URI</th>'. + ($this->use_sessions?'<th>Curr. Speed</th><th>Avg. Speed</th>':''). + '<th>Size</th><th>Time</th>'. + '</tr>'; + $ausers=$acon=0; + unset($session_data); + if (isset($_SESSION['time']) && ((time() - $_SESSION['time']) < 3*60) && isset($_SESSION['sqdata']) && is_array($_SESSION['sqdata'])) { + //only if the latest data was less than 3 minutes ago + $session_data = $_SESSION['sqdata']; + } + $table=''; + foreach($users as $key=>$v){ + $ausers++; + $table.='<tr><td style="border-right:0;" colspan="2"><b>'.(is_int($key)?long2ip($key):$key).'</b></td>'. + '<td style="border-left:0;" colspan="5"> </td></tr>'; + $user_avg = $user_curr = $con_color = 0; + foreach ($v as $con){ + if(substr($con["uri"],0,7)=="http://" || substr($con["uri"],0,6)=="ftp://"){ + if(strlen($con["uri"])>SQSTAT_SHOWLEN) $uritext=htmlspecialchars(substr($con["uri"],0,SQSTAT_SHOWLEN)).'</a> ....'; + else $uritext=htmlspecialchars($con["uri"]).'</a>'; + $uri='<a target="_blank" href="'.htmlspecialchars($con["uri"]).'">'.$uritext; + } + else $uri=htmlspecialchars($con["uri"]); + $acon++; + //speed stuff + $con_id = $con['connection']; + $is_time = time(); + $curr_speed=0; + $avg_speed=0; + if (isset($session_data[$con_id]) && $con_data = $session_data[$con_id] ) { + // if we have info about current connection, we do analyze its data + // current speed + $was_time = $con_data['time']; + $was_size = $con_data['size']; + if ($was_time && $was_size) { + $delta = $is_time - $was_time; + if ($delta == 0) { + $delta = 1; + } + if ($con['bytes'] >= $was_size) { + $curr_speed = ($con['bytes'] - $was_size) / 1024 / $delta; + } + } else { + $curr_speed = $con['bytes'] / 1024; + } + + //avg speed + $avg_speed = $con['bytes'] / 1024; + if ($con['seconds'] > 0) { + $avg_speed /= $con['seconds']; + } + } + + $new_data[$con_id]['time'] = $is_time; + $new_data[$con_id]['size'] = $con['bytes']; + + //sum speeds + $total_avg += $avg_speed; + $user_avg += $avg_speed; + $total_curr += $curr_speed; + $user_curr += $curr_speed; + + if($use_js) $js='onMouseout="hideddrivetip()" onMouseover="ddrivetip(\''.$this->implode_with_keys($con,'<br/>').'\')"'; + else $js=''; + $table.='<tr'.( (++$con_color % 2 == 0) ? ' class="odd"' : '' ).'><td id="white"></td>'. + '<td nowrap '.$js.' width="80%" >'.$uri.'</td>'; + if($this->use_sessions){ + $table .= '<td nowrap align="right">'.( (round($curr_speed, 2) > 0) ? sprintf("%01.2f KB/s", $curr_speed) : '' ).'</td>'. + '<td nowrap align="right">'.( (round($avg_speed, 2) > 0) ? sprintf("%01.2f KB/s", $avg_speed) : '' ). '</td>'; + } + $table .= '<td nowrap align="right">'.$this->filesize_format($con["bytes"]).'</td>'. + '<td nowrap align="right">'.$this->duration($con["seconds"],"short").'</td>'. + '</tr>'; + } + if($this->use_sessions){ + $table.=sprintf("<tr><td colspan=\"2\"></td><td align=\"right\" id=\"highlight\">%01.2f KB/s</td><td align=\"right\" id=\"highlight\">%01.2f KB/s</td><td colspan=\"2\"></td>", + $user_curr, $user_avg); + } + + } + $_SESSION['time'] = time(); + if(isset($new_data)) $_SESSION['sqdata'] = $new_data; + $stat_row=''; + if($this->use_sessions){ + $stat_row.=sprintf("<tr class=\"total\"><td><b>Total:</b></td><td align=\"right\" colspan=\"5\"><b>%d</b> users and <b>%d</b> connections @ <b>%01.2f/%01.2f</b> KB/s (CURR/AVG)</td></tr>", + $ausers, $acon, $total_curr, $total_avg); + } + else { + $stat_row.=sprintf("<tr class=\"total\"><td><b>Total:</b></td><td align=\"right\" colspan=\"5\"><b>%d</b> users and <b>%d</b> connections</td></tr>", + $ausers, $acon); + } + if($ausers==0){ + $text.='<tr><td colspan=6><b>No active connections</b></td></tr>'; + } + else { + $text.=$stat_row.$table.$stat_row; + } + $text .= '</table>'. + '<p class="copyleft">© <a href="mailto:samm@os2.kiev.ua?subject=SqStat '.SQSTAT_VERSION.'">Alex Samorukov</a>, 2006</p>'; + return $this->formatXHTML($text,$refresh,$use_js); + } + + function parseRequest($data, $group_by = 'host', $resolveip = false) { $parsed = array(); + if ($this->use_sessions) { + session_name('SQDATA'); + session_start(); + } + + # resort data array + $users = array(); + switch ($group_by) { + case "username": + $group_by_name = "User"; + $group_by_key = "username"; + break; + case "host": + default: + $group_by_name = "Host"; + $group_by_key = "peer"; + break; + } + + # resolve IP & group + foreach ($data["con"] as $key => $v) { # skip myself + if (substr($v["uri"], 0, 13) == "cache_object:") continue; + + $ip = substr($v["peer"], 0, strpos($v["peer"], ":")); + $v["peer"] = $ip; + + # name from hosts + if (isset($this->hosts[$ip])) { + $ip = $this->hosts[$ip]; + } + else + # i use ip2long() to make ip sorting work correctly + if ($resolveip) { + $hostname = gethostbyaddr($ip); + if ($hostname == $ip) + $ip = ip2long($ip); # resolve failed. use (ip2long) key + else $ip = $hostname; + } + else { + $ip = ip2long(substr($v["peer"], 0, strpos($v["peer"], ":"))); + } + $v['con_id'] = $key; + $v["username"] = isset($v["username"]) ? $v["username"] : "N/A"; + + # users [key => conn_array] + $users[$v[$group_by_key]][] = $v; + } + ksort($users); + + unset($session_data); + if (isset($_SESSION['time']) && ((time() - $_SESSION['time']) < 3*60) && + isset($_SESSION['sqdata']) && is_array($_SESSION['sqdata'])) { + # only if the latest data was less than 3 minutes ago + $session_data = $_SESSION['sqdata']; + } + + # users count & con cont + $ausers = $acon = 0; + $total_avg = $total_curr = 0; + foreach ($users as $key => $v) { $ausers++; + + $user_avg = $user_curr = $con_color = 0; + foreach ($v as $con_key => $con){ $cres = array(); + $acon++; + + $uritext = $con["uri"]; + if (substr($con["uri"], 0, 7) == "http://" || substr($con["uri"], 0, 6) == "ftp://") { + if (strlen($uritext) > SQSTAT_SHOWLEN) + $uritext = htmlspecialchars(substr($uritext, 0, SQSTAT_SHOWLEN)) . ' ....'; + } + else $uritext = htmlspecialchars($uritext); + $cres['uritext'] = $uritext; + $cres['uri'] = $con["uri"]; + + # speed stuff + $con_id = $con['connection']; + $is_time = time(); + $curr_speed = $avg_speed = 0; + if (isset($session_data[$con_id]) && $con_data = $session_data[$con_id] ) { + # if we have info about current connection, we do analyze its data + # current speed + $was_time = $con_data['time']; + $was_size = $con_data['size']; + if ($was_time && $was_size) { + $delta = $is_time - $was_time; + if ($delta == 0) { + $delta = 1; + } + if ($con['bytes'] >= $was_size) { + $curr_speed = ($con['bytes'] - $was_size) / 1024 / $delta; + } + } else { + $curr_speed = $con['bytes'] / 1024; + } + + # avg speed + $avg_speed = $con['bytes'] / 1024; + if ($con['seconds'] > 0) { + $avg_speed /= $con['seconds']; + } + } + $cres['cur_speed'] = $curr_speed; + $cres['avg_speed'] = $avg_speed; + $cres['seconds'] = $con["seconds"]; + $cres['bytes'] = $con["bytes"]; + + # groupped parsed[key => conn_key] + $parsed['users'][$key]['con'][$con_key] = $cres; + + # for sessions + $new_data[$con_id]['time'] = $is_time; + $new_data[$con_id]['size'] = $con['bytes']; + + # sum speeds + $total_avg += $avg_speed; + $user_avg += $avg_speed; + $total_curr += $curr_speed; + $user_curr += $curr_speed; + } + + # total per user + $parsed['users'][$key]['user_curr'] = $user_curr; + $parsed['users'][$key]['user_avg'] = $user_avg; + } + + # total info + $parsed['ausers'] = $ausers; + $parsed['acon'] = $acon; + $parsed['total_avg'] = $total_avg; + $parsed['total_curr'] = $total_curr; + + # update session info + $_SESSION['time'] = time(); + if (isset($new_data)) $_SESSION['sqdata'] = $new_data; + + return $parsed; + } + + function errorMsg($errno, $errstr) + { $this->errno = $errno; + $this->errstr = $errstr; + } + + function load_hosts() + { + # loading hosts file + $hosts_array = array(); + + if (!empty($this->hosts_file)) { + if (is_file($this->hosts_file)) { + $handle = @fopen($this->hosts_file, "r"); + if ($handle) { + while (!feof($handle)) { + $buffer = fgets($handle, 4096); + unset($matches); + if (preg_match('/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})[ \t]+(.+)$/i', $buffer, $matches)) { + $hosts_array[$matches[1]]=$matches[2]; + } + } + fclose($handle); + } + $this->hosts = $hosts_array; + } + else { + #error + $this->errorMsg(4, "Hosts file not found. Cant read <tt>'{$this->hosts_file}'</tt>."); + return $this->errno; + } + } + + return 0; + } + + function query_exec() + { + $data = ""; + + $this->server_version = '(unknown)'; + if ($this->connect($this->squidhost, $this->squidport)) { + $data = $this->makeQuery($this->cachemgr_passwd); + if ($this->errno == 0) { + $this->server_version = $data['server_version']; + $data = $this->parseRequest($data, 'host', true); + } + } + + return $data; + } + +} +?>
\ No newline at end of file diff --git a/config/lightsquid/sqstat.css b/config/lightsquid/sqstat.css new file mode 100644 index 00000000..7575933e --- /dev/null +++ b/config/lightsquid/sqstat.css @@ -0,0 +1,68 @@ +/* "connections" table */ +TABLE.result{ + border:1px solid #ccccdd;border-collapse:collapse; +} +TABLE.result TH{ + font-family: Verdana;font-size:14px; +} +TABLE.result TD{ + font-family: Verdana;font-size:11px;border:1px solid #c0c0c0;padding:2px; +} +TABLE.result TR.total TD{ + background-color:#DCDAD5; +} + +TABLE.result TH{ + background-color:#ccccdd; + white-space: nowrap; padding: 0px 2px; +} + +TABLE.result tr.odd td { + background-color: #eef; +} +TABLE.result tr.odd td#white { + background-color: #fff; +} +TABLE.result td#highlight { + background-color: #e9e9e9; +} + + +/* top header */ +DIV.header{ + border:3px solid #ccccdd;margin-bottom:10px;padding:3px; + font-family: Verdana;font-size:12pt; +} +.copyleft,SELECT{ + font-family: Verdana;font-size:10px; +} +.copyleft A{ + text-decoration:none +} +.copyleft A:HOVER{ + text-decoration:underline +} +FORM{ + margin:0;padding:0; +} + +#dhtmltooltip{ + position: absolute; + /* width: 350px; */ + border: 2px solid black; + padding: 2px; + background-color: lightyellow; + visibility: hidden; + z-index: 100; + font-family: Verdana; font-size: 10px; +} + + +#dhtmlpointer{ + position:absolute; + left: -300px; + z-index: 101; + visibility: hidden; +} + + diff --git a/config/lightsquid/sqstat.php b/config/lightsquid/sqstat.php new file mode 100644 index 00000000..a56b604a --- /dev/null +++ b/config/lightsquid/sqstat.php @@ -0,0 +1,417 @@ +<?php +/* $Id$ */ +/* + sqstat.php + Squid Proxy Server realtime stat + + (c) Alex Samorukov, samm@os2.kiev.ua + modification by 2011 Serg Dvoriancev, dv_serg@mail.ru + + part of pfSense (www.pfSense.com) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 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. +*/ + +/* +*** sqstat - Squid Proxy Server realtime stat *** +(c) Alex Samorukov, samm@os2.kiev.ua +*/ + +require_once('guiconfig.inc'); +require_once('sqstat.class.php'); + +# init +$squidclass = new squidstat(); + +# ------------------------------------------------------------------------------ +# Requests +# ------------------------------------------------------------------------------ + +# AJAX responce +if ($_REQUEST['getactivity']) +{ + header("Content-type: text/javascript"); + echo sqstat_AJAX_response( $_REQUEST ); + exit; +} + +# ------------------------------------------------------------------------------ +# HTML Page +# ------------------------------------------------------------------------------ + +$pgtitle = "Proxy Squid: Realtime stat (sqstat)"; + +require_once("head.inc"); + +?> + +<link href="sqstat.css" rel="stylesheet" type="text/css"/> +<script type="text/javascript" src="/javascript/scriptaculous/prototype.js"></script> +<script type="text/javascript" src="zhabascript.js"></script> + +<!-- Ajax Script --> +<script type="text/javascript"> + +var intervalID = 0; + +function el(id) { + return document.getElementById(id); +} + +function getactivity(action) { + var url = "<?php echo ($_SERVER["PHP_SELF"]); ?>"; + var pars = "getactivity=yes"; + + var myAjax = new Ajax.Request( url, + { + method: 'post', + parameters: pars, + onComplete: activitycallback + }); +} + +function activitycallback(transport) { + + if (200 == transport.status) { + result = transport.responseText; + } +} + +function update_start() { + var cmax = parseInt(el('refresh').value); + + update_stop(); + + if (cmax > 0) { + intervalID = window.setInterval('getactivity();', cmax * 1000); + } +} + +function update_stop() { + window.clearInterval(intervalID); + intervalID = 0; +} + +// pre-call +window.setTimeout('update_start()', 150); + +</script> + +<!-- HTML --> + +<!-- begin --> +<body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<?php include("fbegin.inc"); ?> + +<?php + # prepare page data + $data = ''; + sqstat_loadconfig(); + if (sqstat_loadconfig() == 0) { + $data = $squidclass->query_exec(); + } + + if ($squidclass->errno == 0) { + $data = sqstat_resultHTML($data); + } + else { + # error + $data = sqstat_errorHTML(); + } +?> + +<!-- form --> +<div id="sqstat_header" class="header" > + <?php echo ( sqstat_headerHTML() ); ?> +</div> + +<!-- result table --> +<div id="sqstat_result" class="result"> + <?php echo ($data); ?> +</div> + +<!-- end --> +<?php include("fend.inc"); ?> +</body> +</html> + +<?php + +# ------------------------------------------------------------------------------ +# Functions +# ------------------------------------------------------------------------------ + +function sqstat_AJAX_response( $request ) +{ + global $squidclass, $data; + $res = ''; + + if (sqstat_loadconfig() != 0) { + return sqstat_AJAX_error(sqstat_errorHTML()); + } + + # Actions + $data = $squidclass->query_exec(); + + $ver = sqstat_serverInfoHTML(); + $res .= "el('sqstat_serverver').innerHTML = '$ver';"; + + $time = date("h:i:s d/m/Y"); + $res .= "el('sqstat_updtime').innerHTML = '$time';"; + + $data = sqstat_resultHTML( $data ); + if ($squidclass->errno == 0) { + $data = sqstat_AJAX_prep($data); + $res .= "el('sqstat_result').innerHTML = '$data';"; + } + else { + # error + $res .= sqstat_AJAX_error(sqstat_errorHTML()); + } + + return $res; +} + +function sqstat_AJAX_prep($text) +{ + $text = str_replace("'", "\'", $text); + $text = str_replace("\n", "\\r\\n", $text); + return $text; +} + +function sqstat_AJAX_error($err) +{ + $err = sqstat_AJAX_prep($err); + $t .= "el('sqstat_result').innerHTML = '$err';"; + return $t; +} + +# ------------------------------------------------------------------------------ +# Reports +# ------------------------------------------------------------------------------ + +function sqstat_headerHTML() +{ + global $squidclass; + + $date = date("h:i:s d/m/Y"); + $squidinfo = sqstat_serverInfoHTML(); + + if (empty($squidclass->autorefresh)) $squidclass->autorefresh = 0; + + return +<<<EOD + <form method="get" action="{$_SERVER["PHP_SELF"]}"> + <input id="counter" name="counter" type="hidden" value=0/> + Squid RealTime stat {$squidclass->sqstat_version} for the {$servers} proxy server <a id='sqstat_serverver'>{$squidinfo}</a>.<br/> + Auto refresh: + <input id="refresh" name="refresh" type="text" size="4" value="{$squidclass->autorefresh}"/> sec. + <input type="button" value="Update" onclick="update_start();"/> + <input type="button" value="Stop" onclick="update_stop();"/> Created at: <tt id='sqstat_updtime'>{$date}</tt><br/> + </form> +EOD; +} + +function sqstat_serverInfoHTML() +{ + global $squidclass; + return $squidclass->server_version . " ({$squidclass->squidhost}:{$squidclass->squidport})"; +} + +function sqstat_resultHTML($data) +{ + global $squidclass; + + $group_by_name = $squidclass->group_by_name; + $use_js = true; + + $t = array(); + + # table header + $t[] = "<table class='result' align='center' width='100%' border='0'>"; + $t[] = "<tr>"; + $t[] = "<th>{$group_by_name}</th><th>URI</th>"; + if ($squidclass->use_sessions) + $t[] = "<th>Curr. Speed</th><th>Avg. Speed</th>"; + $t[] = "<th>Size</th><th>Time</th>"; + $t[] = "</tr>"; + + # table body + if (is_array($data['users'])) { + $tbl = array(); + + $con_color = 0; + foreach($data['users'] as $key => $v) { + # skeep total info + if ($key == 'total') continue; + # group row + $tbl[] = "<tr>"; + $tbl[] = "<td style='border-right:0;' colspan='2'><b>" . (is_int($key) ? long2ip($key) : $key) . "</b></td>"; + $tbl[] = "<td style='border-left:0;' colspan='5'> </td>"; + $tbl[] = "</tr>"; + + # connections row + foreach ($v['con'] as $con) { + if ($use_js) + $js = "onMouseout='hideddrivetip()' onMouseover='ddrivetip(\"" . $squidclass->implode_with_keys($con,"<br/>") . "\")'"; + else $js=''; + + # begin new row + $class = (++$con_color % 2 == 0) ? " class='odd'" : ""; + $tbl[] = "<tr ($class)>"; + + # URL + $uri = "<a target='_blank' href='" . htmlspecialchars($con["uri"]) ."'>{$con['uritext']}</a>"; + $tbl[] = "<td id='white'></td>"; + $tbl[] = "<td nowrap {$js} width='80%'>{$uri}</td>"; + + # speed + if ($squidclass->use_sessions) { + $cur_s = round($con['cur_speed'], 2) > 0 ? sprintf("%01.2f KB/s", $con['cur_speed']) : ''; + $avg_s = round($con['avg_speed'], 2) > 0 ? sprintf("%01.2f KB/s", $con['avg_speed']) : ''; + $tbl[] = "<td nowrap align='right'>{$cur_s}</td>"; + $tbl[] = "<td nowrap align='right'>{$avg_s}</td>"; + } + + # file size + $filesize = $squidclass->filesize_format($con["bytes"]); + $duration = $squidclass->duration($con["seconds"], "short"); + $tbl[] = "<td nowrap align='right'>{$filesize}</td>"; + $tbl[] = "<td nowrap align='right'>{$duration}</td>"; + + # end row + $tbl[] = "</tr>"; + } + + # total user speed + if ($squidclass->use_sessions) { + $user_curr = sprintf("%01.2f KB/s", $v['user_curr']); + $user_avg = sprintf("%01.2f KB/s", $v['user_avg']); + $tbl[] ="<tr>"; + $tbl[] ="<td colspan='2'></td>"; + $tbl[] ="<td align='right' id='highlight'>{$user_curr}</td>"; + $tbl[] ="<td align='right' id='highlight'>{$user_avg}</td>"; + $tbl[] ="<td colspan='2'></td>"; + } + } + + + # status row + $stat = array(); + $ausers = sprintf("%d", $data['ausers']); + $acon = sprintf("%d", $data['acon']); + $stat[] = "<tr class='total'><td><b>Total:</b></td>"; + if ($squidclass->use_sessions) { + $total_curr = sprintf("%01.2f", $data['total_curr']); + $total_avg = sprintf("%01.2f", $data['total_avg']); + $stat[] = "<td align='right' colspan='5'><b>{$ausers}</b> users and <b>{$acon}</b> connections @ <b>{$total_curr}/{$total_avg}</b> KB/s (CURR/AVG)</td>"; + } + else { + $stat[] = "<td align='right' colspan='5'><b>{$ausers}</b> users and <b>{$acon}</b> connections</td>"; + } + $t[] = "</tr>"; + } + + if ($ausers == 0) { + $t[] = "<tr><td colspan=6><b>No active connections</b></td></tr>"; + } + else { + $stat = implode("\n", $stat); + $tbl = implode("\n", $tbl); + $t[] = $stat . $tbl . $stat; + } + + $t[] = "</table>"; + $t[] = "<p class='copyleft'>Report based on SQStat © <a href='mailto:samm@os2.kiev.ua?subject=SqStat '" . SQSTAT_VERSION . "'>Alex Samorukov</a>, 2006</p>"; + + return implode("\n", $t); +} + +function sqstat_errorHTML() +{ + global $squidclass; + $t = array(); + + # table header + $t[] = "<table class='result' align='center' width='100%' border='0'>"; + $t[] = "<tr><th align='left'>SqStat error</th></tr>"; + $t[] = "<tr><td>"; + $t[] = '<p style="color:red">Error (' . $squidclass->errno . '): ' . $squidclass->errstr . '</p>'; + $t[] = "</td></tr>"; + $t[] = "</table>"; + + return implode ("\n", $t); +} + +function sqstat_loadconfig() +{ + global $squidclass, $config; + + $squidclass->errno = 0; + $squidclass->errstr = ''; + + $squidclass->sqstat_version = SQSTAT_VERSION; + + # === load config from pfSense === + $iface = '127.0.0.1'; + $iport = 3128; + $squid_settings = $config['installedpackages']['squid']['config'][0]; + if (!empty($squid_settings)) { + # squid interface IP & port + $realif = array(); + $iface = ($squid_settings['active_interface'] ? $squid_settings['active_interface'] : 'lan'); + $iface = explode(",", $iface); + foreach ($iface as $i => $if) { + $realif[] = sqstat_get_real_interface_address($if); + $iface = $realif[$i][0] ? $realif[$i][0] : '127.0.0.1'; + } + $iport = $squid_settings['proxy_port'] ? $squid_settings['proxy_port'] : 3128; + } + $squidclass->squidhost = $iface; + $squidclass->squidport = $iport; + + $squidclass->group_by = "host"; + $squidclass->resolveip = true; + $squidclass->hosts_file = ''; # hosts file not used + $squidclass->autorefresh = 3; # refresh 3 sec by default + $squidclass->cachemgr_passwd = ''; + + # load hosts file, if defined + if (!empty($squidclass->hosts_file)) { + $squidclass->load_hosts(); + } + + return $squidclass->errno; +} + +function sqstat_get_real_interface_address($iface) +{ + global $config; + + $iface = convert_friendly_interface_to_real_interface_name($iface); + $line = trim(shell_exec("ifconfig $iface | grep inet | grep -v inet6")); + list($dummy, $ip, $dummy2, $netmask) = explode(" ", $line); + + return array($ip, long2ip(hexdec($netmask))); +} + +?>
\ No newline at end of file diff --git a/config/lightsquid/zhabascript.js b/config/lightsquid/zhabascript.js new file mode 100644 index 00000000..311e5fe9 --- /dev/null +++ b/config/lightsquid/zhabascript.js @@ -0,0 +1,118 @@ +/*********************************************** +* Cool DHTML tooltip script- © Dynamic Drive DHTML code library (www.dynamicdrive.com) +* This notice MUST stay intact for legal use +* Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code +***********************************************/ + +var offsetxpoint=-60 //Customize x offset of tooltip +var offsetypoint=20 //Customize y offset of tooltip +var ie=document.all +var ns6=document.getElementById && !document.all +var enabletip=false +var tipobj=false; + +function jsInit(){ + + if (ie||ns6) + tipobj=document.all? document.all["dhtmltooltip"] : document.getElementById? document.getElementById("dhtmltooltip") : "" + //alert(tipobj); +} + +/*********************************************** +* Cool DHTML tooltip script II- © Dynamic Drive DHTML code library (www.dynamicdrive.com) +* This notice MUST stay intact for legal use +* Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code +***********************************************/ + +var offsetfromcursorX=12 //Customize x offset of tooltip +var offsetfromcursorY=10 //Customize y offset of tooltip + +var offsetdivfrompointerX=10 //Customize x offset of tooltip DIV relative to pointer image +var offsetdivfrompointerY=14 //Customize y offset of tooltip DIV relative to pointer image. Tip: Set it to (height_of_pointer_image-1). + +//document.write('<div id="dhtmltooltip"></div>') //write out tooltip DIV +document.write('<img id="dhtmlpointer" src="arrow.gif">') //write out pointer image + +var ie=document.all +var ns6=document.getElementById && !document.all +var enabletip=false +if (ie||ns6) + var tipobj=document.all? document.all["dhtmltooltip"] : document.getElementById? document.getElementById("dhtmltooltip") : "" + +var pointerobj=document.all? document.all["dhtmlpointer"] : document.getElementById? document.getElementById("dhtmlpointer") : "" + +function ietruebody(){ + return (document.compatMode && document.compatMode!="BackCompat")? document.documentElement : document.body +} + +function ddrivetip(thetext, thewidth, thecolor){ + if(!tipobj) return false; + if (ns6||ie){ + if (typeof thewidth!="undefined") tipobj.style.width=thewidth+"px" + if (typeof thecolor!="undefined" && thecolor!="") tipobj.style.backgroundColor=thecolor + tipobj.innerHTML=thetext + enabletip=true + return false + } +} + +function positiontip(e){ + if (enabletip){ + var nondefaultpos=false + var curX=(ns6)?e.pageX : event.clientX+ietruebody().scrollLeft; + var curY=(ns6)?e.pageY : event.clientY+ietruebody().scrollTop; + //Find out how close the mouse is to the corner of the window + var winwidth=ie&&!window.opera? ietruebody().clientWidth : window.innerWidth-20 + var winheight=ie&&!window.opera? ietruebody().clientHeight : window.innerHeight-20 + + var rightedge=ie&&!window.opera? winwidth-event.clientX-offsetfromcursorX : winwidth-e.clientX-offsetfromcursorX + var bottomedge=ie&&!window.opera? winheight-event.clientY-offsetfromcursorY : winheight-e.clientY-offsetfromcursorY + + var leftedge=(offsetfromcursorX<0)? offsetfromcursorX*(-1) : -1000 + + //if the horizontal distance isn't enough to accomodate the width of the context menu +/* if (rightedge<tipobj.offsetWidth){ + //move the horizontal position of the menu to the left by it's width + tipobj.style.left=curX-tipobj.offsetWidth+"px" + nondefaultpos=true + alert(1); + } + else */ + if (curX<leftedge) + tipobj.style.left="5px" + else{ + //position the horizontal position of the menu where the mouse is positioned + tipobj.style.left=curX+offsetfromcursorX-offsetdivfrompointerX+"px" + pointerobj.style.left=curX+offsetfromcursorX+"px" + } + + //same concept with the vertical position + if (bottomedge<tipobj.offsetHeight){ + tipobj.style.top=curY-tipobj.offsetHeight-offsetfromcursorY+"px" + nondefaultpos=true + } + else{ + tipobj.style.top=curY+offsetfromcursorY+offsetdivfrompointerY+"px" + pointerobj.style.top=curY+offsetfromcursorY+"px" + } + tipobj.style.visibility="visible" + if (!nondefaultpos) + pointerobj.style.visibility="visible" + else + pointerobj.style.visibility="hidden" + } +} + +function hideddrivetip(){ + if (ns6||ie){ + enabletip=false + tipobj.style.visibility="hidden" + pointerobj.style.visibility="hidden" + tipobj.style.left="-1000px" + tipobj.style.backgroundColor='' + tipobj.style.width='' + } +} + +document.onmousemove=positiontip + |