<?php
//if(!defined('ISDVBBS')) exit('Access denied.Sorry, you can not access this file directly.');
if( !defined('ISDVBBS') ){
	//header('HTTP/1.0 401 Unauthorized');
	header('HTTP/1.0 400 Bad Request');
	exit;
}

/**
* 此类用于解决系统自带的文件操作函数在安全模式下受限制的问题。
* 只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
* 为了保证操作的可靠性，建议您在处理文件时，传递完整的文件名，包括其完整的路径，可使用系统函数 realpath()获取完整文件名。
*
*
* @package 		dvphp
* @author		
* @version		$Revision: 1.0 $
* @date 		$Date: 2006/07/27 $
* @copyright 	http://p.dvbbs.net/license.html
*
*/
class FileHandle
{
	/**
	 * Description 此函数用于解决 copy() 在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
	 * @author 
	 * @param string 原文件名
	 * @param string 新的文件名
	 * @return boolean 将文件从 source 拷贝到 dest。如果成功则返回 TRUE，失败则返回 FALSE。
	 */
	function chmod($filename, $mode)
	{
		if (false === FileHandle::file_exists($filename)) {
			trigger_error("FileHandle::chmod({$filename}): No such file or directory", E_USER_WARNING);
			return false;
		}
		if (FileHandle::is_allowed_to_access_in_safe_mode($filename)) {
			return chmod($filename, $mode);
		} else {
			$writabledir = FileHandle::find_writable_dir();
			if (empty($writabledir)) {
				return false;
			}
			$operationcode = "if(@chmod('{$filename}', '{$mode}')){print 1;exit;};";
			$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
			if ($execresult == 1) {
				return true;
			} elseif (!empty($execresult)) {
				trigger_error("FileHandle::chmod({$filename},{$mode}): {$execresult}", E_USER_WARNING);
				return false;
			}
		}
		return false;
	}
	/**
	 * Description 此函数用于解决 copy() 在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
	 * @author 
	 * @param string 原文件名
	 * @param string 新的文件名
	 * @return boolean 将文件从 source 拷贝到 dest。如果成功则返回 TRUE，失败则返回 FALSE。
	 */
	function copy($source, $dest)
	{
		if (!FileHandle::is_file($source)) {
			trigger_error("FileHandle::copy({$source}): failed to open stream: Is not a file.", E_USER_WARNING);
			return false;
		}
		if (FileHandle::is_dir($dest)) {
			trigger_error("FileHandle::copy({$dest}): failed to open stream: Is a directory.", E_USER_WARNING);
			return false;
		}
		$tmpsource = $source;
		if (false === ($source = realpath($source))) {
			trigger_error("FileHandle::copy({$tmpsource},{$dest}): No such file or directory.", E_USER_WARNING);
			return false;
		}
		if (false === ($srcisallowed = FileHandle::is_allowed_to_access_in_safe_mode($source))) {
			$srcisallowed = FileHandle::is_allowed_to_access_in_safe_mode(dirname($source));
		}
		$destdir = realpath(dirname($dest));
		if (false === ($destisallowed = FileHandle::is_allowed_to_access_in_safe_mode($dest))) {
			if (false === $destdir) {
				trigger_error("FileHandle::copy({$tmpsource},{$dest}): No such file or directory.", E_USER_WARNING);
				return false;
			}
			$destisallowed = FileHandle::is_allowed_to_access_in_safe_mode($destdir);
		}

		if (true === $srcisallowed AND true === $destisallowed) { // 仅当 source 和 dest 的 UID 与当前脚本的 UID 相同时执行此处代码
			return copy($source, $dest);
		} else {
			$writabledir = FileHandle::find_writable_dir();
			if (empty($writabledir)) {
				return false;
			}
			if (true === $srcisallowed) { // 仅当 source 的 UID 与当前脚本的 UID 相同时执行此处代码(dest 必须与脚本运行时 UID 相同才可以执行成功)
				$tmpfile = $writabledir.'/'.strtolower(substr(base64_encode(md5(uniqid(strtotime('now'),true))),20,8));
				if (copy($source, $tmpfile)) {
					$operationcode = "if(@rename('{$tmpfile}', '{$dest}')) {print 1;exit;}";
					$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
					if ($execresult == 1) {
						return true;
					} else {
						@unlink($tmpfile);
						if (!empty($execresult)) {
							trigger_error("FileHandle::copy({$source},{$dest}): {$execresult}", E_USER_WARNING);
						}
						return false;
					}
				}
			} elseif (true === $destisallowed) {// 仅当 dest 的 UID 与当前脚本的 UID 相同时执行此处代码(source 必须与脚本运行时 UID 相同才可以执行成功)
				$destbackup = '';
				if (is_file($dest)) {
					$destbackup = $dest.'_backup_'.strtolower(substr(base64_encode(md5(uniqid(strtotime('now'),true))),20,8));
					if (!rename($dest, $destbackup)) {
						return false;
					}
				}
				if (touch($dest)) {
					$operationcode = "if(@copy('{$source}', '{$dest}')){print 1;exit;}";
					$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
					if ($execresult == 1) {
						if ($destbackup) {
							unlink($destbackup);
						}
						return true;
					} else {
						if ($destbackup) {
							rename($destbackup, $dest);
						}
						if (!empty($execresult)) {
							trigger_error("FileHandle::copy({$source},{$dest}): {$execresult}", E_USER_WARNING);
						}
						return false;
					}
				}
			} else { // source 和 dest 的 UID 与当前脚本的 UID 不相同时执行此处代码（ source 和 dest 必须与脚本运行时 UID 相同才可以执行成功）
				$operationcode = "if(@copy('{$source}', '{$dest}')){print 1;exit;}";
				$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
				if ($execresult == 1) {
					return true;
				} elseif (!empty($execresult)) {
					trigger_error("FileHandle::copy({$source},{$dest}): {$execresult}", E_USER_WARNING);
					return false;
				}
			}
		}
		return false;
	}//end function copy

