= 1024 && $i < $l; $bytes /= 1024, $i++ ); // Return a rounded figure with unit return ( round($bytes, 2) . ' ' . $types[$i] ); } // Convert path to URL function pathToURL($filePath) { // Run through realpath to normalise path $realPath = realpath($filePath); // Verify that the path passed is real and find the directory if ( is_file($realPath)) { $dir = dirname($realPath); } elseif ( is_dir($realPath) ) { $dir = $realPath; } else { // Path does not exist, fails return false; } // Expand the document root path $_SERVER['DOCUMENT_ROOT'] = realpath($_SERVER['DOCUMENT_ROOT']); // Make sure the path is not lower than the server root if ( strlen($dir) < strlen($_SERVER['DOCUMENT_ROOT']) ) { return false; } // Determine path from web root $rootPos = strlen($_SERVER['DOCUMENT_ROOT']); // Make sure $rootPos includes the first slash if ( ( $tmp = substr($_SERVER['DOCUMENT_ROOT'], -1) ) && ( $tmp == '/' || $tmp == '\\' ) ) { --$rootPos; } // Extract path below webroot and discard path above webroot $pathFromRoot = substr($realPath, $rootPos); // Build URL from parts $path = 'http' . ( isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off' ? 's' : '' ) . '://' . $_SERVER['HTTP_HOST'] . $pathFromRoot; // Convert to forward slash if on Windows if ( DIRECTORY_SEPARATOR == '\\' ) { $path = str_replace('\\', '/', $path); } return $path; } // Hide from non-js browsers by using document.write() to output function jsWrite($str) { return ''; } // Convert a string of bool value to bool function bool($str) { if ( $str == 'false' ) { return false; } if ( $str == 'true' ) { return true; } return NULL; } /***************************************************************** * CLASSES ******************************************************************/ /***************************************************************** * Location wrapper - allows us to have observers on the location ******************************************************************/ class Location { // Observers private $observers; // Redirect elsewhere public function redirect($to = '') { // Notify observers $this->notifyObservers('redirect'); // Redirect and quit header('Location: ' . ADMIN_URI . '?' . $to); exit; } // Redirect elsewhere but without observer public function cleanRedirect($to = '') { // Redirect and quit header('Location: ' . ADMIN_URI . '?' . $to); exit; } // Register observers public function addObserver(&$obj) { $this->observers[] = $obj; } // Notify observers public function notifyObservers($action) { // Determine method to call $method = 'on' . ucfirst($action); // Prepare parameters $params = func_get_args(); array_shift($params); // Loop through all observers foreach ( $this->observers as $obj ) { // If an observing method exists, call it if ( method_exists($obj, $method) ) { call_user_func_array(array(&$obj, $method), $params); } } } } /***************************************************************** * Input wrapper for incoming data ******************************************************************/ class Input { // Set up inputs public function __construct() { $this->GET = $this->prepare($_GET); $this->POST = $this->prepare($_POST); $this->COOKIE = $this->prepare($_COOKIE); } // Return array with keys converted to lowercase and values cleaned private function prepare($array) { $return = array(); foreach ( $array as $key => $value ) { $return[strtolower($key)] = self::clean($value); } return $return; } // Get an input - inputs can be requested in the form pVarName // where VarName is (case insensitive) name of variable (duh!) // and p denotes from _POST. G and C are also available. public function __get($name) { // Do we have a varname? if ( ! isset($name[1]) ) { return NULL; } // Split into GPC and VarName (case insensitive) $from = strtolower($name[0]); $var = strtolower(substr($name, 1)); // Define $from to target relationships $targets = array('g' => $this->GET, 'p' => $this->POST, 'c' => $this->COOKIE); // Look for the value and return it if ( isset($targets[$from][$var]) ) { return $targets[$from][$var]; } // Not found, return false return NULL; } // Clean a value static public function clean($val) { static $magicQuotes; // What is our magic quotes setting? if ( ! isset($magicQuotes) ) { $magicQuotes = get_magic_quotes_gpc(); } // What type is this? switch ( true ) { case is_string($val): // Strip slashes and trim if ( $magicQuotes ) { $val = stripslashes($val); } $val = trim($val); break; case is_array($val): $val = array_map(array('Input', 'clean'), $val); break; default: return $val; } return $val; } } /***************************************************************** * Output wrappers ******************************************************************/ // A simple overloading object class Overloader { // Store variables in this array protected $data; // Set value (case insensitive) public function __set($name, $value) { $this->data[strtolower($name)] = $value; } // Get value (case insensitive) public function __get($name) { $name = strtolower($name); return isset($this->data[$name]) ? $this->data[$name] : ''; } } // Base wrapper object abstract class Output extends Overloader { // Full page to output protected $output; // Content only protected $content; // Array of observers protected $observers = array(); // Output the page final public function out() { // Notify our observers we're about to print $this->notifyObservers('print', $this); // Wrap content in our wrapper $this->wrap(); // Send headers $this->sendHeaders(); // Send body print $this->output; // Page completed, finish exit; } // Override this to send custom headers instead of default (html) protected function sendHeaders() {} // Wrapper for body content protected function wrap() { $this->output = $this->content; } // Add content public function addContent($content) { $this->content .= $content; } // Register observers public function addObserver(&$obj) { $this->observers[] = $obj; } // Notify observers public function notifyObservers($action) { // Determine method to call $method = 'on' . ucfirst($action); // Prepare parameters $params = func_get_args(); array_shift($params); // Loop through all observers foreach ( $this->observers as $obj ) { // If an observing method exists, call it if ( method_exists($obj, $method) ) { call_user_func_array(array(&$obj, $method), $params); } } } // Send status code public function sendStatus($code) { header(' ', true, $code); } // More overloading. Set value with key. public function __call($func, $args) { if ( substr($func, 0, 3) == 'add' && strlen($func) > 3 && ! isset($args[2]) ) { // Saving with key or not? if ( isset($args[1]) ) { $this->data[strtolower(substr($func, 3))][$args[0]] = $args[1]; } else { $this->data[strtolower(substr($func, 3))][] = $args[0]; } } } } // Output with our HTML skin class SkinOutput extends Output { // Print all private function printAll($name) { $name = strtolower($name); if ( isset($this->data[$name]) && is_array($this->data[$name]) ) { foreach ( $this->data[$name] as $item ) { echo $item; } } } // Wrap content in HTML skin protected function wrap() { // Prepare the "get image" path $imgs = ADMIN_URI . '?image='; // Self $self = ADMIN_URI; // Prepare date $date = date('H:i, d F Y'); // Append "glype control panel" to title $title = $this->title . ( $this->title ? ' : ' : '' ) . 'glype control panel'; // Buffer so we can get this into a variable ob_start(); // Print output echo << {$title}

