<?php
/*    ***************************************
    HSDN MIRRORING CACHER Version: 1.0.11 RC1

    (c)    2007-2008, Information Networks Ltd. 
        All rights reserved.
        URL: http://www.hsdn.org
        Email: info@hsdn.org


    WARRANTY
    ***************************************
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.


    .htaccess directives:
    ***************************************
    DirectoryIndex mcacher.php
    ErrorDocument 403 /mcacher.php
    ErrorDocument 404 /mcacher.php
    RewriteEngine on
    RewriteBase /
    RewriteCond $1 !^(robots.txt)
    RewriteRule ^(.*)$ /mcacher.php

*/
error_reporting(0);


/*    ***************************************
    SETTINGS SECTION
*/

$version "1.0.11 RC1"// Program version
$cache_dir "../cache"// Cache Directory
$cache_time "30"// Cache Time
$no_cache = array(); // No Cached Pages

$host ""// Server address
$port "80"// Server port
$name ""// Server Host
$cache_time = ($cache_time*60*60)*24// Days

/*    ***************************************
    GENERAL SECTION
*/

// Getting URI
$uri $_SERVER["REQUEST_URI"];
$arg substr($uri,1);

// HTTP Request
$agent $_SERVER["HTTP_USER_AGENT"];
$client $_SERVER["REMOTE_ADDR"];
$request =
    
"GET /".$arg." HTTP/1.0\r\n".
    
"Host: ".$name."\r\n".
    
"Accept: */*\r\n".
    
"Accept-Language: ru\r\n".
    
"User-Agent: ".$agent."\r\n".
    
"X-Forwarded-For: ".$client."\r\n".
    
"\r\n";

