r1980 + lots of adjustments to fit the new RackCode authorization framework
authorDenis Ovsienko <infrastation@yandex.ru>
Tue, 17 Jun 2008 17:47:16 +0000 (17:47 +0000)
committerDenis Ovsienko <infrastation@yandex.ru>
Tue, 17 Jun 2008 17:47:16 +0000 (17:47 +0000)
find_object_ip_helper.php
inc/auth.php
inc/functions.php
inc/init.php
inc/interface.php
inc/navigation.php
inc/ophandlers.php
index.php
link_helper.php
process.php
render_image.php

index aef2aa8290d27f00be1b000b8710329fec54ebb7..96dcfcb0815a495f7329cbc4819f9a19de64229e 100644 (file)
@@ -3,7 +3,12 @@
        // This is our context.
        $pageno = 'objects';
        $tabno = 'default';
-       authorize();
+       fixContext()
+       if (!permitted())
+       {
+               renderAccessDenied();
+               die;
+       }
 ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" style="height: 100%;">
@@ -21,10 +26,7 @@ echo "<link rel=icon href='" . getFaviconURL() . "' type='image/x-icon' />";
 <h2>Choose a port:</h2><br><br>
 <input type=hidden id='ip'>
 <select size="30" id="addresses">
-<?php
-       authorize();
-       renderObjectAddressesAndNames ();
-?>
+<?php renderObjectAddressesAndNames (); ?>
 </select><br><br>
 <input type='submit' value='Proceed' onclick='if (getElementById("ip")!="") { opener.document.getElementById("remoteip").value=getElementById("ip").value; window.close();}'>
 </div>
index 95c54c9ba6dc91acb7d91e4a9e9f36e5c14d4449..6323a6c4fde1793d4ee02f45aa9ffacab864b715 100644 (file)
@@ -24,28 +24,43 @@ function authenticate ()
        }
 }
 
-// Show error unless the user is allowed access here.
-function authorize ($subject = array())
+// Merge accumulated tags into a single chain, add location-specific
+// autotags and try getting access clearance. Page and tab are mandatory,
+// operation is optional.
+function permitted ($p = NULL, $t = NULL, $o = NULL)
 {
-       global $remote_username, $expl_tags, $impl_tags, $auto_tags;
-       if (!count ($subject))
-               $subject = array_merge ($expl_tags, $impl_tags, $auto_tags);
-       if (gotClearanceForTagChain ($subject))
-               return TRUE;
-       else
-       {
-               showError ("User '${remote_username}' is not allowed to access here.");
-               die();
-       }
+       global $pageno, $tabno, $op;
+       global
+               $user_tags,
+               $auto_tags,
+               $expl_tags,
+               $impl_tags;
+
+       if ($p === NULL)
+               $p = $pageno;
+       if ($t === NULL)
+               $t = $tabno;
+       $subject = array_merge
+       (
+               $user_tags,
+               $auto_tags,
+               $expl_tags,
+               $impl_tags
+       );
+       $subject[] = array ('tag' => '$page_' . $p);
+       $subject[] = array ('tag' => '$tab_' . $t);
+       if ($o === NULL and isset ($op))
+               $subject[] = array ('tag' => '$op_' . $op);
+       return gotClearanceForTagChain ($subject);
 }
 
-// A yay/nay replacement for authorized() function.
-function probeLocation ($p = 'index', $t = 'default')
+function accessiblePath ($p, $t)
 {
-       $authz_ctx = getUserAutoTags();
-       $authz_ctx[] = array ('tag' => '$page_' . $p);
-       $authz_ctx[] = array ('tag' => '$tab_' . $t);
-       return gotClearanceForTagChain ($authz_ctx);
+       global $user_tags;
+       $subject = $user_tags;
+       $subject[] = array ('tag' => '$page_' . $p);
+       $subject[] = array ('tag' => '$tab_' . $t);
+       return gotClearanceForTagChain ($subject);
 }
 
 // This function returns TRUE, if username and password are valid.