	/**
	 * Description 此函数用于解决 file_exists() 在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
	 * @author 
	 * @param string 要检测的文件或目录
	 * @return boolean 如果要检测的文件或目录存在则返回 TRUE，否则返回 FALSE。
	 */
	function file_exists($filename)
	{
		if (realpath($filename)) {
			return true;
		} else {
			return false;
		}
	}//end function file_exists

	/**
	 * Description 获取目标文件的内容；此函数在系统函数 file_get_contents 存在时，将直接调用它并返回值
	 * @author 
	 * @param string 目标文件名
	 * @return string 返回目标文件的内容，如果操作失败，返回FALSE
	 */
	function file_get_contents($file)
	{
		if (function_exists('file_get_contents')) {
			return file_get_contents($file);
		} else {
			if ($fp = fopen($file, 'rb')) {
				$filecontents = '';
				//@flock($fp, 1);
				$filesize = filesize($file);
				if ($filesize > 0) {
					$filecontents = @fread($fp, $filesize);
				} elseif (false !== $filesize) {
					while (!feof($fp)) {
						$filecontents .= fgets($fp);
					}
				}
				//@flock($fp, 3);
				@fclose($fp);
				return $filecontents;
			}
		}
		return false;
	}//end function file_get_contents

	/**
	 * Description 向目标文件写入数据内容；此函数在系统函数 file_put_contents 存在时，将直接调用它并返回值
	 * @author 
	 * @param string 目标文件名
	 * @param string 要写入的数据(使用引用传递的数据)
	 * @return int 返回写入文件的字节数。失败时返回FALSE
	 */
	function file_put_contents($destfile, &$contents, $flags = null, $rescontext = null)
	{
		if (function_exists('file_put_contents')) {
			if (null !== $flags) {
				return @file_put_contents($destfile, $contents, $flags, $rescontext);
			} else {
				return @file_put_contents($destfile, $contents);
			}
		} else {
			$openmode = '';
			switch ($flags) {
				case 1: // FILE_USE_INCLUDE_PATH 
					$openmode = 'wb';
					break;
				case 0: // FILE_APPEND
				default:
					$openmode = 'ab';
					break;
			}
			if ($fp = fopen($destfile, $openmode)) {
				if (@flock($fp, 2|4)) {
					$writed = @fwrite($fp,$contents);
					@flock($fp, 3);
				} else {
					$writed = false;
				}
				@fclose($fp);
				return $writed;
			} else {
				return false;
			}
		}
	}//end function file_put_contents

	/**
	 * Description 此函数用于解决 相关文件操作函数，在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
	 * @author 
	 * @param string 在临时文件中要执行的PHP代码
	 * @return mixed 如果发生错误，返回 FALSE，否则返回PHP代码的执行结果。
	 */
	function filehandle_in_safe_mode(&$operationcode)
	{
		global $_SERVER;
		$writabledir = FileHandle::find_writable_dir();
		if (empty($writabledir)) {
			return false;
		}
		$writabledir = str_replace('\\', '/', $writabledir);
		$documentroot = str_replace('\\', '/', realpath($_SERVER['DOCUMENT_ROOT']));
		$tmpscriptname = strtolower(substr(base64_encode(md5(uniqid(strtotime('now'),true))),20,8)).'.php';
		$pathname = $writabledir.'/'.$tmpscriptname;
		$fwriteresult = false;
		if ($fp = fopen($pathname, 'w')) {
			if (flock($fp, 2)) {
			$urltokenid = md5(uniqid(rand().getmypid().getmyinode(), true));
			$phpcode = "<?php\r\n";
			$phpcode .= <<<EOT
				error_reporting(7);
				ini_set('track_errors', 'on');
				if (getenv('SERVER_ADDR') != getenv('REMOTE_ADDR')) {
					print('IP not match!');
					exit(0);
				}
				if (isset(\$_GET)) {
					\$httpget =& \$_GET;
				} elseif(isset(\$HTTP_GET_VARS)) {
					\$httpget =& \$HTTP_GET_VARS;
				} else {
					print('URL argument error!');
					exit(0);
				}
				if (empty(\$httpget)) {
					print('URL argument error!');
					exit(0);
				}
				if (\$httpget['urltokenid'] !== '{$urltokenid}') {
					print('URL token id error!');
					exit(0);
				}
				//------------------ Start user define code------------------
				\r\n{$operationcode}\r\n
				//------------------ End user define code------------------
				if (!empty(\$php_errormsg)) {
					print \$php_errormsg;
				}
				exit;
EOT;
				$phpcode .= "\r\n?>";
				$fwriteresult = fwrite($fp, $phpcode, strlen($phpcode));
			}
			fclose($fp);
		}
		if (false === $fwriteresult) {
			return false;
		}
		$scriptpath = str_replace($documentroot, '', $pathname);
		if ($scriptpath{0} !== '/') {
			$scriptpath = '/'.$scriptpath; 
		}
		$execresult = 0;
		$httpfile = 'http://'.getenv('SERVER_NAME').':'.getenv('SERVER_PORT').$scriptpath.'?urltokenid='.$urltokenid;
		if ($fp = fopen($httpfile, 'rb')) {
			$execresult = trim(fgets($fp));
			fclose($fp);
			chmod($pathname, 0777);
		}
		unlink($pathname);
		return $execresult;
	}//end function filehandle_in_safe_mode