// Gen MD5 Hash
$md5_str md5($arg); 
if(!
$md5_strerrstr("Internal Program Error!","Cache File is not specified.");

 
// Cache File location
$cache_file $cache_dir."/".$md5_str;
if(!
$cache_direrrstr("Internal Program Error!","Cache Directory is not specified.");

// Check No Cache array
$exclude false;

foreach(
$no_cache as $val)
{
    if(
eregi($val,$uri))
    {
        
$exclude true;
        break;
    }
}

if(
$exclude && $arg)
{
    
$data_array no_cache($host,$port,$request,$cache_file); // No create Cache
}
else
{
    
// Check Cache File
    
if(file_exists($cache_file)) 
    {
        
// Get File time
        
$cachelink_time filemtime($cache_file);
        if(!
$cachelink_timeerrstr("Internal Program Error!","Can't get Cache File time.");

        
// Check File time
        
if ((time() - $cache_time) < $cachelink_time
        {
            
$data_array read_cache($cache_file); // Read Cache
        
}
        else
        {
            
$data_array create_cache($host,$port,$request,$cache_file); // Create Cache
        
}
    }
    else
    {
        
$data_array create_cache($host,$port,$request,$cache_file); // Create Cache
    
}
}

$headers_array parse_headers($data_array[0]); // Parse Headers

// Print Headers
if(count($headers_array) != 0)
{
    while(list(
$key,$value) = each($headers_array)) 
    {
        if(
$key != "Transfer-Encoding"// Exclude Header
        
header($key.": ".$value);
    }
}
else
{
    
errstr("Remote Node Error!","Bad Headers received.");
}

// Wrappers
$head header_wrapper($data_array[0]);
$body body_wrapper($data_array[1],$headers_array["Content-Type"]);

// Print Body
if(!$bodyerrstr("Remote Node Error!","Bad Source Data received.");
print 
$body;


/*    ***************************************
    GENERAL FUNCTIONS SECTION
*/

// No Cache
function no_cache($host,$port,$request
{
    
// Delete old Cache File
    
if(file_exists($cache_file)) unlink ($cache_file);

    
// Connect to Socket
    
if ($fp create_socket($host,$port))
    {
        
write_socket($fp,$request); // Write to Socket

        // Get Data from Socket
        
$head read_socket_header($fp);
        
$body read_socket_body($fp);
        return array(
$head,$body);
    }
}

// Create Cache
function create_cache($host,$port,$request,$cache_file=NULL
{
    
// Connect to Socket
    
if ($fp create_socket($host,$port))
    {
        
write_socket($fp,$request); // Write to Socket
        
$head read_socket_header($fp); // Get Headers from Socket
        
$headers_array parse_headers($head); // Parse Headers

        // ETag Cache Menhod
        
if($headers_array["ETag"] && file_exists($cache_file))
        {
            
$data_array read_cache($cache_file); // Read Cache File
            
$cache_headers_array parse_headers($data_array[0]); // Parse Cache Headers

            
if(file_exists($cache_file) && ($headers_array["ETag"] == $cache_headers_array["ETag"])) 
            {
                
// Return values
                
$head $data_array[0];
                
$body $data_array[1];
            }
            else
            {
                
$body read_socket_body($fp); // Get Body from Socket
                
$source $head."\r\n".$body;

                
// Check Data
                
if($head && $body)
                {
                    
// Open Cache File for Write
                    
if($fw fopen($cache_file,"w"))
                    {
                        
fwrite($fw,$source);
                        
fclose($fw);
                    }
                    else
                    {
                        
errstr("Internal Program Error!","Can't open Cache File for write.");
                    }
                }
            }
        }
        
// Basic Cache Method
        
else
        {
            
$body read_socket_body($fp); // Get Body from Socket
            
$source $head."\r\n".$body;

            
// Check Data
            
if($head && $body)
            {
                
// Open Cache File for Write
                
if($fw fopen($cache_file,"w"))
                {
                    
fwrite($fw,$source);
                    
fclose($fw);
                }
                else
                {
                    
errstr("Internal Program Error!","Can't open Cache File for write.");
                }
            }
        }

        return array(
$head,$body);
    }
}

// Read Cache
function read_cache($cache_file)
{
    
// Read Cache File
    
if($fh fopen($cache_file,"r"))
    {
        
//echo "<!-- $cache_file -->";
        // Get Headers
        
$head '';
        while(!
feof($fh))
        {
            
$fgets fgets($fh,2048); 
            if (
$fgets == "\r\n" || $fgets == "\n") break;
            
$head .= $fgets;
        }

        
// Get Source Data
        
$body '';
        while(!
feof($fh)) $body .= fread($fh,4096);

        return array(
$head,$body);
    }
    else
    {
        
errstr("Internal Program Error!","Can't read ".$cache_file." Cache File.");
    }
}

// Create Socket
function create_socket($host,$port)
{
    
// Connect to Socket
    
if (!$fp fsockopen($host,$port,$errno,$errstr,10))
    
errstr("Bad Gateway!",$errstr.".");
    else
    return 
$fp;
}

// Write to Socket
function write_socket($fp,$request
{
    
fputs($fp,$request); // Write request
}

// Read Header from Socket
function read_socket_header($fp)
{
    
// Get Headers
    
$head '';
    while(!
feof($fp))
    {
        
$fgets fgets($fp,2048); 
        if (
$fgets == "\r\n" || $fgets == "\n") break;
        
$head .= $fgets;
    }
    
    if(!
$headerrstr("Remote Node Error!","Can't read Headers.");
    return 
$head;
}

// Read Body from Socket
function read_socket_body($fp)
{
    
// Get Source Data
    
$body '';
    while(!
feof($fp)) $body .= fread($fp,4096);
    
    if(!
$bodyerrstr("Remote Node Error!","Can't read Body Source.");
    return 
$body;
}

// Parse HTTP Headers
function parse_headers($headers false)
{
    if(
$headers === false) return false;
    
$headers str_replace("\r","",$headers);
    
$headers explode("\n",$headers); // Split lines

    // Gen Headers Array
    
foreach($headers as $value
    {
        
$header explode(": ",$value); // Split Header
        
if($header[0] && $header[1]) $headerdata[$header[0]] = $header[1];
    }

    return 
$headerdata;
}

// Error String
function errstr($error,$subject)
{
    global 
$name,$version;
    print 
"<html><head></head><body bgcolor=\"#CCCCFF\"><h1>".$error."</h1>".$subject."<p><hr><address>HSDN Mirroring Cacher/".$version." for <a href=\"http://".$name."\">".$name."</address></body></html>";
    exit();
}


/*    ***************************************
    WRAPPER FUNCTIONS SECTION
*/

// Head Wrapper
function header_wrapper($head
{
    
// Replace strings
    
$replace = array(
        
/*      :-)      */
    
);

    
$head replace($replace,$head);
    
    return 
$head;
}

// Body Wrapper
function body_wrapper($body,$ctype
{
    global 
$version,$arg,$name;

    
// Replace strings
    
$replace = array(
        
/*      :-)      */
    
);

    
// Cute strings
    
$cute = array(
        
/*      :-)      */
    
);

    if(
ereg('text',$ctype))
    {
        
$body replace($replace,$body);
        
$body cute($cute,$body);
    }

    return 
$body;
}

// Content Cute
function cute($array,$source
{
    foreach(
$array as $val)
    {
        
$source preg_replace("|^(.*)".$val."(.*)$|isU","\\1<!-- CUT -->\\2",$source);
    }
    return 
$source;
}

// Content Replace
function replace($array,$source
{
    foreach(
$array as $key=>$val)
    {
        
$source str_replace($key,$val,$source);
    }
    return 
$source;
}

?>