{$this->bodyTitle}

OUT; // Do we have any error messages? if ( $this->error ) { // Print all foreach ( $this->error as $id => $message ) { echo << X {$message}
OUT; } } // Do we have any confirmation messages? if ( $this->confirm ) { // Print all foreach ( $this->confirm as $id => $message ) { echo << X {$message}
OUT; } } // Print content echo $this->content; // Print footer links if ( is_array($this->footerLinks) ) { echo '
'; } // And finish off the page echo <<
 
OUT; $this->output = ob_get_contents(); // Discard buffer ob_end_clean(); } } // Send output in "raw" form class RawOutput extends Output { protected function sendHeaders() { header('Content-Type: text/plain; charset="utf-8"'); header('Content-Disposition: inline; filename=""'); } } /***************************************************************** * User object ******************************************************************/ // Manage sessions and stores user data class User { // Username we're logged in as public $name; // Our user agent public $userAgent; // Our IP address public $IP; // Reason for aborting a session public $aborted; // Constructor sets up session public function __construct() { // Don't try to start if autostarted if ( session_id() == '' ) { // Set up new session session_name('admin'); session_start(); } // Always use a fresh ID for security session_regenerate_id(); // Prepare user data $this->userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''; $this->IP = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; // Use user-agent and IP as identifying data since these shouldn't change mid-session $authKey = $this->userAgent . $this->IP; // Do we have a stored auth key? if ( isset($_SESSION['auth_key']) ) { // Compare our current auth_key to stored key if ( $_SESSION['auth_key'] != $authKey ) { // Mismatch. Session may be stolen. $this->clear(); $this->aborted = 'Session data mismatch.'; } } else { // No stored auth key, save it $_SESSION['auth_key'] = $authKey; } // Are we verified? if ( ! empty($_SESSION['verified']) ) { $this->name = $_SESSION['verified']; } // Have we expired? Only expire if we're logged in of course... if ( $this->isAdmin() && isset($_SESSION['last_click']) && $_SESSION['last_click'] < (time() - ADMIN_TIMEOUT) ) { $this->clear(); $this->aborted = 'Your session timed out after ' . round(ADMIN_TIMEOUT/60) . ' minutes of inactivity.'; } // Set last click time $_SESSION['last_click'] = time(); } // Log out, destroy all session data public function clear() { // Clear existing session_destroy(); // Unset existing variables $_SESSION = array(); $this->name = false; // Restart session session_start(); } // Log in, saving username session for future requests public function login($name) { $this->name = $name; $_SESSION['verified'] = $name; } // Are we verified or not? public function isAdmin() { return (bool) $this->name; } } /***************************************************************** * Notice handler (errors or confirmations) ******************************************************************/ class Notice { // Storage of messages private $data = array(); // Type of notice handler private $type; // Constructor fetches any stored from session and clears session public function __construct($type) { // Save type $this->type = $type; // Array key $key = 'notice_' . $type; // Any existing? if ( isset($_SESSION[$key]) ) { // Extract $this->data = $_SESSION[$key]; // And clear unset($_SESSION[$key]); } } // Get messages public function get($id = false) { // Requesting an individual message? if ( $id !== false ) { return isset($this->data[$id]) ? $this->data[$id] : false; } // Requesting all return $this->data; } // Add message public function add($msg, $id = false) { // Add with or without an explicit key if ( $id ) { $this->data[$id] = $msg; } else { $this->data[] = $msg; } } // Do we have any messages? public function hasMsg() { return ! empty($this->data); } // Observer the print method of output public function onPrint(&$output) { $funcName = 'add' . $this->type; // Add our messages to the output object foreach ( $this->data as $msg ) { $output->{$funcName}($msg); } } // Observe redirects - store notices in session public function onRedirect() { $_SESSION['notice_' . $this->type] = $this->data; } } /***************************************************************** * Initialise instances of defined classes. If we were structing * this nicely we'd stick the above in separate files to keep it * clean but we're sacrificing good structure for the convenience * of running this admin script stand-alone. ******************************************************************/ // Create output object $output = new SkinOutput; // Create an overloader object to hold our template vars. // This keeps them all together and avoids problems with undefined variable notices. $tpl = new Overloader; // Location wrapper for redirections $location = new Location; // Create user object $user = new User(); // Create notice handlers $confirm = new Notice('confirm'); $error = new Notice('error'); // Input wrapper $input = new Input; /***************************************************************** * Nearly finished preparing, now just bind them together as appropriate ******************************************************************/ // Add notice handlers as observers of the output object $output->addObserver($confirm); $output->addObserver($error); // Add notice handlers as observers on redirect(); $location->addObserver($confirm); $location->addObserver($error); // Pass user details to output object $output->admin = $user->name; /***************************************************************** * AJAX INTERCEPTS ******************************************************************/ if ( $input->gFetch && $user->isAdmin() ) { // Stop caching of response header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT' ); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); switch ( $input->gFetch ) { // Get the latest version from glype.com case 'latest-version': echo @file_get_contents('http://www.glype.com/feeds/proxy-version.php'); break; // Get the latest news case 'news': // Style the news echo ''; // Connect to glype if ( $ch = curl_init('http://www.glype.com/feeds/news.php') ) { // Set timeout curl_setopt($ch, CURLOPT_TIMEOUT, 2); // Grab feed $success = curl_exec($ch); curl_close($ch); } // Ensure we have a return if ( empty($success) ) { echo 'Currently unable to connect to glype.com for a news update.'; } break; // Verify a directory exists and is writable case 'test-dir': $fail = false; // Verify if ( ! ( $dir = $input->gDir ) ) { // Check we have a dir to test $fail = 'no directory given'; } else if ( ! file_exists($dir) || ! is_dir($dir) ) { // Check it exists and is actually a directory $fail = 'directory does not exist'; // Try to create it (in case it was inside the temporary directory) if ( ! bool($input->gTmp) && is_writable(dirname($dir)) && @mkdir($dir, 0755, true) ) { // Reset error messages and delete directory $fail = false; $ok = 'directory does not exist but can be created'; rmdir($dir); } } else if ( ! is_writable($dir) ) { // Make sure it's writable $fail = 'directory not writable - permission denied'; } else { // OK $ok = 'directory exists and is writable'; } // Print result if ( $fail ) { echo 'Error: ', $fail; } else { echo 'OK: ', $ok; } break; } // Finish here exit; } /***************************************************************** * Did our settings file load? If not, nothing else we can do. ******************************************************************/ if ( ! $settingsLoaded ) { // Show error and exit $error->add('The settings file for your glype proxy could not be found. Please upload this tool into your root glype directory. If you wish to run this script from another location, edit the configuration options at the top of the file.