	/**
	 * Description 此函数将在站点根目录下寻找可写的目录。
	 * @author 
	 * @param string 可选的参数，如果指定，本函数将分析给定的目录是否可以被当前脚本写入
	 * @return boolean 如果成功则返回 TRUE，失败则返回 FALSE。
	 */
	function find_writable_dir($writabledir = '')
	{
		global $_SERVER;
		static $cachewritabledir = null;
		if (!empty($writabledir) AND is_dir($writabledir) AND FileHandle::is_writable($writabledir)) {
			$writable = realpath($writabledir);
			if (false !== strpos($writable, realpath($_SERVER['DOCUMENT_ROOT']))) {
				$cachewritabledir = $writable;
			}
		}
		if (null === $cachewritabledir) {
			$documentroot = realpath($_SERVER['DOCUMENT_ROOT']);
			$LIFO_queue = array();
			if ($tmpobj = @dir($documentroot)) {
				$LIFO_index = 0;
				$LIFO_queue[0] =& $tmpobj;
				$dir =& $tmpobj;
				while ($LIFO_index >= 0 AND !empty($dir)) {
					if (false === ($entry = $dir->read())) {
						$dir->close();
						unset($LIFO_queue[$LIFO_index]);
						$LIFO_index--;
						$dir =& $LIFO_queue[$LIFO_index];
						continue;
					}
					if ('..' == $entry) {
						continue;
					}
					$currfullentry = $dir->path.'/'.$entry;
					if (is_dir($currfullentry) AND FileHandle::is_writable($currfullentry)) {
						$cachewritabledir = realpath($currfullentry);
						break;
					}
				}
				if (!empty($LIFO_queue) AND is_array($LIFO_queue)) {
					for ( ; $LIFO_index >= 0 ; $LIFO_index--) {
						$LIFO_queue[$LIFO_index]->close();
						unset($LIFO_queue[$LIFO_index]);
					}
				}
			}
		}
		if (empty($cachewritabledir) OR !is_dir($cachewritabledir)) {
			trigger_error("FileHandle::find_writable_dir({$writabledir}): Cannot find writable directory. ", E_USER_WARNING);
			$cachewritabledir = false;
		}
		return $cachewritabledir;
	}//end function find_writable_dir

	/**
	 * Description 此函数用于解决 fileperms() 在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。。
	 * @author 
	 * @param string 目标文件或目录
	 * @return int 返回文件的访问权限，如果出错则返回 FALSE。
	 */
	function fileperms($filename)
	{
		if (FileHandle::is_allowed_to_access_in_safe_mode($filename)) {
			return fileperms($filename);
		} else {
			$writabledir = FileHandle::find_writable_dir();
			if (empty($writabledir)) {
				return false;
			}
			$operationcode = "if(@fileperms('{$filename}')){print 1;exit;}";
			$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
			if ($execresult == 1) {
				return true;
			} elseif (!empty($execresult)) {
				trigger_error("FileHandle::file_exists({$filename}): {$execresult}", E_USER_WARNING);
				return false;
			}
		}
	}