index 3c28e3e4cccd727b9ce1b071691b60f43e5fa2ba..d583e4ba28420d97c24fa738d615fce1dcac3b70 100644 (file)
@@ -1394,7 +1394,7 @@ function loadRackObjectAutoTags ()
        $object_id = $_REQUEST['object_id'];
        $oinfo = getObjectInfo ($object_id);
        $ret = array();
-       $ret[] = array ('tag' => '$id_' . $_REQUEST['object_id']);
+       $ret[] = array ('tag' => '$objectid_' . $_REQUEST['object_id']);
        $ret[] = array ('tag' => '$any_object');
        return $ret;
 }
@@ -1403,9 +1403,9 @@ function loadRackObjectAutoTags ()
 function getIPv4PrefixTags ($prefix)
 {
        $ret = array();
-       $ret[] = array ('tag' => '$ipv4net-' . str_replace ('.', '-', $prefix['ip']) . '-' . $prefix['mask']);
-       // FIXME: find and list tags for all parent networks
-       $ret[] = array ('tag' => '$any_ipv4net');
+       $ret[] = array ('tag' => '$ip4net-' . str_replace ('.', '-', $prefix['ip']) . '-' . $prefix['mask']);
+       // FIXME: find and list tags for all parent networks?
+       $ret[] = array ('tag' => '$any_ip4net');
        $ret[] = array ('tag' => '$any_net');
        return $ret;
 }
@@ -1415,7 +1415,7 @@ function loadIPv4PrefixAutoTags ()
        assertUIntArg ('id', __FUNCTION__);
        return array_merge
        (
-               array (array ('tag' => '$id_' . $_REQUEST['id'])),
+               array (array ('tag' => '$ip4netid_' . $_REQUEST['id'])),
                getIPv4PrefixTags (getIPRange ($_REQUEST['id']))
        );
 }
@@ -1425,7 +1425,7 @@ function loadIPv4AddressAutoTags ()
        assertIPv4Arg ('ip', __FUNCTION__);
        return array_merge
        (
-               array (array ('tag' => '$ipv4net-' . str_replace ('.', '-', $_REQUEST['ip']) . '-32')),
+               array (array ('tag' => '$ip4net-' . str_replace ('.', '-', $_REQUEST['ip']) . '-32')),
                getIPv4PrefixTags (getRangeByIP ($_REQUEST['ip']))
        );
 }
@@ -1434,7 +1434,7 @@ function loadRackAutoTags ()
 {
        assertUIntArg ('rack_id', __FUNCTION__);
        $ret = array();
-       $ret[] = array ('tag' => '$id_' . $_REQUEST['rack_id']);
+       $ret[] = array ('tag' => '$rackid_' . $_REQUEST['rack_id']);
        $ret[] = array ('tag' => '$any_rack');
        return $ret;
 }
@@ -1459,6 +1459,23 @@ function loadIPv4RSPoolAutoTags ()
        return $ret;
 }
 
+function fixContext ()
+{
+       global $pageno, $tabno, $auto_tags, $expl_tags, $impl_tags, $page;
+       if (isset ($page[$pageno]['autotagloader']))
+               $auto_tags = $page[$pageno]['autotagloader'] ();
+       if
+       (
+               isset ($page[$pageno]['tagloader']) and
+               isset ($page[$pageno]['bypass']) and
+               isset ($_REQUEST[$page[$pageno]['bypass']])
+       )
+       {
+               $expl_tags = $page[$pageno]['tagloader'] ($_REQUEST[$page[$pageno]['bypass']]);
+               $impl_tags = getImplicitTags ($expl_tags);
+       }
+}
+
 function getUserAutoTags ()
 {
        global $remote_username, $accounts;
@@ -1522,4 +1539,14 @@ function getTagFilterStr ($tagfilter = array())
        return $ret;
 }
 
+function buildRedirectURL ($pageno, $tabno, $what, $text)
+{
+       global $root, $page;
+       $url = "${root}?page=${pageno}&tab=${tabno}";
+       if (isset ($page[$pageno]['bypass']))
+               $url .= '&' . $page[$pageno]['bypass'] . '=' . $_REQUEST[$page[$pageno]['bypass']];
+       $url .= "&${what}=" . urlencode ($text);
+       return $url;
+}
+
 ?>