Attempted to load: ' . ADMIN_GLYPE_SETTINGS . ''); $output->out(); } /***************************************************************** * Verify a valid action and force to something else if not. ******************************************************************/ // Are we an admin? If not, force login page. if ( ! $user->isAdmin() ) { $action = 'login'; } // Do we even have any user details? If not, force installer. if ( ! isset($adminDetails) ) { $action = 'install'; } /***************************************************************** * Prepare template variables ******************************************************************/ // URI to self $self = ADMIN_URI; // Links to other sections of the control panel if ( $user->isAdmin() ) { $output->addNavigation('Home', $self); $output->addNavigation('Edit Settings', $self . '?settings'); $output->addNavigation('Cache Status', $self . '?cache'); $output->addNavigation('View Logs', $self . '?logs'); } /***************************************************************** * Process current request. ******************************************************************/ switch ( $action ) { /***************************************************************** * INSTALL - save an admin username/password in our settings file ******************************************************************/ case 'install': // Do we have any admin details already? if ( isset($adminDetails) ) { // Add error $error->add('An administrator account already exists. For security reasons, you must manually create additional administrator accounts.'); // And redirect to index $location->redirect(); } // Do we have any submitted details to process? if ( $input->pSubmit ) { // Verify inputs if ( ! ( $username = $input->pAdminUsername ) ) { $error->add('You must enter a username to protect access to your control panel!'); } if ( ! ( $password = $input->pAdminPassword ) ) { $error->add('You must enter a password to protect access to your control panel!'); } // In case things go wrong, add this into the template $tpl->username = $username; // Process the installation if no errors if ( ! $error->hasMsg() && is_writable(ADMIN_GLYPE_SETTINGS) ) { // Load up the file $file = file_get_contents(ADMIN_GLYPE_SETTINGS); // Clear any closing php tag ? > (unnecessary and gets in the way) if ( substr(trim($file), -2) == '?>' ) { $file = substr(trim($file), 0, -2); } // Look for a "Preserve Me" section if ( strpos($file, '//---PRESERVE ME---') === false ) { // If it doesn't exist, add it $file .= "\n//---PRESERVE ME--- // Anything below this line will be preserved when the admin control panel rewrites // the settings. Useful for storing settings that don't/can't be changed from the control panel\n"; } // Prepare the inputs $password = md5($password); // Add to file $file .= "\n\$adminDetails[" . quote($username) . "] = " . quote($password) . ";\n"; // Save updated file if ( file_put_contents(ADMIN_GLYPE_SETTINGS, $file) ) { // Add confirmation $confirm->add('Installation successful. You have added ' . $username . ' as an administrator and are now logged in.'); // Log in the installer $user->login($username); } else { // Add error message $error->add('Installation failed. The settings file appears writable but file_put_contents() failed.'); } // Redirect $location->redirect(); } } // Prepare skin variables $output->title = 'install'; $output->bodyTitle = 'First time use installation'; // Add javascript $output->addDomReady("document.getElementById('username').focus();"); // Is the settings file writable? if ( ! ( $writable = is_writable(ADMIN_GLYPE_SETTINGS) ) ) { $error->add('The settings file was found at ' . ADMIN_GLYPE_SETTINGS . ' but is not writable. Please set the appropriate permissions to make the settings file writable.'); // And disable the submit button $tpl->disabled = ' disabled="disabled"'; } else { $confirm->add('Settings file was found and is writable. Installation can proceed. Do not leave the script at this stage!'); } // Print form echo <<No administrator details were found in the settings file. Enter a username and password below to continue. The details supplied will be required on all future attempts to use this control panel.