	/**
	 * Description 将文件或目录从 source 拷贝到 dest；支持深层目录拷贝，暂不支持符号连接。
	 * @author 
	 * @param string 原文件或目录名
	 * @param string 新的或目录文件名
	 * @return boolean 如果成功则返回 TRUE，失败则返回 FALSE。
	 */
	function icopy($source, $dest)
	{
		$srcisallowed = FileHandle::is_allowed_to_access_in_safe_mode($source);
		$destisallowed = FileHandle::is_allowed_to_access_in_safe_mode($dest);
		if (FileHandle::is_file($source)) {
			return FileHandle::copy($source, $dest);
		} elseif (FileHandle::is_dir($source)) {
			if (FileHandle::is_file($dest)) {
				trigger_error("FileHandle::icopy({$dest}): failed to open stream: Is a file", E_USER_WARNING);
				return false;
			}
			if (!FileHandle::is_dir($dest) AND !FileHandle::mkdir($dest)) {
				return false;
			}
			$LIFO_queue = array(); // 轻便型后入先出队列
			if ($LIFO_queue[0] =& FileHandle::scandir($source)) {
				$LIFO_index = 0;
				$sourcedir = $source;
				$destdir = realpath($dest);
				while ($LIFO_index >= 0) {
					if (isset($LIFO_queue[$LIFO_index]['files'])) {
						foreach ($LIFO_queue[$LIFO_index]['files'] AS $entry) {
							FileHandle::copy($sourcedir.'/'.$entry, $destdir.'/'.$entry);
						}
						unset($LIFO_queue[$LIFO_index]['files']);
					}
					$emptydirectories = true;
					if (isset($LIFO_queue[$LIFO_index]['directories'])) {
						foreach ($LIFO_queue[$LIFO_index]['directories'] AS $index => $entry) {
							unset($LIFO_queue[$LIFO_index]['directories'][$index]);
							if ($entry == '.' OR $entry == '..') {
								continue;
							}
							$srcfullentry = $sourcedir.'/'.$entry;
							$destfullentry = $destdir.'/'.$entry;
							if (!FileHandle::is_dir($destfullentry) AND !FileHandle::mkdir($destfullentry)) {
								continue;
							}
							if ($filelist =& FileHandle::scandir($srcfullentry)) {
								$sourcedir = $srcfullentry;
								$destdir = $destfullentry;
								$LIFO_index++;
								$LIFO_queue[$LIFO_index] =& $filelist;
								$emptydirectories = false;
								break;
							}
						}
					}
					if (true === $emptydirectories) {
						unset($LIFO_queue[$LIFO_index]['directories']);
						$LIFO_index--;
						$sourcedir = dirname($sourcedir);
						$destdir = dirname($destdir);
						if ($LIFO_index < 0) {
							unset($LIFO_queue);
							break;
						}
					}
				}
			}
		} else {
			trigger_error("FileHandle::icopy({$source}): No such file or directory", E_USER_WARNING);
			return false;
		}
	}//end function icopy

	/**
	 * Description 检查是否可以在目标文件或目录上执行相关文件操作
	 * @author 
	 * @param string 要在其上执行操作的文件或目录
	 * @param boolean 对于每一个被检查的文件或目录，都有一份相关的检查缓存结果；
	 * @return boolean 如果允许文件操作，返回TRUE，否则返回 FALSE。
	 */
	function is_allowed_to_access_in_safe_mode($destination, $clearcache = false)
	{
		static $cachetest = array();
		if (false !== $clearcache) {
			unset($cachetest[$destination]);
		} elseif (isset($cachetest[$destination])) {
			return $cachetest[$destination];
		}
		$cachetest[$destination] = false;
		if (!get_cfg_var('safe_mode')) {
			$cachetest[$destination] = true;
		} elseif (false === realpath($destination)) {// realpath 不受安全模式限制
			if (false !== ($parentdir = realpath(dirname($destination)))) {// realpath 和 dirname 都不受安全模式限制
				$tmpfilename = $parentdir.'/'.md5(uniqid(rand().getmypid().getmyinode(), true));
				if (@touch($tmpfilename)) {
					$cachetest[$destination] = true;
					unlink($tmpfilename);
				}
			}
		} else {
			$myuid = @getmyuid();
			if (is_numeric($myuid) AND @fileowner($destination) == $myuid) {// fileowner 受安全模式限制返回 false
				$cachetest[$destination] = true;
			} elseif (is_dir($destination)) {// is_dir 受安全模式限制返回 false
				$tmpfilename = $destination.'/'.md5(uniqid(rand(), true));
				if (@touch($tmpfilename)) {
					$cachetest[$destination] = true;
					unlink($tmpfilename);
				}
			} elseif (is_file($destination)) {// is_file 受安全模式限制返回 false
				$cachetest[$destination] = @touch($destination);
			}
		}
		return $cachetest[$destination];
	}//end function is_allowed_to_access_in_safe_mode

	/**
	 * Description 此函数用于解决 is_dir() 在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
	 * @author 
	 * @param string 要检测的目录
	 * @return boolean 如果目录存在且为正常的文件则返回 TRUE，否则返回 FALSE。
	 */
	function is_dir($filename)
	{
		if (FileHandle::is_allowed_to_access_in_safe_mode($filename)) {
			return is_dir($filename);
		} else {
			$writabledir = FileHandle::find_writable_dir();
			if (empty($writabledir)) {
				return false;
			}
			$operationcode = "if(@is_dir('{$filename}')){print 1;exit;}";
			$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
			if ($execresult == 1) {
				return true;
			} elseif (!empty($execresult)) {
				trigger_error("FileHandle::is_dir({$filename}): {$execresult}", E_USER_WARNING);
				return false;
			}
		}
	}//end function is_dir

	/**
	 * Description 此函数用于解决 is_file() 在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
	 * @author 
	 * @param string 要检测的文件
	 * @return boolean 如果文件存在且为正常的文件则返回 TRUE，否则返回 FALSE。
	 */
	function is_file($filename)
	{
		if (FileHandle::is_allowed_to_access_in_safe_mode($filename)) {
			return is_file($filename);
		} else {
			$writabledir = FileHandle::find_writable_dir();
			if (empty($writabledir)) {
				return false;
			}
			$operationcode = "if(@is_file('{$filename}')){print 1;exit;}";
			$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
			if ($execresult == 1) {
				return true;
			} elseif (!empty($execresult)) {
				trigger_error("FileHandle::is_file({$filename}): {$execresult}", E_USER_WARNING);
				return false;
			}
		}
	}//end function is_file

