'), array('_'), $title); $title = trim($title); // Remove control characters return preg_replace('#[[:cntrl:]]#u', '', $title); // [\x00-\x1F\x7F] } /** * Clean a string of html that may be used as file content * * @param string $text The string to be cleansed. Passed by reference */ public static function CleanText(&$text){ \gp\tool\Editing::tidyFix($text); self::rmPHP($text); self::FixTags($text); $text = \gp\tool\Plugins::Filter('CleanText',array($text)); } /** * Use html parser to check the validity of $text * * @param string $text The html content to be checked. Passed by reference */ public static function FixTags(&$text){ $gp_html_output = new \gp\tool\Editing\HTML($text); $text = $gp_html_output->result; } /** * Remove php tags from $text * * @param string $text The html content to be checked. Passed by reference */ public static function rmPHP(&$text){ $search = array(''); $replace = array('<?', '<?php', '?>'); $text = str_replace($search, $replace, $text); } /** * Removes any NULL characters in $string. * @since 3.0.2 * @param string $string * @return string */ public static function NoNull($string){ $string = preg_replace('/\0+/', '', $string); return preg_replace('/(\\\\0)+/', '', $string); } /** * Save the content for a new page in /data/_pages/ * @since 1.8a1 * */ public static function NewTitle($title, $section_content=false, $type='text'){ // get the file for the title if( empty($title) ){ return false; } $file = self::PageFile($title); if( !$file ){ return false; } // organize section data $file_sections = array(); if( is_array($section_content) && isset($section_content['type']) ){ $file_sections[0] = $section_content; }elseif( is_array($section_content) ){ $file_sections = $section_content; }else{ $file_sections[0] = array( 'type' => $type, 'content' => $section_content, ); } // add meta data $meta_data = array( 'file_number' => self::NewFileNumber(), 'file_type' => $type, ); return self::SaveData($file,'file_sections',$file_sections,$meta_data); } /** * Return the data file location for a title * Since v4.6, page files are within a subfolder * As of v2.3.4, it defaults to an index based file name but falls back on title based file name for backwards compatibility * * * @param string $title * @return string The path of the data file */ public static function PageFile($title){ global $dataDir, $config, $gp_index; $index_path = false; // filename based on title index if( gp_index_filenames && isset($gp_index[$title]) && isset($config['gpuniq']) ){ $index_path = $dataDir . '/data/_pages/' . substr($config['gpuniq'], 0, 7) . '_' . $gp_index[$title] . '/page.php'; } // using file name instead of index $normal_path = $dataDir . '/data/_pages/' . str_replace('/', '_', $title) . '/page.php'; if( !$index_path || self::Exists($normal_path) ){ return $normal_path; } return $index_path; } public static function NewFileNumber(){ global $config; if( !isset($config['file_count']) ){ $config['file_count'] = 0; } $config['file_count']++; \gp\admin\Tools::SaveConfig(); return $config['file_count']; } /** * Get the meta data for the specified file * * @param string $file * @return array */ public static function GetTitleMeta($file){ self::Get($file,'meta_data'); return self::$last_meta; } /** * Return an array of info about the data file * */ public static function GetFileStats($file){ $file_stats = self::Get($file, 'file_stats'); if( $file_stats ){ return $file_stats; } return array('created'=> time()); } /** * Save a file with content and data to the server * This function will be deprecated in future releases. Using it is not recommended * * @param string $file The path of the file to be saved * @param string $contents The contents of the file to be saved * @param string $code The data to be saved * @param string $time The unix timestamp to be used for the $fileVersion * @return bool True on success */ public static function SaveFile($file, $contents, $code=false, $time=false){ $result = self::FileStart($file, $time); if( $result !== false ){ $result .= "\n" . $code; } $result .= "\n\n?" . ">\n"; $result .= $contents; return self::Save($file, $result); } /** * Save raw content to a file to the server * * @param string $file The path of the file to be saved * @param string $contents The contents of the file to be saved * @return bool True on success */ public static function Save($file,$contents){ global $gp_not_writable; $exists = self::Exists($file); //make sure directory exists if( !$exists ){ $dir = \gp\tool::DirName($file); if( !file_exists($dir) ){ self::CheckDir($dir); } } $fp = @fopen($file, 'wb'); if( $fp === false ){ $gp_not_writable[] = $file; return false; } if( !flock($fp, LOCK_EX) ){ trigger_error('flock could not be obtained.'); return false; } if( !$exists ){ @chmod($file, gp_chmod_file); }elseif( function_exists('opcache_invalidate') && substr($file, -4) === '.php' ){ opcache_invalidate($file); } $return = fwrite($fp, $contents); flock($fp, LOCK_UN); fclose($fp); return ($return !== false); } /** * Rename a file * @since 4.6 */ public static function Rename($from, $to){ global $gp_not_writable; if( !self::WriteLock() ){ return false; } //make sure directory exists $dir = \gp\tool::DirName($to); if( !file_exists($dir) && !self::CheckDir($dir) ){ return false; } return rename($from, $to); } /** * Replace $to with $from * */ public static function Replace($from, $to){ $temp_dir = ''; // move the $to out of the way if it exists if( file_exists($to) ){ $temp_dir = $to . '_' . time(); if( !self::rename($to, $temp_dir) ){ return false; } } // rename $from -> $to if( !self::rename($from, $to) ){ if( $temp_dir ){ self::rename($temp_dir, $to); } return false; } if( !empty($temp_dir) ){ self::RmAll($temp_dir); } return true; } /** * Get a write lock to prevent simultaneous writing * @since 3.5.3 */ public static function WriteLock(){ if( defined('gp_has_lock') ){ return gp_has_lock; } $expires = gp_write_lock_time; if( self::Lock('write', gp_random, $expires) ){ define('gp_has_lock', true); return true; } trigger_error('CMS write lock could not be obtained.'); define('gp_has_lock', false); return false; } /** * Get a lock * Loop and delay to wait for the removal of existing locks (maximum of about .2 of a second) * */ public static function Lock($file, $value, &$expires){ global $dataDir; $tries = 0; $lock_file = $dataDir . '/data/_lock_' . sha1($file); $file_time = 0; $elapsed = 0; while( $tries < 1000 ){ if( !file_exists($lock_file) ){ file_put_contents($lock_file, $value); usleep(100); }elseif( !$file_time ){ $file_time = filemtime($lock_file); } $contents = @file_get_contents($lock_file); if( $value === $contents ){ @touch($lock_file); return true; } if( $file_time ){ $elapsed = time() - $file_time; if( $elapsed > $expires ){ @unlink($lock_file); } } clearstatcache(); usleep(100); $tries++; } if( $file_time ){ $expires -= $elapsed; } return false; } /** * Remove a lock file if the value matches * */ public static function Unlock($file, $value){ global $dataDir; $lock_file = $dataDir . '/data/_lock_' . sha1($file); if( !file_exists($lock_file) ){ return true; } $contents = @file_get_contents($lock_file); if( $contents === false ){ return true; } if( $value === $contents ){ unlink($lock_file); return true; } return false; } /** * Save array(s) to a $file location * Takes 2n+3 arguments * * @param string $file The location of the file to be saved * @param string $varname The name of the variable being saved * @param array $array The value of $varname to be saved * * @deprecated 4.3.5 */ public static function SaveArray(){ if( gp_data_type === '.json' ){ throw new Exception('SaveArray() cannot be used for json data. Use SaveData() instead'); } $args = func_get_args(); $count = count($args); if( ($count %2 !== 1) || ($count < 3) ){ trigger_error('Wrong argument count ' . $count . ' for \gp\tool\Files::SaveArray() '); return false; } $file = array_shift($args); $file_stats = array(); $data = ''; while( count($args) ){ $varname = array_shift($args); $array = array_shift($args); if( $varname == 'file_stats' ){ $file_stats = $array; }else{ $data .= self::ArrayToPHP($varname, $array); $data .= "\n\n"; } } $data = self::FileStart($file, time(), $file_stats) . $data; return self::Save($file, $data); } /** * Save array to a $file location * * @param string $file The location of the file to be saved * @param string $varname The name of the variable being saved * @param array $array The value of $varname to be saved * @param array $meta meta data to be saved along with $array * */ public static function SaveData($file, $varname, $array, $meta=array()){ $file = self::FilePath($file); if( gp_data_type === '.json' ){ $json = self::FileStart_Json($file); $json[$varname] = $array; $json['meta_data'] = $meta; $content = json_encode($json); }else{ $content = self::FileStart($file); $content .= self::ArrayToPHP($varname, $array); $content .= "\n\n"; $content .= self::ArrayToPHP('meta_data', $meta); } return self::Save($file, $content); } /** * Experimental * */ private static function FileStart_Json($file, $time=null ){ global $gpAdmin; if( is_null($time) ){ $time = time(); } //file stats $file_stats = self::GetFileStats($file); $file_stats['gpversion'] = gpversion; $file_stats['modified'] = $time; $file_stats['username'] = false; if( \gp\tool::loggedIn() ){ $file_stats['username'] = $gpAdmin['username']; } $json = array(); $json['file_stats'] = $file_stats; return $json; } /** * Return the beginning content of a data file * */ public static function FileStart($file, $time=null, $file_stats=array()){ global $gpAdmin; if( is_null($time) ){ $time = time(); } //file stats $file_stats = (array)$file_stats + self::GetFileStats($file); $file_stats['gpversion'] = gpversion; $file_stats['modified'] = $time; if( \gp\tool::loggedIn() ){ $file_stats['username'] = $gpAdmin['username']; }else{ $file_stats['username'] = false; } return '<' . '?' . 'php' . "\ndefined('is_running') or die('Not an entry point...');" . "\n" . '$fileVersion = \'' . gpversion . '\';' // @deprecated 3.0 . "\n" . '$fileModTime = \'' . $time . '\';' // @deprecated 3.0 . "\n" . self::ArrayToPHP('file_stats', $file_stats) . "\n\n"; } public static function ArrayToPHP($varname, &$array){ return '$' . $varname . ' = ' . var_export($array, true) . ';'; } /** * Insert a key-value pair into an associative array * * @param mixed $search_key Value to search for in existing array to insert before * @param mixed $new_key Key portion of key-value pair to insert * @param mixed $new_value Value portion of key-value pair to insert * @param array $array Array key-value pair will be added to * @param int $offset Offset distance from where $search_key was found. A value of 1 would insert after $search_key, a value of 0 would insert before $search_key * @param int $length If length is omitted, nothing is removed from $array. If positive, then that many elements will be removed starting with $search_key + $offset * @return bool True on success */ public static function ArrayInsert($search_key, $new_key, $new_value, &$array, $offset=0, $length=0){ $array_keys = array_keys($array); $array_values = array_values($array); $insert_key = array_search($search_key,$array_keys); if( ($insert_key === null) || ($insert_key === false) ){ return false; } array_splice($array_keys, $insert_key + $offset, $length, $new_key); array_splice($array_values, $insert_key + $offset, $length, 'fill'); //use fill in case $new_value is an array $array = array_combine($array_keys, $array_values); $array[$new_key] = $new_value; return true; } /** * Replace a key-value pair in an associative array * ArrayReplace() is a shortcut for using \gp\tool\Files::ArrayInsert() with $offset = 0 and $length = 1 */ public static function ArrayReplace($search_key, $new_key, $new_value, &$array){ return self::ArrayInsert($search_key, $new_key, $new_value, $array, 0, 1); } /** * Check recursively to see if a directory exists, if it doesn't attempt to create it * * @param string $dir The directory path * @param bool $index Whether or not to add an index.hmtl file in the directory * @return bool True on success */ public static function CheckDir($dir, $index=true){ global $config; if( !file_exists($dir) ){ $parent = \gp\tool::DirName($dir); self::CheckDir($parent, $index); //ftp mkdir if( !@mkdir($dir,gp_chmod_dir) ){ return false; } @chmod($dir, gp_chmod_dir); //some systems need more than just the 0755 in the mkdir() function // make sure there's an index.html file // only check if we just created the directory, we don't want to keep // creating an index.html file if a user deletes it if( $index && gp_dir_index ){ $indexFile = $dir . '/index.html'; if( !file_exists($indexFile) ){ //not using \gp\tool\Files::Save() so we can avoid infinite looping // (it's safe since we already know the directory exists and we're not concerned about the content) file_put_contents($indexFile, '<html></html>'); @chmod($indexFile, gp_chmod_file); } } } return true; } /** * Remove a directory * Will only work if directory is empty * */ public static function RmDir($dir){ return @rmdir($dir); } /** * Remove a file or directory and it's contents * */ public static function RmAll($path){ if( empty($path) ){ return false; } if( is_link($path) ){ return @unlink($path); } if( !is_dir($path) ){ return @unlink($path); } $success = true; $subDirs = array(); //$files = scandir($path); $files = self::ReadDir($path, false); foreach($files as $file){ $full_path = $path . '/' . $file; if( !is_link($full_path) && is_dir($full_path) ){ $subDirs[] = $full_path; continue; } if( !@unlink($full_path) ){ $success = false; } } foreach($subDirs as $subDir){ if( !self::RmAll($subDir) ){ $success = false; } } if( $success ){ return self::RmDir($path); } return false; } /** * Get the correct path for the data file * Two valid methods to get a data file path: * Full path: /var/www/html/site/data/_site/config.php * Relative: _site/config * */ public static function FilePath($path){ global $dataDir; $ext = pathinfo($path, PATHINFO_EXTENSION); if( $ext === 'gpjson' ){ $path = substr($path,0,-7); }elseif( $ext === 'php' ){ $path = substr($path,0,-4); }else{ $path = $dataDir . '/data/' . ltrim($path, '/'); } if( gp_data_type === '.json' ){ return $path . '.gpjson'; } return $path . '.php'; } /** * @deprecated 3.0 * Use \gp\tool\Editing::CleanTitle() instead * Used by Simple_Blog1 */ public static function CleanTitle($title, $spaces='_'){ trigger_error('Deprecated Function'); return \gp\tool\Editing::CleanTitle($title, $spaces); } } } namespace{ class gpFiles extends gp\tool\Files{} }