Username:
Password:

disabled}>

OUT; break; /***************************************************************** * LOG IN ******************************************************************/ case 'login': // Do we have any login details to process? if ( $input->pLoginSubmit ) { // Verify inputs if ( ! ( $username = $input->pAdminUsername ) ) { $error->add('You did not enter your username. Please try again.'); } if ( ! ( $password = $input->pAdminPassword ) ) { $error->add('You did not enter your password. Please try again.'); } // Validate the submitted details if ( ! $error->hasMsg() ) { // Validate submitted password if ( isset($adminDetails[$username]) && $adminDetails[$username] == md5($password) ) { // Update user $user->login($username); // Redirect to index $location->cleanRedirect(); } else { // Incorrect password $error->add('The login details you submitted were incorrect.'); } } } // Have we been automatically logged out? if ( $user->aborted ) { $error->add($user->aborted); } // Set up page titles $output->title = 'log in'; $output->bodyTitle = 'Log in'; // Add javascript $output->addDomReady("document.getElementById('username').focus();"); // Show form echo <<This is a restricted area for authorised users only. Enter your log in details below.

Username:
Password:

OUT; break; /***************************************************************** * LOG OUT ******************************************************************/ case 'logout': // Clear all user data $user->clear(); // Print confirmation $confirm->add('You are now logged out.'); // Redirect back to login page $location->redirect('login'); break; /***************************************************************** * INDEX - check status and print summary ******************************************************************/ case '': // // System requirements // $requirements = array(); // PHP VERSION ---------------------- // Find PHP version - may be bundled OS so strip that out $phpVersion = ( $tmp = strpos(PHP_VERSION, '-') ) ? substr(PHP_VERSION, 0, $tmp) : PHP_VERSION; // Check above 5 and if not, add error text if ( ! ( $ok = version_compare($phpVersion, '5', '>=') ) ) { $error->add('glype proxy requires at least PHP 5 or greater.'); } // Add to requirements $requirements[] = array('name' => 'PHP version', 'value' => $phpVersion, 'ok' => $ok); // CURL ------------------------------- // Check for libcurl if ( ! ( $ok = function_exists('curl_version') ) ) { $error->add('glype proxy requires cURL/libcurl.'); } // curl version $curlVersion = $ok && ( $tmp = curl_version() ) ? $tmp['version'] : 'not available'; // Add to requirements $requirements[] = array('name' => 'cURL version', 'value' => $curlVersion, 'ok' => $ok); // -------------------------------------- // Print page header $output->bodyTitle = 'Welcome to your control panel'; echo <<This script provides an easy to use interface for managing your glype proxy. Use the navigation above to get started.