	/**
	 * Description 检测目标文件是否是通过 HTTP POST 提交过来的文件
	 * @author 
	 * @param string 要检测的文件名
	 * @return boolean 如果成功返回TRUE，否则返回FALSE
	 */
	function is_uploaded_file($filename)
	{
		if( function_exists('is_uploaded_file') ){
			return is_uploaded_file($filename);
		} else{
			if (!$tmp_file = get_cfg_var('upload_tmp_dir')) {
				$tmp_file = dirname(tempnam('', ''));
			}
			$tmp_file .= '/' . basename($filename);
			/* User might have trailing slash in php.ini... */
			return (ereg_replace('/+', '/', $tmp_file) == $filename);
		}
	}//end function is_uploaded_file

	/**
	 * Description 以不计入安全模式的限制来检测目标文件或目录是否可以写入
	 * @author 
	 * @param string 在PHP5，此参数可以是一个允许进行是否可写检查的目录名
	 * @return boolean 如果可写返回TRUE，否则返回FALSE
	 */
	function is_writable($filename)
	{
		static $cache = array();
		if (isset($cache[$filename])) {
			return $cache[$filename];
		}
		static $versioncompare = '4.4.2';
		$cache[$filename] = false;
		if (FileHandle::is_allowed_to_access_in_safe_mode($filename)) {
			if (version_compare(PHP_VERSION, $versioncompare) >= 0 OR is_file($filename)) {
				$cache[$filename] = is_writable($filename);
			} elseif (is_dir($filename)) {
				$testfile = "{$filename}/".md5(uniqid(rand(), true));
				if (@touch($testfile)) {
					@unlink($testfile);
					$cache[$filename] = true;
				}
			}
		} else {
			$writabledir = FileHandle::find_writable_dir();
			if (empty($writabledir)) {
				return false;
			}
			$operationcode = <<<EOT
				if (version_compare(PHP_VERSION, '{$versioncompare}') >= 0 OR is_file('{$filename}')) {
					print(is_writable('{$filename}'));
					exit;
				} elseif (is_dir('{$filename}')) {
					\$testfile = "{$filename}/".md5(uniqid(rand(), true));
					if (@touch(\$testfile)) {
						@unlink(\$testfile);
						print(1);
						exit;
					}
				}
EOT;
			$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
			if ($execresult == 1) {
				$cache[$filename] = true;
			}
		}
		return $cache[$filename];
	}//end function is_writable

	/**
	 * Description 此函数用于解决 mkdir() 在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
	 * @author 
	 * @param string 要创建的目录
	 * @param int 指定目录创建成功后，要改变的文件属性模式
	 * @return boolean 如果成功则返回 TRUE，失败则返回 FALSE。
	 */
	function mkdir($pathname, $mode = 0777)
	{
		if (true === FileHandle::is_allowed_to_access_in_safe_mode($pathname)) {
			if (mkdir($pathname, $mode)) {
				return chmod($pathname, $mode);
			}
		} else {
			$parentdir = realpath(dirname($pathname));
			if (false === $parentdir) {
				trigger_error("FileHandle::mkdir({$pathname}): No such file or directory.", E_USER_WARNING);
				return false;
			}
			$writabledir = FileHandle::find_writable_dir();
			if (empty($writabledir)) {
				if (false === ($writabledir = FileHandle::find_writable_dir($parentdir))) {
					return false;
				}
			}
			$mkdircode = "if(@mkdir('{$pathname}', '{$mode}') AND @chmod('{$pathname}', '{$mode}')){print(1);exit;}";
			$execresult = FileHandle::filehandle_in_safe_mode($mkdircode);
			if ($execresult == 1) {
				return true;
			} elseif (!empty($execresult)) {
				trigger_error("FileHandle:mkdir({$pathname}): {$execresult}", E_USER_WARNING);
				return false;
			}
		}
		return false;
	}//end function mkdir

	/**
	 * Description 此函数用于解决 move_uploaded_file() 在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
	 * @author 
	 * @param string 上传文件的文件名
	 * @param string 目标文件
	 * @return boolean 如果成功则返回 TRUE，失败则返回 FALSE。
	 */ 
	function move_uploaded_file($filename, $destination)
	{
		if (true === FileHandle::is_allowed_to_access_in_safe_mode($destination)) {
			return move_uploaded_file($filename, $destination);
		} else {
			$destdir = realpath(dirname($destination));
			if (false === $destdir) {
				trigger_error("FileHandle::move_uploaded_file({$destination}): No such file or directory", E_USER_WARNING);
				return false;
			}
			$writabledir = FileHandle::find_writable_dir($destdir);
			if (empty($writabledir)) {
				return false;
			}

			$destfile = $writabledir.'/'.basename($destination);
			if (move_uploaded_file($filename, $destfile)) {
				@chmod($destfile, 0777);
				$operationcode = "if(@rename('{$destfile}', '{$destination}')){print(1);exit;}";
				$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
			}
			@unlink($destfile);
			if ($execresult == 1) {
				return true;
			} elseif (!empty($execresult)) {
				trigger_error("FileHandle::move_uploaded_file(): {$execresult}", E_USER_WARNING);
				return false;
			}
		}
		return false;
	}//end function move_uploaded_file

