Index: /trunk/FACT++/www/showlog.php
===================================================================
--- /trunk/FACT++/www/showlog.php	(revision 15112)
+++ /trunk/FACT++/www/showlog.php	(revision 15112)
@@ -0,0 +1,360 @@
+<?php
+
+require_once("smartfact/config.php");
+
+function login()
+{
+    global $ldaphost;
+    global $baseDN;
+    global $groupDN;
+
+    $username = $_SERVER['PHP_AUTH_USER'];
+    $password = $_SERVER['PHP_AUTH_PW'];
+
+    $con = @ldap_connect($ldaphost);
+    if (!$con)
+        return "ldap_connect failed to ".$ldaphost;
+
+    //------------------ Look for user common name
+    $attributes = array('cn', 'mail');
+    $dn         = 'ou=People,'.$baseDN;
+    $filter     = '(uid='.$username.')';
+
+    $sr = @ldap_search($con, $dn, $filter, $attributes);
+    if (!$sr)
+        return "ldap_search failed for dn=".$dn.": ".ldap_error($con);
+
+    $srData = @ldap_get_entries($con, $sr);
+    if ($srData["count"]==0)
+        return "No results returned by ldap_get_entries for dn=".$dn.".";
+
+    $email         =$srData[0]['mail'][0];
+    $userCommonName=$srData[0]['cn'][0];
+    $userDN        =$srData[0]['dn'];
+
+    //------------------ Authenticate user
+    if (!@ldap_bind($con, $userDN, $password))
+        return "ldap_bind failed: ".ldap_error($con);
+
+    //------------------ Check if the user is in FACT ldap group
+    $attributes= array("member");
+    $filter= '(objectClass=*)';
+
+    // Get all members of the group.
+    $sr = @ldap_read($con, $groupDN, $filter, $attributes);
+    if (!$sr)
+        return "ldap_read failed for dn=".$groupDN.": ".ldap_error($con);
+
+    // retrieve the corresponding data
+    $srData = @ldap_get_entries($con, $sr);
+    if ($srData["count"]==0)
+        return "No results returned by ldap_get_entries for dn=".$dn.".";
+
+    @ldap_unbind($con);
+
+    $found = false;
+    foreach ($srData[0]['member'] as $member)
+        if (strpos($member, "cn=".$userCommonName.",")===0)
+            return "";
+
+    return "Sorry, your credentials don't match!";
+}
+
+function ascii2entities($string)
+{
+    for ($i=128; $i<256; $i++)
+    {
+        $entity  = htmlentities(chr($i), ENT_QUOTES, 'cp1252');
+        $temp    = substr($entity, 0, 1);
+        $temp   .= substr($entity, -1, 1);
+        $string  = str_replace(chr($i), $temp!='&;'?'':$entity, $string);
+    }
+    return $string;
+}
+
+function ansi_decode($matches)
+{
+    static $colors =
+        array(
+              'black',
+              'maroon',
+              'green',
+              'olive',
+              'navy',
+              'purple',
+              'teal',
+              'silver',
+              'gray',
+              'red',
+              'lime',
+              'yellow',
+              'blue',
+              'fuchsia',
+              'aqua',
+              'white'
+             );
+
+    // Default styles.
+    static $styles =
+        array(
+              'background'   => null,  // Default is defined by the stylesheet.
+              'blink'        => false,
+              'bold'         => false,
+              'color'        => null,  // Default is defined by the stylesheet.
+              //'inverse'      => false, // Cannot be expressed in terms of CSS!
+              'italic'       => false, // Not supported by DarkOwl's ANSI.
+              'line-through' => false, // Not supported by DarkOwl's ANSI.
+              'underline'    => false,
+             );
+
+    static $css = '';
+
+    // Copy the previous styles.
+    $newstyles = $styles;
+    // Extract the codes from the escape sequences.
+    preg_match_all('/\d+/', $matches[0], $matches);
+
+    // Walk through the codes.
+    foreach ($matches[0] as $code)
+    {
+        switch ($code)
+        {
+        case '0':
+            // Reset all styles.
+            $newstyles['background']   = null;
+            $newstyles['blink']        = false;
+            $newstyles['bold']         = false;
+            $newstyles['color']        = null;
+            //              $newstyles['inverse']      = false;
+            $newstyles['italic']       = false;
+            $newstyles['line-through'] = false;
+            $newstyles['underline']    = false;
+            break;
+
+        case '1':
+            // Set the bold style.
+            $newstyles['bold'] = true;
+            break;
+
+        case '3':
+            // Set the italic style.
+            $newstyles['italic'] = true;
+            break;
+
+        case '4':
+        case '21': // Actually double underline, but CSS doesn't support that yet.
+            // Set the underline style.
+            $newstyles['underline'] = true;
+            break;
+
+        case '5':
+        case '6': // Actually rapid blinking, but CSS doesn't support that.
+            // Set the blink style.
+            $newstyles['blink'] = true;
+            break;
+
+//          case '7':
+//              // Set the inverse style.
+//              $newstyles['inverse'] = true;
+//              break;
+
+        case '9':
+            // Set the line-through style.
+            $newstyles['line-through'] = true;
+            break;
+
+        case '2': // Previously incorrectly interpreted by Pueblo/UE as cancel bold, now still supported for backward compatibility.
+        case '22':
+            // Reset the bold style.
+            $newstyles['bold'] = false;
+            break;
+
+        case '23':
+            // Reset the italic style.
+            $newstyles['italic'] = false;
+            break;
+
+        case '24':
+            // Reset the underline style.
+            $newstyles['underline'] = false;
+            break;
+
+        case '25':
+            // Reset the blink style.
+            $newstyles['blink'] = false;
+            break;
+
+//          case '27':
+//              // Reset the inverse style.
+//              $newstyles['inverse'] = false;
+//              break;
+
+        case '29':
+            // Reset the line-through style.
+            $newstyles['line-through'] = false;
+            break;
+
+        case '30': case '31': case '32': case '33': case '34': case '35': case '36': case '37':
+            // Set the foreground color.
+            $newstyles['color'] = $code - 30;
+            break;
+
+        case '39':
+            // Reset the foreground color.
+            $newstyles['color'] = null;
+            break;
+
+        case '40': case '41': case '42': case '43': case '44': case '45': case '46': case '47':
+            // Set the background color.
+            $newstyles['background'] = $code - 40;
+            break;
+
+        case '49':
+            // Reset the background color.
+            $newstyles['background'] = null;
+            break;
+
+        default:
+            // Unsupported code; simply ignore.
+            break;
+        }
+    }
+
+    // Styles are effectively unchanged; return nothing.
+    if ($newstyles === $styles)
+        return '';
+
+    // Copy the new styles.
+    $styles = $newstyles;
+    // If there's a previous CSS in effect, close the <span>.
+    $html = $css ? '</span>' : '';
+    // Generate CSS.
+    $css = '';
+
+    // background-color property.
+    if (!is_null($styles['background']))
+        $css .= ($css ? ';' : '') . "background-color:{$colors[$styles['background']]}";
+
+    // text-decoration property.
+    if ($styles['blink'] || $styles['line-through'] || $styles['underline'])
+    {
+        $css .= ($css ? ';' : '') . 'text-decoration:';
+
+        if ($styles['blink'])
+            $css .= 'blink';
+
+        if ($styles['line-through'])
+            $css .= 'line-through';
+
+        if ($styles['underline'])
+            $css .= 'underline';
+    }
+
+    // font-weight property.
+    if ($styles['bold'] && is_null($styles['color']))
+        $css .= ($css ? ';' : '') . 'font-weight: bold';
+
+    // color property.
+    if (!is_null($styles['color']))
+        $css .= ($css ? ';' : '') . "color:{$colors[$styles['color'] | $styles['bold'] << 3]}";
+
+    // font-style property.
+    if ($styles['italic'])
+        $css .= ($css ? ';' : '') . 'font-style:italic';
+
+    // Generate and return the HTML.
+    if ($css)
+        $html .= "<span style=\"$css\">";
+
+    return $html;
+}
+
+function ansi2html($str)
+{
+    // Replace database strings
+    $str = preg_replace("/\ (([[:word:].-]+)(:[^ ]+)?(@))?([[:word:].-]+)(:([[:digit:]]+))?(\/([[:word:].-]+))/", " $2$4$5$8", $str);
+    // Replace special characters to their corresponding HTML entities
+    $str = ascii2entities($str);
+    // Replace ANSI codes.
+    $str = preg_replace_callback('/(?:\e\[\d+(?:;\d+)*m)+/', 'ansi_decode', "$str\033[0m");
+    // Strip ASCII bell.
+    $str = str_replace("\007", '', $str);
+    // Replace \n
+    // $str = str_replace("\n", "<br/>\n", $str);
+    // Return the parsed string.
+    return $str;
+}
+
+if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']))
+{
+    header('WWW-Authenticate: Basic realm="SmartFACT++"');
+    header('HTTP/1.0 401 Unauthorized');
+    return;
+}
+
+$rc = login();
+if ($rc!="")
+    return header('HTTP/1.0 401 '.$rc);
+
+$refresh = isset($_GET['refresh']) ? $_GET['refresh'] : -1;
+if ($refresh>0 && $refresh<60)
+   $refresh = 60;
+
+unset($_GET['refresh']);
+
+$name = empty($_GET['log']) ? "dimserver.log" : $_GET['log'].".log";
+$dir  = empty($_GET['dir']) ? "FACT++"        : $_GET['dir'];
+
+if (!strpos($name, "/")===false || !strpos($dir, "/")===false)
+    return header('HTTP/1.0 403 Access forbidden.');
+
+$filename = "/users/fact/".$dir."/".$name;
+
+$size = filesize($filename);
+if ($size>30000000) // 30MB
+    return header('HTTP/1.0 403 File too large.');
+
+// FIXME: Reading the file line by line avoids any danger that
+//        something yields a problem if files grow too large
+$file = file($filename);
+if ($file===false)
+    return header('HTTP/1.0 403 Access forbidden.');
+
+$max = 10000;
+$pos = 500;
+$n = count($file);
+if ($n>$max)
+{
+    $file[$pos] = "\n<b>[...]</b>\n\n";
+    array_splice($file, $pos+1, $n-$max);
+}
+
+?>
+<!DOCTYPE HTML>
+<html>
+<head>
+<?php
+if ($refresh>0)
+   print("<meta http-equiv='refresh' content='".$refresh."'>\n");
+?>
+<title><?php print($dir." - ".$name);?></title>
+</head>
+
+<script>
+function scroll(top)
+{
+   document.getElementById(top?'top':'bottom').scrollIntoView(top);
+}
+</script>
+<body onload="setTimeout(function(){scroll(false);},1);">
+<span onclick="scroll(true);" style="cursor:pointer;padding:0 5px 4px 7px;position:fixed;top:0;right:0;text-decoration:underline;color:navy;background-color:#f0f0f0;">go to top &uarr;</span>
+<span onclick="scroll(false);" style="cursor:pointer;padding:0 5px 4px 7px;padding-top:0px;position:fixed;bottom:0;right:0;text-decoration:underline;color:navy;background-color:#f0f0f0;">go to bottom &darr;</span>
+<H2 id="top"><?php printf("%s - %s   (%dkB)", $dir, $name, $size/1000);?></H2>
+
+<pre style="font-size:small;font-family:'Lucida Console',Monaco,monospace">
+<?php
+foreach ($file as $line)
+    print(ansi2html($line));
+?>
+</pre><span id="bottom""/></body>
+</html>
