Mercurial > hg > vamp-website
diff forum/Sources/Class-Package.php @ 76:e3e11437ecea website
Add forum code
author | Chris Cannam |
---|---|
date | Sun, 07 Jul 2013 11:25:48 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/forum/Sources/Class-Package.php Sun Jul 07 11:25:48 2013 +0200 @@ -0,0 +1,1019 @@ +<?php + +/** + * Simple Machines Forum (SMF) + * + * @package SMF + * @author Simple Machines http://www.simplemachines.org + * @copyright 2011 Simple Machines + * @license http://www.simplemachines.org/about/smf/license.php BSD + * + * @version 2.0 + */ + +if (!defined('SMF')) + die('Hacking attempt...'); + +/* The following functions are all within the xmlArray class, which is the xml + parser. There are more functions, but these are the ones that should be + used from outside the class: + + class xmlArray(string data, bool auto_trim = false, + int error_level = error_reporting(), bool is_clone = false) + - creates a new xmlArray, which is an simple xml dom parser. + - data should be the xml data or an array of, unless is_clone is true. + - auto_trim can be used to automatically trim textual data. + - error_level specifies whether notices should be generated for + missing elements and attributes. + - if is_clone is true, the xmlArray is cloned from another - used + internally only. + + string xmlArray::name() + - retrieves the name of the current element, usually ''. + + string xmlArray::fetch(string path, bool get_elements = false) + - retrieves the textual value of the specified path. + - children are parsed for text, but only textual data is returned + unless get_elements is true. + + xmlArray xmlArray::path(string path, bool return_set = false) + - finds any elements that match the path specified. + - will always return a set if there is more than one of the element + or return_set is true. + - returns in the form of a new xmlArray. + + bool xmlArray::exists(string path) + - returns whether the specified path matches at least one element. + + int xmlArray::count(string path) + - returns the number of elements the path matches. + + array xmlArray::set(string path) + - returns an array of xmlArray's matching the specified path. + - this differs from ->path(path, true) in that instead of an xmlArray + of elements, an array of xmlArray's is returned for use with foreach. + + string xmlArray::create_xml(string path = '.') + - returns the specified path as an xml file. +*/ + +// An xml array. Reads in xml, allows you to access it simply. Version 1.1. +class xmlArray +{ + // The array and debugging output level. + public $array, $debug_level, $trim; + + // Create an xml array. + // the xml data, trim elements?, debugging output level, reserved. + //ie. $xml = new xmlArray(file('data.xml')); + public function __construct($data, $auto_trim = false, $level = null, $is_clone = false) + { + // If we're using this try to get some more memory. + @ini_set('memory_limit', '32M'); + + // Set the debug level. + $this->debug_level = $level !== null ? $level : error_reporting(); + $this->trim = $auto_trim; + + // Is the data already parsed? + if ($is_clone) + { + $this->array = $data; + return; + } + + // Is the input an array? (ie. passed from file()?) + if (is_array($data)) + $data = implode('', $data); + + // Remove any xml declaration or doctype, and parse out comments and CDATA. + $data = preg_replace('/<!--.*?-->/s', '', $this->_to_cdata(preg_replace(array('/^<\?xml.+?\?' . '>/is', '/<!DOCTYPE[^>]+?' . '>/s'), '', $data))); + + // Now parse the xml! + $this->array = $this->_parse($data); + } + + // Get the root element's name. + //ie. echo $element->name(); + public function name() + { + return isset($this->array['name']) ? $this->array['name'] : ''; + } + + // Get a specified element's value or attribute by path. + // the path to the element to fetch, whether to include elements? + //ie. $data = $xml->fetch('html/head/title'); + public function fetch($path, $get_elements = false) + { + // Get the element, in array form. + $array = $this->path($path); + + if ($array === false) + return false; + + // Getting elements into this is a bit complicated... + if ($get_elements && !is_string($array)) + { + $temp = ''; + + // Use the _xml() function to get the xml data. + foreach ($array->array as $val) + { + // Skip the name and any attributes. + if (is_array($val)) + $temp .= $this->_xml($val, null); + } + + // Just get the XML data and then take out the CDATAs. + return $this->_to_cdata($temp); + } + + // Return the value - taking care to pick out all the text values. + return is_string($array) ? $array : $this->_fetch($array->array); + } + + // Get an element, returns a new xmlArray. + // the path to the element to get, always return full result set? (ie. don't contract a single item.) + //ie. $element = $xml->path('html/body'); + public function path($path, $return_full = false) + { + // Split up the path. + $path = explode('/', $path); + + // Start with a base array. + $array = $this->array; + + // For each element in the path. + foreach ($path as $el) + { + // Deal with sets.... + if (strpos($el, '[') !== false) + { + $lvl = (int) substr($el, strpos($el, '[') + 1); + $el = substr($el, 0, strpos($el, '[')); + } + // Find an attribute. + elseif (substr($el, 0, 1) == '@') + { + // It simplifies things if the attribute is already there ;). + if (isset($array[$el])) + return $array[$el]; + else + { + if (function_exists('debug_backtrace')) + { + $trace = debug_backtrace(); + $i = 0; + while ($i < count($trace) && isset($trace[$i]['class']) && $trace[$i]['class'] == get_class($this)) + $i++; + $debug = ' from ' . $trace[$i - 1]['file'] . ' on line ' . $trace[$i - 1]['line']; + } + else + $debug = ''; + + // Cause an error. + if ($this->debug_level & E_NOTICE) + trigger_error('Undefined XML attribute: ' . substr($el, 1) . $debug, E_USER_NOTICE); + return false; + } + } + else + $lvl = null; + + // Find this element. + $array = $this->_path($array, $el, $lvl); + } + + // Clean up after $lvl, for $return_full. + if ($return_full && (!isset($array['name']) || substr($array['name'], -1) != ']')) + $array = array('name' => $el . '[]', $array); + + // Create the right type of class... + $newClass = get_class($this); + + // Return a new xmlArray for the result. + return $array === false ? false : new $newClass($array, $this->trim, $this->debug_level, true); + } + + // Check if an element exists. + // the path to the element to get. + //ie. echo $xml->exists('html/body') ? 'y' : 'n'; + public function exists($path) + { + // Split up the path. + $path = explode('/', $path); + + // Start with a base array. + $array = $this->array; + + // For each element in the path. + foreach ($path as $el) + { + // Deal with sets.... + if (strpos($el, '[') !== false) + { + $lvl = (int) substr($el, strpos($el, '[') + 1); + $el = substr($el, 0, strpos($el, '[')); + } + // Find an attribute. + elseif (substr($el, 0, 1) == '@') + return isset($array[$el]); + else + $lvl = null; + + // Find this element. + $array = $this->_path($array, $el, $lvl, true); + } + + return $array !== false; + } + + // Count the number of occurances of a path. + // the path to search for. + //ie. echo $xml->count('html/head/meta'); + public function count($path) + { + // Get the element, always returning a full set. + $temp = $this->path($path, true); + + // Start at zero, then count up all the numeric keys. + $i = 0; + foreach ($temp->array as $item) + { + if (is_array($item)) + $i++; + } + + return $i; + } + + // Get an array of xmlArray's for use with foreach. + // the path to search for. + //ie. foreach ($xml->set('html/body/p') as $p) + public function set($path) + { + // None as yet, just get the path. + $array = array(); + $xml = $this->path($path, true); + + foreach ($xml->array as $val) + { + // Skip these, they aren't elements. + if (!is_array($val) || $val['name'] == '!') + continue; + + // Create the right type of class... + $newClass = get_class($this); + + // Create a new xmlArray and stick it in the array. + $array[] = new $newClass($val, $this->trim, $this->debug_level, true); + } + + return $array; + } + + // Create an xml file from an xml array. + // the path to the element. (optional) + //ie. echo $this->create_xml() + public function create_xml($path = null) + { + // Was a path specified? If so, use that array. + if ($path !== null) + { + $path = $this->path($path); + + // The path was not found + if ($path === false) + return false; + + $path = $path->array; + } + // Just use the current array. + else + $path = $this->array; + + // Add the xml declaration to the front. + return '<?xml version="1.0"?' . '>' . $this->_xml($path, 0); + } + + // Output the xml in an array form. + // the path to output. + //ie. print_r($xml->to_array()); + public function to_array($path = null) + { + // Are we doing a specific path? + if ($path !== null) + { + $path = $this->path($path); + + // The path was not found + if ($path === false) + return false; + + $path = $path->array; + } + // No, so just use the current array. + else + $path = $this->array; + + return $this->_array($path); + } + + // Parse data into an array. (privately used...) + protected function _parse($data) + { + // Start with an 'empty' array with no data. + $current = array( + ); + + // Loop until we're out of data. + while ($data != '') + { + // Find and remove the next tag. + preg_match('/\A<([\w\-:]+)((?:\s+.+?)?)([\s]?\/)?' . '>/', $data, $match); + if (isset($match[0])) + $data = preg_replace('/' . preg_quote($match[0], '/') . '/s', '', $data, 1); + + // Didn't find a tag? Keep looping.... + if (!isset($match[1]) || $match[1] == '') + { + // If there's no <, the rest is data. + if (strpos($data, '<') === false) + { + $text_value = $this->_from_cdata($data); + $data = ''; + + if ($text_value != '') + $current[] = array( + 'name' => '!', + 'value' => $text_value + ); + } + // If the < isn't immediately next to the current position... more data. + elseif (strpos($data, '<') > 0) + { + $text_value = $this->_from_cdata(substr($data, 0, strpos($data, '<'))); + $data = substr($data, strpos($data, '<')); + + if ($text_value != '') + $current[] = array( + 'name' => '!', + 'value' => $text_value + ); + } + // If we're looking at a </something> with no start, kill it. + elseif (strpos($data, '<') !== false && strpos($data, '<') == 0) + { + if (strpos($data, '<', 1) !== false) + { + $text_value = $this->_from_cdata(substr($data, 0, strpos($data, '<', 1))); + $data = substr($data, strpos($data, '<', 1)); + + if ($text_value != '') + $current[] = array( + 'name' => '!', + 'value' => $text_value + ); + } + else + { + $text_value = $this->_from_cdata($data); + $data = ''; + + if ($text_value != '') + $current[] = array( + 'name' => '!', + 'value' => $text_value + ); + } + } + + // Wait for an actual occurance of an element. + continue; + } + + // Create a new element in the array. + $el = &$current[]; + $el['name'] = $match[1]; + + // If this ISN'T empty, remove the close tag and parse the inner data. + if ((!isset($match[3]) || trim($match[3]) != '/') && (!isset($match[2]) || trim($match[2]) != '/')) + { + // Because PHP 5.2.0+ seems to croak using regex, we'll have to do this the less fun way. + $last_tag_end = strpos($data, '</' . $match[1]. '>'); + if ($last_tag_end === false) + continue; + + $offset = 0; + while (1 == 1) + { + // Where is the next start tag? + $next_tag_start = strpos($data, '<' . $match[1], $offset); + // If the next start tag is after the last end tag then we've found the right close. + if ($next_tag_start === false || $next_tag_start > $last_tag_end) + break; + + // If not then find the next ending tag. + $next_tag_end = strpos($data, '</' . $match[1]. '>', $offset); + + // Didn't find one? Then just use the last and sod it. + if ($next_tag_end === false) + break; + else + { + $last_tag_end = $next_tag_end; + $offset = $next_tag_start + 1; + } + } + // Parse the insides. + $inner_match = substr($data, 0, $last_tag_end); + // Data now starts from where this section ends. + $data = substr($data, $last_tag_end + strlen('</' . $match[1]. '>')); + + if (!empty($inner_match)) + { + // Parse the inner data. + if (strpos($inner_match, '<') !== false) + $el += $this->_parse($inner_match); + elseif (trim($inner_match) != '') + { + $text_value = $this->_from_cdata($inner_match); + if ($text_value != '') + $el[] = array( + 'name' => '!', + 'value' => $text_value + ); + } + } + } + + // If we're dealing with attributes as well, parse them out. + if (isset($match[2]) && $match[2] != '') + { + // Find all the attribute pairs in the string. + preg_match_all('/([\w:]+)="(.+?)"/', $match[2], $attr, PREG_SET_ORDER); + + // Set them as @attribute-name. + foreach ($attr as $match_attr) + $el['@' . $match_attr[1]] = $match_attr[2]; + } + } + + // Return the parsed array. + return $current; + } + + // Get a specific element's xml. (privately used...) + protected function _xml($array, $indent) + { + $indentation = $indent !== null ? ' +' . str_repeat(' ', $indent) : ''; + + // This is a set of elements, with no name... + if (is_array($array) && !isset($array['name'])) + { + $temp = ''; + foreach ($array as $val) + $temp .= $this->_xml($val, $indent); + return $temp; + } + + // This is just text! + if ($array['name'] == '!') + return $indentation . '<![CDATA[' . $array['value'] . ']]>'; + elseif (substr($array['name'], -2) == '[]') + $array['name'] = substr($array['name'], 0, -2); + + // Start the element. + $output = $indentation . '<' . $array['name']; + + $inside_elements = false; + $output_el = ''; + + // Run through and recurively output all the elements or attrbutes inside this. + foreach ($array as $k => $v) + { + if (substr($k, 0, 1) == '@') + $output .= ' ' . substr($k, 1) . '="' . $v . '"'; + elseif (is_array($v)) + { + $output_el .= $this->_xml($v, $indent === null ? null : $indent + 1); + $inside_elements = true; + } + } + + // Indent, if necessary.... then close the tag. + if ($inside_elements) + $output .= '>' . $output_el . $indentation . '</' . $array['name'] . '>'; + else + $output .= ' />'; + + return $output; + } + + // Return an element as an array... + protected function _array($array) + { + $return = array(); + $text = ''; + foreach ($array as $value) + { + if (!is_array($value) || !isset($value['name'])) + continue; + + if ($value['name'] == '!') + $text .= $value['value']; + else + $return[$value['name']] = $this->_array($value); + } + + if (empty($return)) + return $text; + else + return $return; + } + + // Parse out CDATA tags. (htmlspecialchars them...) + function _to_cdata($data) + { + $inCdata = $inComment = false; + $output = ''; + + $parts = preg_split('~(<!\[CDATA\[|\]\]>|<!--|-->)~', $data, -1, PREG_SPLIT_DELIM_CAPTURE); + foreach ($parts as $part) + { + // Handle XML comments. + if (!$inCdata && $part === '<!--') + $inComment = true; + if ($inComment && $part === '-->') + $inComment = false; + elseif ($inComment) + continue; + + // Handle Cdata blocks. + elseif (!$inComment && $part === '<![CDATA[') + $inCdata = true; + elseif ($inCdata && $part === ']]>') + $inCdata = false; + elseif ($inCdata) + $output .= htmlentities($part, ENT_QUOTES); + + // Everything else is kept as is. + else + $output .= $part; + } + + return $output; + } + + // Turn the CDATAs back to normal text. + protected function _from_cdata($data) + { + // Get the HTML translation table and reverse it. + $trans_tbl = array_flip(get_html_translation_table(HTML_ENTITIES, ENT_QUOTES)); + + // Translate all the entities out. + $data = strtr(preg_replace('~&#(\d{1,4});~e', "chr('\$1')", $data), $trans_tbl); + + return $this->trim ? trim($data) : $data; + } + + // Given an array, return the text from that array. (recursive and privately used.) + protected function _fetch($array) + { + // Don't return anything if this is just a string. + if (is_string($array)) + return ''; + + $temp = ''; + foreach ($array as $text) + { + // This means it's most likely an attribute or the name itself. + if (!isset($text['name'])) + continue; + + // This is text! + if ($text['name'] == '!') + $temp .= $text['value']; + // Another element - dive in ;). + else + $temp .= $this->_fetch($text); + } + + // Return all the bits and pieces we've put together. + return $temp; + } + + // Get a specific array by path, one level down. (privately used...) + protected function _path($array, $path, $level, $no_error = false) + { + // Is $array even an array? It might be false! + if (!is_array($array)) + return false; + + // Asking for *no* path? + if ($path == '' || $path == '.') + return $array; + $paths = explode('|', $path); + + // A * means all elements of any name. + $show_all = in_array('*', $paths); + + $results = array(); + + // Check each element. + foreach ($array as $value) + { + if (!is_array($value) || $value['name'] === '!') + continue; + + if ($show_all || in_array($value['name'], $paths)) + { + // Skip elements before "the one". + if ($level !== null && $level > 0) + $level--; + else + $results[] = $value; + } + } + + // No results found... + if (empty($results)) + { + if (function_exists('debug_backtrace')) + { + $trace = debug_backtrace(); + $i = 0; + while ($i < count($trace) && isset($trace[$i]['class']) && $trace[$i]['class'] == get_class($this)) + $i++; + $debug = ' from ' . $trace[$i - 1]['file'] . ' on line ' . $trace[$i - 1]['line']; + } + else + $debug = ''; + + // Cause an error. + if ($this->debug_level & E_NOTICE && !$no_error) + trigger_error('Undefined XML element: ' . $path . $debug, E_USER_NOTICE); + return false; + } + // Only one result. + elseif (count($results) == 1 || $level !== null) + return $results[0]; + // Return the result set. + else + return $results + array('name' => $path . '[]'); + } +} + +// http://www.faqs.org/rfcs/rfc959.html +if (!class_exists('ftp_connection')) +{ + class ftp_connection + { + public $connection, $error, $last_message, $pasv; + + // Create a new FTP connection... + public function __construct($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = 'ftpclient@simplemachines.org') + { + // Initialize variables. + $this->connection = 'no_connection'; + $this->error = false; + $this->pasv = array(); + + if ($ftp_server !== null) + $this->connect($ftp_server, $ftp_port, $ftp_user, $ftp_pass); + } + + public function connect($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = 'ftpclient@simplemachines.org') + { + if (substr($ftp_server, 0, 6) == 'ftp://') + $ftp_server = substr($ftp_server, 6); + elseif (substr($ftp_server, 0, 7) == 'ftps://') + $ftp_server = 'ssl://' . substr($ftp_server, 7); + if (substr($ftp_server, 0, 7) == 'http://') + $ftp_server = substr($ftp_server, 7); + $ftp_server = strtr($ftp_server, array('/' => '', ':' => '', '@' => '')); + + // Connect to the FTP server. + $this->connection = @fsockopen($ftp_server, $ftp_port, $err, $err, 5); + if (!$this->connection) + { + $this->error = 'bad_server'; + return; + } + + // Get the welcome message... + if (!$this->check_response(220)) + { + $this->error = 'bad_response'; + return; + } + + // Send the username, it should ask for a password. + fwrite($this->connection, 'USER ' . $ftp_user . "\r\n"); + if (!$this->check_response(331)) + { + $this->error = 'bad_username'; + return; + } + + // Now send the password... and hope it goes okay. + fwrite($this->connection, 'PASS ' . $ftp_pass . "\r\n"); + if (!$this->check_response(230)) + { + $this->error = 'bad_password'; + return; + } + } + + public function chdir($ftp_path) + { + if (!is_resource($this->connection)) + return false; + + // No slash on the end, please... + if ($ftp_path !== '/' && substr($ftp_path, -1) === '/') + $ftp_path = substr($ftp_path, 0, -1); + + fwrite($this->connection, 'CWD ' . $ftp_path . "\r\n"); + if (!$this->check_response(250)) + { + $this->error = 'bad_path'; + return false; + } + + return true; + } + + public function chmod($ftp_file, $chmod) + { + if (!is_resource($this->connection)) + return false; + + if ($ftp_file == '') + $ftp_file = '.'; + + // Convert the chmod value from octal (0777) to text ("777"). + fwrite($this->connection, 'SITE CHMOD ' . decoct($chmod) . ' ' . $ftp_file . "\r\n"); + if (!$this->check_response(200)) + { + $this->error = 'bad_file'; + return false; + } + + return true; + } + + public function unlink($ftp_file) + { + // We are actually connected, right? + if (!is_resource($this->connection)) + return false; + + // Delete file X. + fwrite($this->connection, 'DELE ' . $ftp_file . "\r\n"); + if (!$this->check_response(250)) + { + fwrite($this->connection, 'RMD ' . $ftp_file . "\r\n"); + + // Still no love? + if (!$this->check_response(250)) + { + $this->error = 'bad_file'; + return false; + } + } + + return true; + } + + public function check_response($desired) + { + // Wait for a response that isn't continued with -, but don't wait too long. + $time = time(); + do + $this->last_message = fgets($this->connection, 1024); + while ((strlen($this->last_message) < 4 || substr($this->last_message, 0, 1) == ' ' || substr($this->last_message, 3, 1) != ' ') && time() - $time < 5); + + // Was the desired response returned? + return is_array($desired) ? in_array(substr($this->last_message, 0, 3), $desired) : substr($this->last_message, 0, 3) == $desired; + } + + public function passive() + { + // We can't create a passive data connection without a primary one first being there. + if (!is_resource($this->connection)) + return false; + + // Request a passive connection - this means, we'll talk to you, you don't talk to us. + @fwrite($this->connection, 'PASV' . "\r\n"); + $time = time(); + do + $response = fgets($this->connection, 1024); + while (substr($response, 3, 1) != ' ' && time() - $time < 5); + + // If it's not 227, we weren't given an IP and port, which means it failed. + if (substr($response, 0, 4) != '227 ') + { + $this->error = 'bad_response'; + return false; + } + + // Snatch the IP and port information, or die horribly trying... + if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $response, $match) == 0) + { + $this->error = 'bad_response'; + return false; + } + + // This is pretty simple - store it for later use ;). + $this->pasv = array('ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], 'port' => $match[5] * 256 + $match[6]); + + return true; + } + + public function create_file($ftp_file) + { + // First, we have to be connected... very important. + if (!is_resource($this->connection)) + return false; + + // I'd like one passive mode, please! + if (!$this->passive()) + return false; + + // Seems logical enough, so far... + fwrite($this->connection, 'STOR ' . $ftp_file . "\r\n"); + + // Okay, now we connect to the data port. If it doesn't work out, it's probably "file already exists", etc. + $fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5); + if (!$fp || !$this->check_response(150)) + { + $this->error = 'bad_file'; + @fclose($fp); + return false; + } + + // This may look strange, but we're just closing it to indicate a zero-byte upload. + fclose($fp); + if (!$this->check_response(226)) + { + $this->error = 'bad_response'; + return false; + } + + return true; + } + + public function list_dir($ftp_path = '', $search = false) + { + // Are we even connected...? + if (!is_resource($this->connection)) + return false; + + // Passive... non-agressive... + if (!$this->passive()) + return false; + + // Get the listing! + fwrite($this->connection, 'LIST -1' . ($search ? 'R' : '') . ($ftp_path == '' ? '' : ' ' . $ftp_path) . "\r\n"); + + // Connect, assuming we've got a connection. + $fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5); + if (!$fp || !$this->check_response(array(150, 125))) + { + $this->error = 'bad_response'; + @fclose($fp); + return false; + } + + // Read in the file listing. + $data = ''; + while (!feof($fp)) + $data .= fread($fp, 4096); + fclose($fp); + + // Everything go okay? + if (!$this->check_response(226)) + { + $this->error = 'bad_response'; + return false; + } + + return $data; + } + + public function locate($file, $listing = null) + { + if ($listing === null) + $listing = $this->list_dir('', true); + $listing = explode("\n", $listing); + + @fwrite($this->connection, 'PWD' . "\r\n"); + $time = time(); + do + $response = fgets($this->connection, 1024); + while ($response[3] != ' ' && time() - $time < 5); + + // Check for 257! + if (preg_match('~^257 "(.+?)" ~', $response, $match) != 0) + $current_dir = strtr($match[1], array('""' => '"')); + else + $current_dir = ''; + + for ($i = 0, $n = count($listing); $i < $n; $i++) + { + if (trim($listing[$i]) == '' && isset($listing[$i + 1])) + { + $current_dir = substr(trim($listing[++$i]), 0, -1); + $i++; + } + + // Okay, this file's name is: + $listing[$i] = $current_dir . '/' . trim(strlen($listing[$i]) > 30 ? strrchr($listing[$i], ' ') : $listing[$i]); + + if ($file[0] == '*' && substr($listing[$i], -(strlen($file) - 1)) == substr($file, 1)) + return $listing[$i]; + if (substr($file, -1) == '*' && substr($listing[$i], 0, strlen($file) - 1) == substr($file, 0, -1)) + return $listing[$i]; + if (basename($listing[$i]) == $file || $listing[$i] == $file) + return $listing[$i]; + } + + return false; + } + + public function create_dir($ftp_dir) + { + // We must be connected to the server to do something. + if (!is_resource($this->connection)) + return false; + + // Make this new beautiful directory! + fwrite($this->connection, 'MKD ' . $ftp_dir . "\r\n"); + if (!$this->check_response(257)) + { + $this->error = 'bad_file'; + return false; + } + + return true; + } + + public function detect_path($filesystem_path, $lookup_file = null) + { + $username = ''; + + if (isset($_SERVER['DOCUMENT_ROOT'])) + { + if (preg_match('~^/home[2]?/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match)) + { + $username = $match[1]; + + $path = strtr($_SERVER['DOCUMENT_ROOT'], array('/home/' . $match[1] . '/' => '', '/home2/' . $match[1] . '/' => '')); + + if (substr($path, -1) == '/') + $path = substr($path, 0, -1); + + if (strlen(dirname($_SERVER['PHP_SELF'])) > 1) + $path .= dirname($_SERVER['PHP_SELF']); + } + elseif (substr($filesystem_path, 0, 9) == '/var/www/') + $path = substr($filesystem_path, 8); + else + $path = strtr(strtr($filesystem_path, array('\\' => '/')), array($_SERVER['DOCUMENT_ROOT'] => '')); + } + else + $path = ''; + + if (is_resource($this->connection) && $this->list_dir($path) == '') + { + $data = $this->list_dir('', true); + + if ($lookup_file === null) + $lookup_file = $_SERVER['PHP_SELF']; + + $found_path = dirname($this->locate('*' . basename(dirname($lookup_file)) . '/' . basename($lookup_file), $data)); + if ($found_path == false) + $found_path = dirname($this->locate(basename($lookup_file))); + if ($found_path != false) + $path = $found_path; + } + elseif (is_resource($this->connection)) + $found_path = true; + + return array($username, $path, isset($found_path)); + } + + public function close() + { + // Goodbye! + fwrite($this->connection, 'QUIT' . "\r\n"); + fclose($this->connection); + + return true; + } + } +} + +?> \ No newline at end of file