	/**
	 * Description 递归删除目录，并根据选项$opts进行有选择地删除————使用循环代替传统的递归算法，速度更快更高效。(暂时不完全支持安全模式下删除)
	 * @author 
	 * @param string 目标目录
	 * @param int 删除选项，允许的值为0,1,2,3
	 * @return array 如果目标目录不存在，或不可写，则返回FALSE，否则，返回一个有四个元素的键值数组；分别表示：subdircount 键表示子文件夹的总数、subdirremovednum 键表示已成功删除的子文件夹的总数、subfilecount 键表示子文件的总数、subfileremovednum 键表示已成功删除的子文件的总数
	 */
	function recursion_removedir($destdir, $rmopts = 0)
	{
		if ($rmopts < 0 OR $rmopts > 3) {
			$rmopts = 0;
		}
		if ($rmopts == 0) { // 全部删除
			$rmself = true;
			$rmsubfile = true;
			$rmsubdir = true;
		} elseif ($rmopts == 1) { // 只删除所有子文件
			$rmself = false;
			$rmsubfile = true;
			$rmsubdir = false;
		} elseif ($rmopts == 2) { // 只删除所有子文件夹
			$rmself = false;
			$rmsubfile = false;
			$rmsubdir = true;
		} elseif ($rmopts == 3) { // 只删除所有子文件和文件夹
			$rmself = false;
			$rmsubfile = true;
			$rmsubdir = true;
		}
		if (false === $rmself AND false === $rmsubfile AND false === $rmsubdir) {
			return false;
		}

		$destdir = realpath($destdir);
		if ((false === $destdir)) {
			trigger_error("FileHandle::recursion_removedir(): No such directory ", E_USER_WARNING);
			return false;
		}
		if (!FileHandle::is_writable($destdir)) {
			trigger_error("FileHandle::recursion_removedir(): Unable to access file {$destdir} because Permission denied ", E_USER_WARNING);
			return false;
		}

		$subdircount = 0;
		$subdirremovednum = 0;
		$subfilecount = 0;
		$subfileremovednum = 0;
		$LIFO_queue = array();
		if ($filelist =& FileHandle::scandir($destdir)) {
			$LIFO_index = 0;
			$LIFO_queue[0] =& $filelist;
			$dir_removelist = array();
			while ($LIFO_index >= 0) {
				if (isset($LIFO_queue[$LIFO_index]['files'])) {
					$subfilecount += count($LIFO_queue[$LIFO_index]['files']);
					if (true === $rmsubfile) {
						foreach ($LIFO_queue[$LIFO_index]['files'] AS $entry) {
							if (FileHandle::unlink($destdir.'/'.$entry)) {
								$subfileremovednum++;
							}
						}
					}
					unset($LIFO_queue[$LIFO_index]['files']);
				}
				$emptydirectories = true;
				if (isset($LIFO_queue[$LIFO_index]['directories'])) {
					if ($count = count($LIFO_queue[$LIFO_index]['directories'])) {
						$subdircount += $count - 2;
					}
					foreach ($LIFO_queue[$LIFO_index]['directories'] AS $index => $entry) {
						unset($LIFO_queue[$LIFO_index]['directories'][$index]);
						if ($entry == '.' OR $entry == '..') {
							continue;
						}
						$currdirrealpath = $destdir.'/'.$entry;
						if (true === $rmsubdir) {
							$dir_removelist[] = $currdirrealpath;
						}
						if ($filelist =& FileHandle::scandir($currdirrealpath)) {
							$destdir = $currdirrealpath;
							$LIFO_index++;
							$LIFO_queue[$LIFO_index] =& $filelist;
							$emptydirectories = false;
							break;
						}
					}
				}
				if (true === $emptydirectories) {
					unset($LIFO_queue[$LIFO_index]['directories']);
					$LIFO_index--;
					$destdir = dirname($destdir);
					if ($LIFO_index < 0) {
						unset($LIFO_queue);
						break;
					}
				}
			}
			if (!empty($dir_removelist) AND is_array($dir_removelist)) {
				for ($i = count($dir_removelist) - 1 ; $i >= 0 ; $i--) {
					if (FileHandle::rmdir($dir_removelist[$i])) {
						$subdirremovednum++;
					}
				}
				unset($cachedir);
			}
			if (true === $rmself) {
				FileHandle::rmdir($destdir);
			}
		}
		return array('subdircount' => $subdircount, 'subdirremovednum' => $subdirremovednum, 'subfilecount' => $subfilecount, 'subfileremovednum' => $subfileremovednum);
	}//end function recursion_rmdir
	