Checking environment...

    OUT; // Print requirements foreach ( $requirements as $li ) { echo "
  • {$li['name']}: {$li['value']}
  • \n"; } // End requirements echo << OUT; // How are we doing - tell user if we're OK or not. if ( $error->hasMsg() ) { echo '

    Environment check failed. You will not be able to run glype proxy until you fix the above issue(s).

    '; } else { echo '

    Environment okay. You can run glype proxy on this server.

    '; } // // Script versions // $acpVersion = ADMIN_VERSION; $proxyVersion = isset($CONFIG['version']) ? $CONFIG['version'] : 'unknown - pre 1.0'; // Create javascript to update the latest stable version $javascript = <<addJavascript($javascript); $output->addDomReady("runAjax('$self?fetch=latest-version', null, updateLatestVersion);"); // Print version summary echo <<

    Checking script versions...

    • Admin control panel: {$acpVersion}
    • Glype proxy version: {$proxyVersion}
    • Latest stable version: unknown
    OUT; // Is the settings file up to date? function forCompare($val) { return str_replace(' ', '', $val); } if ( $proxyVersion != 'unknown - pre 1.0' && version_compare(forCompare($acpVersion), forCompare($proxyVersion), '>') ) { echo "

    Note: Your settings file needs updating. Use the Edit Settings page and click Update.

    "; } // // Glype news // echo <<

    Latest glype news...

    OUT; // Add footer links $output->addFooterLinks('glype documentation', 'http://docs.glype.com/'); $output->addFooterLinks('glype support forums', 'http://forums.glype.com/'); break; /***************************************************************** * SETTINGS ******************************************************************/ case 'settings': // Check the settings are writable if ( ! is_writable(ADMIN_GLYPE_SETTINGS) ) { $error->add('The settings file is not writable. You will not be able to save any changes. Please set permissions to allow PHP to write to ' . realpath(ADMIN_GLYPE_SETTINGS) . ''); $tpl->disabled = ' disabled="disabled"'; } // Load options into object $options = simplexml_load_string('
    ";}}return $options;]]>siteSKIN
    You may be held reponsible for requests from your proxy\'s IP address. You can use logs to record the unencoded URLs of pages visited by users in case of illegal activity undertaken through your proxy. Unencoded logging is most useful if using the unique URLs option.

    ]]>
    You can restrict access to websites through your proxy with either a whitelist or a blacklist:

    • Whitelist: any site that is not on the list will be blocked.
    • Blacklist: any site that is on the list will be blocked
    ]]>
    You can ban users from accessing your proxy by IP address. You can specify individual IP addresses or IP address ranges in the following formats:

    • 127.0.0.1
    • 127.0.0.1-127.0.0.5
    • 127.0.0.1/255.255.255.255
    • 192.168.17.1/16
    • 189.128/11
    ]]>
    All cookies must be sent to the proxy script. The script can then choose the correct cookies to forward to the target server. However there are finite limitsin both the client\'s storage space and the size of the request Cookie: header thatthe server will accept. For prolonged browsing, you may wish to store cookiesserver side to avoid this problem.


    This has obvious privacy issues - if using this option, ensure your site clearlystates how it handles cookies and protect the cookie data from unauthorised access.

    ]]>
    '); // // SAVE CHANGES // if ( $input->pSubmit && ! $error->hasMsg() ) { // Filter inputs to create valid PHP code function filter($value, $type) { switch ( $type ) { // Quote strings case 'string': default: return quote($value); // Clean integers case 'int': return intval($value); // Float case 'float': if ( is_numeric($value) ) { return $value; } return quote($value); // Create arrays - make empty array if no value, not an array with a single empty value case 'array': $args = $value ? implode(', ', array_map('quote', (array) $value)) : ''; return 'array(' . $args . ')'; // Bool - check we have a real bool and resort to default if not case 'bool': if ( bool($value) === NULL ) { $value = $option->default; } return $value; } } // Create a comment line function comment($text, $multi=false) { // Comment marker $char = $multi ? '*' : '//'; // Split and make newlines with the comment char $text = wordwrap($text, 65, "\n$char "); // Return a large comment if ( $multi ) { return '/***************************************************************** * ' . $text . ' ******************************************************************/'; } // Return a small comment return "// $text"; } // Prepare the file hader $toWrite = 'section as $section ) { // Add section header to the file $toWrite .= NL . NL . comment($section['name'], true) . NL; // Now go through this section's options foreach ( $section->option as $option ) { $key = (string) $option['key']; // Grab the posted value $value = $input->{'p' . $key}; // The user-configurable options need special treatment if ( $section['type'] == 'user' ) { // We need to save 4 values - title, desc, default and force $title = filter( ( isset($value['title']) ? $value['title'] : $option->title ), 'string'); $desc = filter( ( isset($value['desc']) ? $value['desc'] : $option->desc ), 'string'); $default = filter( ( isset($value['default']) ? $value['default'] : $option['default']), 'bool'); $force = filter( ( isset($value['force']) ? $value['force'] : $option['force'] ), 'bool'); // Write them $toWrite .= "\n\$CONFIG['options'][" . quote($key) . "] = array( 'title' => $title, 'desc' => $desc, 'default' => $default, 'force' => $force );\n"; // Finished saving, move to next continue; } // Do we have a posted value or is it forced? if ( $value === NULL || $section['forced'] ) { // Resort to default (which comes ready quoted) $value = $option->default; } else { // Yes, apply quotes and any pre-storage logic if ( $option->toStore && ($tmp = @eval($option->toStore)) ) { $value = $tmp; } // Normalize directory paths if ( $option->isDir ) { // Use forward slash only $value = str_replace('\\', '/', $value); // Add trailing slash if ( substr($value, -1) && substr($value, -1) != '/' ) { $value .= '/'; } } // Filter it according to desired var type $value = filter($value, $option['type']); // Add any relativeness if ( $option->relative && $input->{'pRelative_' . $key} ) { $value = $option->relative['to'] . ' . ' . $value; } } // Add to file (commented description and $CONFIG value) $toWrite .= NL . comment($option->desc) . NL; $toWrite .= '$CONFIG[' . quote($key) . '] = ' . $value . ';' . NL; } } // Extract any preserved details $file = file_get_contents(ADMIN_GLYPE_SETTINGS); // And add to file if ( $tmp = strpos($file, '//---PRESERVE ME---') ) { $toWrite .= NL . substr($file, $tmp); } // Finished, save to file if ( file_put_contents(ADMIN_GLYPE_SETTINGS, $toWrite) ) { $confirm->add('The settings file has been updated.'); } else { $error->add('The settings file failed to write. The file was detected as writable but file_put_contents() returned false.'); } // And redirect to reload the new settings $location->redirect('settings'); } // // SHOW FORM // // Set up page variables $output->title = 'edit settings'; $output->bodyTitle = 'Edit settings'; // Print form echo <<This page allows you to edit your configuration to customize and tweak your proxy. If an option is unclear, hover over the option name for a more detailed description. More...

    OUT; // Add an "Update" button. Functionally identical to "Save". function forCompare($val) { return str_replace(' ', '', $val); } if ( empty($CONFIG['version']) || version_compare(forCompare(ADMIN_VERSION), forCompare($CONFIG['version']), '>') ) { echo '

    Your settings file needs updating.

    '; } // Add the javascript for this page $javascript = <<

    ' . $section['name'] . '

    '; } // What type of section is this? switch ( $section['type'] ) { // Standard option/value pairs case 'settings': // Comment if ( $section->comment ) { echo '
    ',$section->comment,'
    '; } // Print table header echo << OUT; // Loop through the child options foreach ( $section->option as $option ) { // Reset variables $field = ''; // Convert to string so we can use it as an array index $key = (string) $option['key']; // Find current value (if we have one) $currentValue = isset($CONFIG[$key]) ? $CONFIG[$key] : @eval('return ' . $option->default . ';'); // If the option can be relative, find out what we're relative from if ( $option->relative ) { // Run code from options XML to get value $relativeTo = @eval('return ' . $option->relative['to'] . ';'); // Remove that from the current value $currentValue = str_replace($relativeTo, '', $currentValue, $relativeChecked); } // If the option has any "toDisplay" filtering, apply it if ( $option->toDisplay && ( $newValue = @eval($option->toDisplay) ) !== false ) { $currentValue = $newValue; } // Create attributes (these are fairly consistent in multiple option types) $attr = <<isDir ) { $attr .= " onchange=\"test{$option['key']}.changed()\""; } $field = ''; // Can we be relative to another variable? if ( $option->relative ) { // Is the box already checked? $checked = empty($relativeChecked) ? '' : ' checked="checked"'; // Escape backslashes so we can use it in javascript $relativeToEscaped = str_replace('\\', '\\\\', $relativeTo); // Add to existing field $field .= << OUT; } break; // SELECT FIELD case 'select': $field = '' . @eval($option->generateOptions). ''; break; // RADIO case 'radio': $onChecked = $currentValue ? ' checked="checked"' : ''; $offChecked = ! $currentValue ? ' checked="checked"' : ''; $field = <<   /   OUT; break; // TEXTAREA case 'textarea': $field = '
    '; break; } // Is there a description to use as tooltip? $tooltip = $option->desc ? 'class="tooltip" onmouseover="tooltip(\'' . htmlentities(addslashes($option->desc), ENT_QUOTES) . '\')" onmouseout="exit()"' : ''; // Add units if ( $option['unit'] ) { $field .= ' ' . $option['unit']; } // Any after field text to add? if ( $option->afterField ) { // Code to eval or string? $add = $option->afterField['eval'] ? @eval($option->afterField) : $option->afterField; // Add to field if ( $add ) { $field .= ' (' . $add . ')'; } } echo << {$field} OUT; // Is this a directory path we're expecting? if ( $option->isDir ) { // Write with javascript to hide from non-js browsers $write = jsWrite('(try again)'); echo <<      $write OUT; $output->addDomReady("window.test{$option['key']} = new testDir('{$option['key']}');test{$option['key']}.changed();"); } } echo ''; break; // User configurable options case 'user': // Print table header echo << Title Default Description Force ? OUT; // Find the current options $currentOptions = isset($CONFIG['options']) ? $CONFIG['options'] : array(); // Print options foreach ( $section->option as $option ) { // Get values from XML $key = (string) $option['key']; // Get values from current settings, resorted to XML if not available $title = isset($currentOptions[$key]['title']) ? $currentOptions[$key]['title'] : $option->title; $default = isset($currentOptions[$key]['default']) ? $currentOptions[$key]['default'] : bool($option['default']); $desc = isset($currentOptions[$key]['desc']) ? $currentOptions[$key]['desc'] : $option->desc; $force = isset($currentOptions[$key]['force']) ? $currentOptions[$key]['force'] : bool($option['force']); // Determine checkboxes $on = $default == true ? ' checked="checked"' : ''; $off = $default == false ? ' checked="checked"' : ''; $force = $force ? ' checked="checked"' : ''; // Row colour $row = isset($row) && $row == 'row1' ? 'row2' : 'row1'; echo <<
    OUT; } // Print table footer echo ''; break; } } // Page footer echo <<

    disabled}>

    Notes:

    • Temporary directory: many features require write access to the temporary directory. Ensure you set up the permissions accordingly if you use any of these features: caching, logging, server-side cookies, maintenance/cleanup and the server load limiter.
    • Sensitive data: some temporary files may contain personal data that should be kept private - that is, log files and server-side cookies. If using these features, protect against unauthorised access by choosing a suitable location above the webroot or using .htaccess files to deny access.
    • Relative paths: you can specify some paths as relative to other paths. For example, if logs are created in /[tmp_dir]/logs/ (as per the default setting), you can edit the value for tmp_dir and the logs path will automatically update.
    • Quick start: by default, all temporary files are created in the /tmp/ directory. Subfolders for features are created as needed. Private files are protected with .htaccess files. If running Apache, you can simply set writable permissions on the /tmp/ directory (0755 or 0777) and all features will work without further changes to the filesystem or permissions.

    ^ top

    OUT; break; /***************************************************************** * CACHE STATUS ******************************************************************/ case 'cache': // Determine status of cache, starting with enabled/disabled $status = $CONFIG['use_cache'] ? 'enabled' : 'disabled'; // Get path to folder $folderPath = isset($CONFIG['cache_path']) ? $CONFIG['cache_path'] : 'not set'; // Does the folder exist and writable? switch ( false ) { case file_exists($folderPath): $folderError = 'does not exist'; break; case is_writable($folderPath): $folderError = 'is not writable'; break; default: $folderError = false; } // Are we clearing or displaying the form? if ( $input->pClear && ! $folderError ) { // Calculate space freed $cleared = 0; // Open dir handle $handle = opendir($folderPath); // Loop through files while ( ($file = readdir($handle)) !== false ) { // Find full path $filePath = $folderPath . '/' . $file; // Ignore dot files and undeletable files if ( ( isset($file[0]) && $file[0] == '.' ) || is_dir($filePath) || ! is_writable($filePath) ) { continue; } // Add filesize to count $cleared += @filesize($filePath); // And delete the file @unlink($filePath); } $cleared = formatSize($cleared); // Close handle closedir($handle); // Show success $confirm->add('The cache has been cleared. ' . $cleared . ' of space freed.'); $location->redirect('cache'); } // Text for folder status $folderStatus = $folderError ? '' . $folderError . '' : 'exists and is writable'; // Space used if ( $folderError ) { $used = 'n/a'; $fileCount = 'n/a'; } else { // Calculate by summing $used = $fileCount = 0; $handle = opendir($folderPath); while ( ($file = readdir($handle)) !== false ) { // Ignore dot files if ( isset($file[0]) && $file[0] != '.' ) { $used += @filesize($folderPath . '/' . $file); ++$fileCount; } } $used = formatSize($used); closedir($handle); } // Total space available on disk/partion if ( $folderError ) { $total = 'n/a'; } else if ( $tmp = @disk_total_space($folderPath) ) { $total = formatSize($tmp); } else { $total = 'unable to determine'; } // Print page $output->title = 'cache'; $output->bodyTitle = 'Cache status'; echo << Cache feature: {$status} Cache folder: {$folderPath} ($folderStatus) Size of cache folder: {$used} used by {$fileCount} files Size of disk: {$total} Options:

    Note: You can also set up your proxy to automatically empty the cache periodically with the maintenance feature.

    OUT; $output->addDomReady("document.getElementById('trim').focus();"); break; /***************************************************************** * LOG INDEX ******************************************************************/ case 'logs': // Are we updating the log destination? if ( $input->pDestination !== NULL ) { // Attempt to validate path $path = realpath($input->pDestination); // Is the path OK? if ( $path ) { $confirm->add('Log folder updated.'); } else { $error->add('Log folder not updated. ' . $input->pDestination . ' does not exist.'); } // Normalize $path = str_replace('\\', '/', $path); // Add trailing slash if ( isset($path[strlen($path)-1]) && $path[strlen($path)-1] != '/' ) { $path .= '/'; } // Save in session $_SESSION['logging_destination'] = $path; // Redirect to avoid "Resend Post?" on refresh $location->redirect('logs'); } // Find status $enabled = empty($CONFIG['enable_logging']) == false; $status = $enabled ? 'enabled' : 'disabled'; $destination = isset($CONFIG['logging_destination']) ? $CONFIG['logging_destination'] : ''; // Are we overriding the real destination with some other value? if ( ! empty($_SESSION['logging_destination']) ) { $destination = $_SESSION['logging_destination']; } // Print header $output->title = 'log viewer'; $output->bodyTitle = 'Logging'; echo <<
    Logging feature: {$status}
    Log folder:

    Log files

    OUT; // Do we have any log files to analyse? if ( ! ( file_exists($destination) && is_dir($destination) && ($logFiles = scandir($destination, 1)) ) ) { // Print none and exit echo '

    No log files to analyse.

    '; break; } // Print starting table echo << OUT; // Set up starting vars $currentYearMonth = false; $first = true; $totalSize = 0; // Go through all files foreach ( $logFiles as $file ) { // Verify files is a glype log. Log files formatted as YYYY-MM-DD.log if ( ! ( strlen($file) == 14 && preg_match('#^([0-9]{4})-([0-9]{2})-([0-9]{2})\.log$#', $file, $matches) ) ) { continue; } // Extract matches list(, $yearNumeric, $monthNumeric, $dayNumeric) = $matches; // Convert filename to timestamp $timestamp = strtotime(str_replace('.log', ' 12:00 PM', $file)); // Extract time parts $month = date('F', $timestamp); $day = date('l', $timestamp); $display = date('jS', $timestamp) . ' (' . $day . ')'; $yearMonth = $yearNumeric . '-' . $monthNumeric; // Display in bold if today if ( $display == date('jS (l)') ) { $display = '' . $display . ''; } // Is this a new month? if ( $yearMonth != $currentYearMonth ) { // Print in a separate table (unless first) if ( $first == false ) { echo <<
    OUT; } // Print table header echo << OUT; // Update vars so we don't do this again until we want to $currentYearMonth = $yearMonth; $first = false; } // Format size $filesize = filesize($destination . $file); $totalSize += $filesize; $size = formatSize($filesize); // Row colour is grey if weekend $row = ( $day == 'Saturday' || $day == 'Sunday' ) ? '3' : '1'; // Print log file row echo << OUT; } // End table $total = formatSize($totalSize); echo <<

    Total space used by logs: {$total}

    Note: Raw logs open in a new window.

    Note: You can set up your proxy to automatically delete old logs with the maintenance feature.

    OUT; break; /***************************************************************** * LOG VIEWER ******************************************************************/ case 'logs-view': $output->title = 'view log'; $output->bodyTitle = 'View log file'; // Find log folder $logFolder = isset($_SESSION['logging_destination']) ? $_SESSION['logging_destination'] : $CONFIG['logging_destination']; // Verify folder is valid if ( ! file_exists($logFolder) || ! is_dir($logFolder) ) { $error->add('The log folder specified does not exist.'); break; } // Find file $file = $input->gFile ? realpath($logFolder . '/' . str_replace('..', '', $input->gFile)) : false; // What type of viewing do we want? switch ( $input->gShow ) { // Raw log file case 'raw': // Find file if ( $file == false || file_exists($file) == false ) { $error->add('The file specified does not exist.'); break; } // Use raw wrapper $output = new RawOutput; // And load file readfile($file); break; // Stats - most visited site case 'popular-sites': // Scan files to find most popular sites $scan = array(); // Find files to scan if ( $file ) { // Single file mode $scan[] = $file; // Date of log file $date = ( $fileTime = strtotime(basename($input->gFile, '.log')) ) ? date('l jS F, Y', $fileTime) : '[unknown]'; } else if ( $input->gMonth && strlen($input->gMonth) > 5 && ( $logFiles = scandir($logFolder) ) ) { // Month mode - use all files in given month foreach ( $logFiles as $file ) { // Match name if ( strpos($file, $input->gMonth) === 0 ) { $scan[] = realpath($logFolder . '/' . $file); } } // Date of log files $date = date('F Y', strtotime($input->gMonth . '-01')); } // Check we have some files to scan if ( empty($scan) ) { $error->add('No files to analyse.'); break; } // Data array $visited = array(); // Read through files foreach ( $scan as $file ) { // Allow extra time @set_time_limit(30); // Open handle to file if ( ( $handle = fopen($file, 'rb') ) === false ) { continue; } // Scan for URLs while ( ( $data = fgetcsv($handle, 2000) ) !== false ) { // Extract URLs if ( isset($data[2]) && preg_match('#(?:^|\.)([a-z0-9-]+\.(?:[a-z]{2,}|[a-z.]{5,6}))$#i', strtolower(parse_url($data[2], PHP_URL_HOST)), $tmp) ) { // Add to tally if ( isset($visited[$tmp[1]]) ) { // Increment an existing count ++$visited[$tmp[1]]; } else { // Create a new item $visited[$tmp[1]] = 1; } } } // Close handle to free resources fclose($handle); } // Sort arsort($visited); // Truncate to first X results $others = array_splice($visited, ADMIN_STATS_LIMIT); // Sum up the "others" group $others = array_sum($others); // Print header echo <<Most visited sites for {$date}
    {$month} {$yearNumeric} [popular sites]
    {$display} {$size} [raw log]   [popular sites]
    OUT; // Find largest value $max = max($visited); // Create horizontal bar chart type thing foreach ( $visited as $site => $count ) { $rowWidth = round(($count/$max)*100); // Print it echo << OUT; } // Table footer echo <<
    {$site}
    {$count}
    Others {$others}

    « Back

    OUT; break; // Anything else - ignore default: $error->add('Missing input. No log view specified.'); } break; /***************************************************************** * Everything else - 404 ******************************************************************/ default: // Send 404 status $output->sendStatus(404); // And print the error page $output->title = 'page not found'; $output->bodyTitle = 'Page Not Found (404)'; echo <<The requested page {$_SERVER['REQUEST_URI']} was not found.

    OUT; } /***************************************************************** * Send content wrapped in our theme ******************************************************************/ // Get buffer $content = ob_get_contents(); // Clear buffer ob_end_clean(); // Add content $output->addContent($content); // And print $output->out();