index 500855216404af89a3daafac487cbf779f084c24..c16220597dede70a6b59d3c37b380f2cd721ea46 100644 (file)
@@ -2,7 +2,7 @@
 /*
 *
 * This file performs RackTables initialisation. After you include it
-* from 1st-level page, don't forget to call authorize(). This is done
+* from 1st-level page, don't forget to call fixContext(). This is done
 * to allow reloading of pageno and tabno variables. pageno and tabno
 * together form security context.
 *
@@ -111,6 +111,7 @@ authenticate();
 $remote_username = $_SERVER['PHP_AUTH_USER'];
 $pageno = (isset ($_REQUEST['page'])) ? $_REQUEST['page'] : 'index';
 $tabno = (isset ($_REQUEST['tab'])) ? $_REQUEST['tab'] : 'default';
+$op = (isset ($_REQUEST['op'])) ? $_REQUEST['op'] : '';
 // Order matters here.
 $taglist = getTagList();
 $tagtree = getTagTree();
@@ -122,19 +123,12 @@ require_once 'inc/triggers.php';
 require_once 'inc/gateways.php';
 require_once 'inc/snmp.php';
 
-global $page;
+// These will be filled in by fixContext()
+$auto_tags = array();
 $expl_tags = array();
 $impl_tags = array();
-$auto_tags = getUserAutoTags();
-$auto_tags[] = array ('tag' => '$page_' . $pageno);
-$auto_tags[] = array ('tag' => '$tab_' . $tabno);
-
-if (isset ($page[$pageno]['tagloader']) and isset ($page[$pageno]['bypass']) and isset ($_REQUEST[$page[$pageno]['bypass']]))
-{
-       $expl_tags = $page[$pageno]['tagloader'] ($_REQUEST[$page[$pageno]['bypass']]);
-       $impl_tags = getImplicitTags ($expl_tags);
-}
-if (isset ($page[$pageno]['autotagloader']))
-       $auto_tags = array_merge ($auto_tags, $page[$pageno]['autotagloader'] ());
+// and this will remain constant
+$user_tags = loadUserTags ($accounts[$remote_username]['user_id']);
+$user_tags = array_merge ($user_tags, getImplicitTags ($user_tags), getUserAutoTags());
 
 ?>
index f12a12570d4b3e1d683a0ad0aa2d883e535c885a..d73fa459187e51e0673898f0588f96dd8045e5c3 100644 (file)
@@ -2649,7 +2649,7 @@ function renderSearchResults ()
                showError ('Search string cannot be empty.', __FUNCTION__);
                return;
        }
-       if (!probeLocation ('object', 'default'))
+       if (!accessiblePath ('objects', 'default'))
        {
                showError ('You are not authorized for viewing information about objects.', __FUNCTION__);
                return;
@@ -4520,7 +4520,7 @@ function renderLivePTR ($id = 0)
        echo "<form method=post action=${root}process.php>";
        echo "<input type=hidden name=page value=${pageno}>\n";
        echo "<input type=hidden name=tab value=${tabno}>\n";
-       echo "<input type=hidden name=op value=import>\n";
+       echo "<input type=hidden name=op value=importPTRData>\n";
        echo "<input type=hidden name=id value=${id}>\n";
        echo '<input type=hidden name=addrcount value=' . ($endip - $startip + 1) . ">\n";
 
@@ -5005,4 +5005,22 @@ function renderUserPasswordEditor ($user_id)
        echo '</table></form>';
 }
 
+function renderAccessDenied ()
+{
+       global $user_tags, $auto_tags, $expl_tags, $impl_tags, $pageno, $tabno;
+       echo "<table border=1 cellspacing=0 cellpadding=3 width='50%' align=center>\n";
+       echo '<tr><th colspan=2><h3>access denied</h3></th></tr>';
+       echo "<tr><th width='50%' class=tag_list_th>Explicit tags:</th><td class=tdleft>";
+       echo serializeTags ($expl_tags) . "</td></tr>\n";
+       echo "<tr><th width='50%' class=tag_list_th>Implicit tags:</th><td class=tdleft>";
+       echo serializeTags ($impl_tags) . "</td></tr>\n";
+       echo "<tr><th width='50%' class=tag_list_th>Automatic tags:</th><td class=tdleft>";
+       echo serializeTags ($auto_tags) . "</td></tr>\n";
+       echo "<tr><th width='50%' class=tag_list_th>User tags:</th><td class=tdleft>";
+       echo serializeTags ($user_tags) . "</td></tr>\n";
+       echo "<tr><th width='50%' class=tag_list_th>Requested page:</th><td class=tdleft>${pageno}</td></tr>\n";
+       echo "<tr><th width='50%' class=tag_list_th>Requested tab:</th><td class=tdleft>${tabno}</td></tr>\n";
+       echo "</table>\n";
+}
+
 ?>
index 1c193e2a637930635aaa0cc1cf655f08f10c4861..e5c465cfc1e1bc779dddf7e991f060c4db4e9f80 100644 (file)
@@ -152,7 +152,7 @@ $tabhandler['iprange']['liveptr'] = 'renderLivePTR';
 $tabhandler['iprange']['tags'] = 'renderIPv4PrefixTags';
 $trigger['iprange']['tags'] = 'trigger_tags';
 $ophandler['iprange']['properties']['editRange'] = 'editRange';
-$ophandler['iprange']['liveptr']['import'] = 'importPTRData';
+$ophandler['iprange']['liveptr']['importPTRData'] = 'importPTRData';
 $ophandler['iprange']['tags']['saveTags'] = 'saveIPv4PrefixTags';
 
 $page['ipaddress']['title_handler'] = 'dynamic_title_ipaddress';
@@ -275,6 +275,7 @@ $page['user']['parent'] = 'userlist';
 $page['user']['bypass'] = 'user_id';
 $page['user']['bypass_type'] = 'uint';
 $page['user']['tagloader'] = 'loadUserTags';
+$page['user']['autotagloader'] = 'getUserAutoTags';
 $tab['user']['default'] = 'View';
 $tab['user']['password'] = 'Change password';
 $tab['user']['tags'] = 'Tags';
@@ -424,7 +425,7 @@ function showTabs ($pageno, $tabno)
        foreach ($tab[$pageno] as $tabidx => $tabtitle)
        {
                // Hide forbidden tabs.
-               if (!probeLocation ($pageno, $tabidx))
+               if (!accessiblePath ($pageno, $tabidx))
                        continue;
                // Dynamic tabs should only be shown in certain cases (trigger exists and returns true).
                if
@@ -459,7 +460,7 @@ function getDirectChildPages ($pageno)
                (
                        isset ($cpage['parent']) and
                        $cpage['parent'] == $pageno and
-                       probeLocation ($cpageno, 'default') == TRUE
+                       accessiblePath ($cpageno, 'default') == TRUE
                )
                        $children[$cpageno] = $cpage;
        return $children;
index cdcfdedeefdc60e0037e9f492ad599faa999887e..7a675a9c917cae10e3260bc31fe85fe1a3c30d4c 100644 (file)
@@ -408,44 +408,40 @@ http://www.cisco.com/en/US/products/hw/routers/ps274/products_tech_note09186a008
 
 function updIPv4Allocation ()
 {
-       global $root, $pageno, $tabno, $page;
+       global $pageno, $tabno;
        assertIPv4Arg ('ip', __FUNCTION__);
        assertUIntArg ('object_id', __FUNCTION__);
        assertStringArg ('bond_name', __FUNCTION__, TRUE);
        assertStringArg ('bond_type', __FUNCTION__);
 
        $error = updateBond ($_REQUEST['ip'], $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
-       $bpname = $page[$pageno]['bypass'];
-       $baseurl = "${root}?page=${pageno}&tab=${tabno}&${bpname}=" . $_REQUEST[$bpname];
        if ($error != '')
-               return "${baseurl}&error=" . urlencode ($error);
+               return buildRedirectURL ($pageno, $tabno, 'error', $error);
        else
-               return "${baseurl}&message=" . urlencode ("allocation updated");
+               return buildRedirectURL ($pageno, $tabno, 'message', 'allocation updated');
 }
 
 function delIPv4Allocation ()
 {
-       global $root, $pageno, $tabno, $page;
+       global $pageno, $tabno;
        assertIPv4Arg ('ip', __FUNCTION__);
        assertUIntArg ('object_id', __FUNCTION__);
 
        $error = unbindIpFromObject ($_REQUEST['ip'], $_REQUEST['object_id']);
-       $bpname = $page[$pageno]['bypass'];
-       $baseurl = "${root}?page=${pageno}&tab=${tabno}&${bpname}=" . $_REQUEST[$bpname];
        if ($error != '')
-               return "${baseurl}&error=" . urlencode ($error);
+               return buildRedirectURL ($pageno, $tabno, 'error', $error);
        else
-               return "${baseurl}&message=" . urlencode ("deallocated");
+               return buildRedirectURL ($pageno, $tabno, 'message', 'deallocated');
 }
 
 function addIPv4Allocation ()
 {
-       global $root, $pageno, $tabno, $page;
-
+       global $pageno, $tabno;
        assertIPv4Arg ('ip', __FUNCTION__);
        assertUIntArg ('object_id', __FUNCTION__);
        assertStringArg ('bond_name', __FUNCTION__, TRUE);
        assertStringArg ('bond_type', __FUNCTION__);
+
        // Strip masklen.
        $ip = ereg_replace ('/[[:digit:]]+$', '', $_REQUEST['ip']);
        $error = bindIpToObject ($ip, $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
@@ -459,12 +455,10 @@ function addIPv4Allocation ()
                        $address['name'] = '';
                updateAddress ($ip, $address['name'], $address['reserved']);
        }
-       $bpname = $page[$pageno]['bypass'];
-       $baseurl = "${root}?page=${pageno}&tab=${tabno}&${bpname}=" . $_REQUEST[$bpname];
        if ($error != '')
-               return "${baseurl}&error=" . urlencode ($error);
+               return buildRedirectURL ($pageno, $tabno, 'error', $error);
        else
-               return "${baseurl}&message=" . urlencode ("allocated");
+               return buildRedirectURL ($pageno, $tabno, 'message', 'allocated');
 }
 
 function addIPv4Prefix ()
@@ -486,7 +480,7 @@ function delIPv4Prefix ()
 {
        global $root, $pageno, $tabno;
 
-       assertUIntArg ('id');
+       assertUIntArg ('id', __FUNCTION__);
        $error = destroyIPv4Prefix ($_REQUEST['id']);
        if ($error != '')
                return "${root}?page=${pageno}&tab=${tabno}&error=" . urlencode ($error);
@@ -496,42 +490,32 @@ function delIPv4Prefix ()
 
 function editRange ()
 {
-       global $root, $pageno, $tabno;
+       global $pageno, $tabno;
+       assertUIntArg ('id', __FUNCTION__);
+       assertStringArg ('name', __FUNCTION__);
 
-       $id = $_REQUEST['id'];
-       $name = $_REQUEST['name'];
-       $error = updateRange($id, $name);
+       $error = updateRange ($_REQUEST['id'], $_REQUEST['name']);
        if ($error != '')
-       {
-               return "${root}?page=${pageno}&tab=${tabno}&id=$id&error=".urlencode($error);
-       }
+               return buildRedirectURL ($pageno, $tabno, 'error', $error);
        else
-       {
-               return "${root}?page=${pageno}&tab=${tabno}&id=$id&message=".urlencode("Range updated");
-       }
-
+               return buildRedirectURL ($pageno, $tabno, 'message', 'IPv4 prefix updated');
 }
 
 function editAddress ()
 {
-       global $root, $pageno, $tabno;
+       global $pageno, $tabno;
+       assertIPv4Arg ('ip', __FUNCTION__);
+       assertStringArg ('name', __FUNCTION__, TRUE);
 
-       $ip = $_REQUEST['ip'];
-       $name = $_REQUEST['name'];
        if (isset ($_REQUEST['reserved']))
                $reserved = $_REQUEST['reserved'];
        else
                $reserved = 'off';
-       $error = updateAddress($ip, $name, $reserved=='on'?'yes':'no');
+       $error = updateAddress ($_REQUEST['ip'], $_REQUEST['name'], $reserved == 'on' ? 'yes' : 'no');
        if ($error != '')
-       {
-               return "${root}?page=${pageno}&tab=${tabno}&ip=$ip&error=".urlencode($error);
-       }
+               return buildRedirectURL ($pageno, $tabno, 'error', $error);
        else
-       {
-               return "${root}?page=${pageno}&tab=${tabno}&ip=$ip&message=".urlencode("Address updated");
-       }
-
+               return buildRedirectURL ($pageno, $tabno, 'message', 'IPv4 address updated');
 }
 
 function createUser ()
index 7bd304a0f40a09c99fab0f9469b00d079f8c3aa2..5f79edd5f4997bd7744efe3c92d3fcf2293f87b0 100644 (file)
--- a/index.php
+++ b/index.php
@@ -1,7 +1,13 @@
 <?php
 
 require 'inc/init.php';
-authorize();
+// no ctx override is necessary
+fixContext();
+if (!permitted())
+{
+       renderAccessDenied();
+       die;
+}
 
 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n";
 echo '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
index ab0fc5d655c60aa15984e80050b5cb33846b10f3..03552831d2d462fd239ba7e7a187e5d472bd5c96 100644 (file)
@@ -3,7 +3,12 @@
        // This is our context.
        $pageno = 'objects';
        $tabno = 'default';
-       authorize();
+       fixContext()
+       if (!permitted())
+       {
+               renderAccessDenied();
+               die;
+       }
 ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" style="height: 100%;">
index 9fb4efb5c440260b8be928b227f46763671c181b..79744004283961272d0803944bebfbcfb2368fbd 100644 (file)
@@ -1,18 +1,19 @@
 <?php
 
 require 'inc/init.php';
-authorize();
+fixContext();
 
-$op = (isset ($_REQUEST['op'])) ? $_REQUEST['op'] : '';
-
-if (!isset ($ophandler[$pageno][$tabno][$op]))
+if (empty ($op) or !isset ($ophandler[$pageno][$tabno][$op]))
 {
        showError ("Invalid request in operation broker: page '${pageno}', tab '${tabno}', op '${op}'");
        die();
 }
 
 // We have a chance to handle an error before starting HTTP header.
-$location = $ophandler[$pageno][$tabno][$op]();
+$location =
+       permitted() ?
+       $ophandler[$pageno][$tabno][$op]() :
+       buildRedirectURL ($pageno, $tabno, 'error', 'Operation not permitted!');
 header ("Location: " . $location);
 
 ?>
index 00398cbd07e8e9fd4d078470f554811bd7e0e865..443109e5418fe48dd5dedfdcab120edc216dc2f4 100644 (file)
@@ -1,22 +1,22 @@
 <?php
 
-// This page outputs PNG rack thumbnail.
-
 require 'inc/init.php';
 
-assertStringArg ('img', __FUNCTION__);
+assertStringArg ('img', 'render_image');
 switch ($_REQUEST['img'])
 {
-       case 'minirack':
-               // Thumbnails are rendered in security context of rackspace.
-               $pageno = 'rackspace';
+       case 'minirack': // rack security context
+               assertUIntArg ('rack_id', 'render_image');
+               $pageno = 'rack';
                $tabno = 'default';
-               authorize();
-               assertUIntArg ('rack_id', __FUNCTION__);
-               renderRackThumb ($_REQUEST['rack_id']);
+               fixContext();
+               if (!permitted())
+                       renderAccessDeniedImage();
+               else
+                       renderRackThumb ($_REQUEST['rack_id']);
                break;
-       case 'progressbar':
-               assertUIntArg ('done', __FUNCTION__, TRUE);
+       case 'progressbar': // no security context
+               assertUIntArg ('done', 'render_image', TRUE);
                renderProgressBarImage ($_REQUEST['done']);
                break;
        default:
@@ -33,6 +33,14 @@ function renderError ()
        imagedestroy ($img);
 }
 
+function renderAccessDeniedImage ()
+{
+       $img = imagecreatefrompng ('pix/pixel.png');
+       header("Content-type: image/png");
+       imagepng ($img);
+       imagedestroy ($img);
+}
+
 // Having a local caching array speeds things up. A little.
 function colorFromHex ($image, $hex)
 {