	/**
	 * Description 删除文件或目录；支持删除子目录和子文件
	 * @author 
	 * @param string 要在其上执行操作的文件或目录
	 * @param boolean 对于每一个被检查的文件或目录，都有一份相关的检查缓存结果；
	 * @return boolean 如果允许文件操作，返回TRUE，否则返回 FALSE。
	 */
	function remove($dest, $opts = array())
	{
		//Start 选项设置初始化
			//Start 递归删除目录选项
				if (!isset($opts['recursion'])) {
					$opts['recursion'] = false;
				} else {
					$opts['recursion'] = true;
				}
			//End 递归删除目录选项
		//End 选项设置初始化
		if (FileHandle::is_file($dest)) {
			return FileHandle::unlink($dest);
		} elseif (FileHandle::is_dir($dest)) {
			if (false === $opts['recursion']) {
				return FileHandle::rmdir($dest);
			} else {
				return FileHandle::recursion_removedir($dest);
			}
		} else {
			trigger_error("FileHandle::remove({$dest}): No such file or directory.", E_USER_WARNING);
			return false;
		}
	}//end function remove

	/**
	 * Description 此函数用于解决 rename() 在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
	 * @author 
	 * @param string 原文件名
	 * @param string 新的文件名
	 * @return boolean 如果成功则返回 TRUE，失败则返回 FALSE。
	 */
	function rename($oldname, $newname)
	{
		$tmpoldname = $oldname;
		$oldname = realpath($oldname);
		if (false === $oldname) {
			trigger_error("FileHandle::rename({$tmpoldname},{$newname}): No such file or directory.", E_USER_WARNING);
			return false;
		}
		if (false === ($oldisallowed = FileHandle::is_allowed_to_access_in_safe_mode($oldname))) {
			$oldisallowed = FileHandle::is_allowed_to_access_in_safe_mode(dirname($oldname));
		}
		$newdir = realpath(dirname($newname));
		if (false === ($newisallowed = FileHandle::is_allowed_to_access_in_safe_mode($newname))) {
			if (false === $newdir) {
				trigger_error("FileHandle::rename({$tmpoldname},{$newname}): No such file or directory.", E_USER_WARNING);
				return false;
			}
			$newisallowed = FileHandle::is_allowed_to_access_in_safe_mode($newdir);
		}

		if (true === $oldisallowed AND true === $newisallowed) {
			return rename($oldname, $newname);
		} else {
			$writabledir = FileHandle::find_writable_dir();
			if (empty($writabledir)) {
				return false;
			}
			if (true === $oldisallowed) {
				$tmpfile = $writabledir.'/'.strtolower(substr(base64_encode(md5(uniqid(strtotime('now'),true))),20,8));
				if (FileHandle::icopy($oldname, $tmpfile)) {
					$operationcode = "if(@rename('{$tmpfile}', '{$newname}')){print(1);exit;}";
					$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
					if ($execresult == 1) {
						FileHandle::remove($oldname, array('recursion' => true));
						return true;
					} else {
						FileHandle::remove($tmpfile, array('recursion' => true));
						if (!empty($execresult)) {
							trigger_error("FileHandle::rename({$oldname}, {$newname}): {$execresult}", E_USER_WARNING);
						}
						return false;
					}
				}
			} elseif (true === $newisallowed) {
				$tmpfile = $newdir.'/'.strtolower(substr(base64_encode(md5(uniqid(strtotime('now'),true))),20,8));
				if (touch($tmpfile)) {
					$operationcode = "if(@rename('{$oldname}', '{$tmpfile}')){print(1);exit;}";
					$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
					if ($execresult == 1) {
						rename($tmpfile, $newname);
						return true;
					} else {
						FileHandle::remove($tmpfile, array('recursion'=>true));
						if (!empty($execresult)) {
							trigger_error("FileHandle::rename({$oldname}, {$newname}): {$execresult}", E_USER_WARNING);
						}
						return false;
					}
				}
			} else {
				$operationcode = "if(@rename('{$oldname}', '{$newname}')){print(1);exit;}";
				$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
				if ($execresult == 1) {
					return true;
				} elseif (!empty($execresult)) {
					trigger_error("FileHandle::rename({$oldname}, {$newname}): {$execresult}", E_USER_WARNING);
					return false;
				}
			}
		}
		return false;
	}//end function rename

	/**
	 * Description 此函数用于解决 rmdir() 在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
	 * @author 
	 * @param string 原文件名
	 * @param string 新的文件名
	 * @param string 具有0777属性的目录，该目录必须和当前脚本具有相同UID的，并且可以通过http访问该目录
	 * @return boolean 如果成功则返回 TRUE，失败则返回 FALSE。
	 */
	function rmdir($destdir)
	{
		if (FileHandle::is_allowed_to_access_in_safe_mode($destdir)) {
			return rmdir($destdir);
		} else {
			$writabledir = FileHandle::find_writable_dir();
			if (empty($writabledir)) {
				return false;
			}
			$operationcode = "if(@rmdir('{$destdir}')){print(1);exit;}";
			$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
			if ($execresult == 1) {
				return true;
			} elseif (!empty($execresult)) {
				trigger_error("FileHandle::rmdir({$destdir}): {$execresult}", E_USER_WARNING);
				return false;
			}
		}
	}//end function rmdir

	/**
	 * Description 列出指定路径中的文件和目录。
	 * @author 
	 * @param string 目标目录
	 * @return array 返回一个 array，包含有 directory 中的文件和目录。如果 directory 不是一个目录，则返回布尔值 FALSE，并产生一条 E_WARNING 级别的错误
	 */
	function &scandir($directory)
	{
		$returnval = false;
		if (false === ($directory = realpath($directory))) {
			return $returnval;
		}
		if (!FileHandle::is_dir($directory)) {
			trigger_error("FileHandle::scandir({$directory}): No suche directory.", E_WARNING);
			return $returnval;
		}
		if (FileHandle::is_allowed_to_access_in_safe_mode($directory)) {
			if ($res_dir = dir($directory)) {
				$returnval = array();
				while (false !== ($entry = $res_dir->read())) {
					$fullentry = $res_dir->path.'/'.$entry;
					if (is_file($fullentry)) {
						$returnval['files'][] = $entry;
					} elseif (is_dir($fullentry)) {
						$returnval['directories'][] = $entry;
					}
				}
				$res_dir->close();
			}
		} else {
			$writabledir = FileHandle::find_writable_dir();
			if (empty($writabledir)) {
				return $returnval;
			}
			$operationcode = <<<EOT
				if (\$res_dir = @dir('{$directory}')) {
					\$returnval = array();
					while (false !== (\$entry = \$res_dir->read())) {
						\$fullentry = \$res_dir->path.'/'.\$entry;
						if (is_file(\$fullentry)) {
							\$returnval['files'][] = \$entry;
						} elseif (is_dir(\$fullentry)) {
							\$returnval['directories'][] = \$entry;
						}
					}
					\$res_dir->close();
					print serialize(\$returnval);
				}
EOT;
			if ($execresult = FileHandle::filehandle_in_safe_mode($operationcode)) {
				$returnval = @unserialize(stripslashes($execresult));
			}
			if (!$returnval) {
				trigger_error("FileHandle::scandir({$directory}): {$execresult}", E_USER_WARNING);
			}
		}
		return $returnval;
	}

	/**
	 * Description 此函数用于解决 touch() 在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
	 * @author 
	 * @param string 原文件名
	 * @param string 新的文件名
	 * @return boolean 将文件从 source 拷贝到 dest。如果成功则返回 TRUE，失败则返回 FALSE。
	 */
	function touch($filename, $time = null, $atime = null)
	{
		if (!FileHandle::file_exists(dirname($filename))) {
			trigger_error("FileHandle::touch({$filename}): No such file or directory.", E_USER_WARNING);
			return false;
		}
		$writabledir = FileHandle::find_writable_dir();
		if (empty($writabledir)) {
			return false;
		}
		if (true === FileHandle::is_allowed_to_access_in_safe_mode($filename)) {
			return touch($filename, $time, $atime);
		} else {
			$operationcode = "if(@touch('{$filename}', '{$time}', '{$atime}')){print(1);exit;}";
			$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
			if ($execresult == 1) {
				return true;
			} elseif (!empty($execresult)) {
				trigger_error("FileHandle::touch({$filename},{$time},{$atime}): {$execresult}", E_USER_WARNING);
				return false;
			}
		}
		return false;
	}//end function touch

	/**
	 * Description 此函数用于解决 unlink() 在安全模式下受限制的问题。只要被操作的文件或目录，其UID与脚本UID或者运行时UID相同，就可以在安全模式下不受限制的运行。
	 * @author 
	 * @param string 目标文件名
	 * @return boolean 如果成功则返回 TRUE，失败则返回 FALSE。
	 */
	function unlink($destfile)
	{
		if (FileHandle::is_allowed_to_access_in_safe_mode($destfile)) {
			return unlink($destfile);
		} else {
			$writabledir = FileHandle::find_writable_dir();
			if (empty($writabledir)) {
				return false;
			}
			$operationcode = "if(@unlink('{$destfile}')){print(1);exit;}";
			$execresult = FileHandle::filehandle_in_safe_mode($operationcode);
			if ($execresult == 1) {
				return true;
			} elseif (!empty($execresult)) {
				trigger_error("FileHandle::unlink({$destfile}): {$execresult}", E_USER_WARNING);
				return false;
			}
		}
	}//end function unlink
	
}//end class FileHandle

/**
 * Description 此函数是 FileHandle::file_put_contents() 的别名
 */
function FilePubContents($destfile, &$contents, $isappend=false)
{
	return FileHandle::file_put_contents($destfile, $contents, $isappend);
}//end function FilePubContents

/**
 * Description 此函数是 FileHandle::file_get_contents() 的别名
 */
function FileGetContents($file)
{
	return FileHandle::file_get_contents($file);
}//end function FileGetContents

/**
 * Description 检测目标目录是否可以写入
 * @author 
 * @param string 目标目录
 * @return boolean 如果可写返回TRUE，否则返回FALSE
 */
function dir_is_writable($testdir)
{
	if (!is_dir($testdir)) {
		return false;
	} else {
		return FileHandle::is_writable($testdir);
	}
}
/**
 * Description 此函数是 dir_is_writable() 的别名
 */
function dir_iswritable($testdir)
{
	return dir_is_writable($testdir);
}

/**
 * Description 此函数是 FileHandle::recursion_rmdir() 的别名
 */
function recursionRemoveDir($destdir, $rmopts = 7)
{
	return FileHandle::recursion_removedir($destdir, $rmopts);
}

function isUploadedFile($filename)
{
	return FileHandle::is_uploaded_file($filename);
}//end function isUploadedFile

?>