1<?php 2/* 3 * Copyright Intermesh BV 4 * 5 * This file is part of Group-Office. You should have received a copy of the 6 * Group-Office license along with Group-Office. See the file /LICENSE.TXT 7 * 8 * If you have questions write an e-mail to info@intermesh.nl 9 * 10 */ 11 12/** 13 * Base class for filesystem objects 14 * 15 * @package GO.base.fs 16 * @version $Id: RFC822.class.inc 7536 2011-05-31 08:37:36Z mschering $ 17 * @author Merijn Schering <mschering@intermesh.nl> 18 * @copyright Copyright Intermesh BV. 19 */ 20 21namespace GO\Base\Fs; 22 23 24abstract class Base{ 25 26 protected $path; 27 28 const INVALID_CHARS = '/[\/:\*\?"<>|\\\]/'; 29 30 /** 31 * Constructor of a file or folder 32 * 33 * @param StringHelper $path The absolute path must be suplied 34 * @throws Exception 35 */ 36 public function __construct($path) { 37 38// \GO::debug("FS construct $path"); 39 40 if(empty($path)) 41 throw new \Exception("Path may not be empty in Base"); 42 43 //normalize path slashes 44 if(\GO\Base\Util\Common::isWindows()) 45 $path=str_replace('\\','/', $path); 46 47 if(!self::checkPathInput($path)) 48 throw new \Exception("The supplied path '$path' was invalid"); 49 50 $parent = dirname($path); 51 if($parent != '/') 52 $this->path=$parent; 53 else 54 $this->path=''; 55 56 $this->path .= '/'.self::utf8Basename($path); 57 } 58 59 /** 60 * Create a folder or file from a path string 61 * 62 * @param StringHelper $path 63 * @return File|Folder 64 */ 65 public static function createFromPath($path){ 66 if(is_file($path)) 67 return new File($path); 68 else 69 return new Folder ($path); 70 } 71 72 73 /** 74 * Get the parent folder object 75 * 76 * @return Folder Parent folder object 77 */ 78 public function parent(){ 79 80 $parentPath = dirname($this->path); 81 if($parentPath==$this->path) 82 return false; 83 84 return new Folder($parentPath); 85 } 86 87 /** 88 * Find the first existing parent folder. 89 * 90 * @return Folder 91 */ 92 public function firstExistingParent(){ 93 $parent=$this; 94 while($parent = $parent->parent()){ 95 if($parent->exists()) 96 return $parent; 97 } 98 return false; 99 } 100 101 /** 102 * Get a child file or folder. 103 * 104 * @param StringHelper $filename 105 * @return File|Folder|boolean 106 */ 107 public function child($filename){ 108 $childPath = $this->path.'/'.$filename; 109 if(is_file($childPath)){ 110 return new File($childPath); 111 } elseif(is_dir($childPath)){ 112 return new Folder($childPath); 113 }else 114 { 115 return false; 116 } 117 } 118 119 /** 120 * Create a new file object. Filesystem file is not created automatically. 121 * 122 * @param StringHelper $filename 123 * @param boolean $isFile 124 * @return \File|\Folder 125 */ 126 public function createChild($filename, $isFile=true){ 127 $childPath = $this->path.'/'.$filename; 128 if($isFile){ 129 return new File($childPath); 130 } else{ 131 return new Folder($childPath); 132 } 133 } 134 135 /** 136 * Return absolute filesystem path 137 * 138 * @return String 139 */ 140 public function path(){ 141 return $this->path; 142 } 143 144 /** 145 * Return the modification unix timestamp 146 * 147 * @return int Unix timestamp 148 */ 149 public function mtime(){ 150 return filemtime($this->path); 151 } 152 /** 153 * Return the creation unix timestamp 154 * 155 * @return int Unix timestamp 156 */ 157 public function ctime(){ 158 return filectime($this->path); 159 } 160 161 /** 162 * Filesize in bytes 163 * 164 * @return int Filesize in bytes 165 */ 166 public function size(){ 167 return filesize($this->path); 168 } 169 170 /** 171 * Get the name of this file or folder 172 * 173 * @return String 174 */ 175 public function name(){ 176 177 if(!function_exists('mb_substr')) 178 { 179 return basename($this->path); 180 } 181 182 if(empty($this->path)) 183 { 184 return ''; 185 } 186 $pos = mb_strrpos($this->path, '/'); 187 if($pos===false) 188 { 189 return $this->path; 190 }else 191 { 192 return mb_substr($this->path, $pos+1); 193 } 194 } 195 196 /** 197 * Check if the file or folder exists 198 * @return boolean 199 */ 200 public function exists(){ 201 return file_exists($this->path); 202 } 203 204 /** 205 * Check if the file or folder is writable for the webserver user. 206 * 207 * @return boolean 208 */ 209 public function isWritable(){ 210 return is_writable($this->path); 211 } 212 213 /** 214 * Change owner 215 * @param StringHelper $user 216 * @return boolean 217 */ 218 public function chown($user){ 219 return chown($this->path, $user); 220 } 221 222 /** 223 * Change group 224 * 225 * @param StringHelper $group 226 * @return boolean 227 */ 228 public function chgrp($group){ 229 return chgrp($this->path, $group); 230 } 231 232 /** 233 * 234 * @param int $permissionsMode <p> 235 * Note that mode is not automatically 236 * assumed to be an octal value, so strings (such as "g+w") will 237 * not work properly. To ensure the expected operation, 238 * you need to prefix mode with a zero (0): 239 * </p> 240 * 241 * @return boolean 242 */ 243 public function chmod($permissionsMode){ 244 return chmod($this->path, $permissionsMode); 245 } 246 247 /** 248 * Delete the file 249 * 250 * @return boolean 251 */ 252 public function delete(){ 253 return false; 254 } 255 256 public function __toString() { 257 return $this->path; 258 } 259 260 /** 261 * Checks if a path send as a request parameter is valid. 262 * 263 * @param String $path 264 * @return boolean 265 */ 266 public static function checkPathInput($path){ 267 $path = '/'.str_replace('\\','/', $path); 268 return strpos($path, '/../') === false; 269 } 270 271 272 /** 273 * Get's the filename from a path string and works with UTF8 characters 274 * 275 * @param String $path 276 * @return String 277 */ 278 public static function utf8Basename($path) 279 { 280 if(!function_exists('mb_substr')) 281 { 282 return basename($path); 283 } 284 //$path = trim($path); 285 if(substr($path,-1,1)=='/') 286 { 287 $path = substr($path,0,-1); 288 } 289 if(empty($path)) 290 { 291 return ''; 292 } 293 $pos = mb_strrpos($path, '/'); 294 if($pos===false) 295 { 296 return $path; 297 }else 298 { 299 return mb_substr($path, $pos+1); 300 } 301 } 302 303 /** 304 * Remove unwanted characters from a string so it can safely be used as a filename. 305 * 306 * @param StringHelper $filename 307 * @return StringHelper 308 */ 309 public static function stripInvalidChars($filename, $replace=''){ 310 $filename = trim(preg_replace(self::INVALID_CHARS,$replace, $filename)); 311 312 //IE likes to change a double white space to a single space 313 //We must do this ourselves so the filenames will match. 314 $filename = preg_replace('/\s+/', ' ', $filename); 315 316 //strip dots from start and end (end . is not allowed on windows) 317 $filename=trim($filename, '. '); 318 319 if(empty($filename)){ 320 $filename = 'unnamed'; 321 } 322 323 if(\GO::config()->convert_utf8_filenames_to_ascii) 324 $filename = \GO\Base\Util\StringHelper::utf8ToASCII($filename); 325 326 if(strlen($filename)>255) 327 $filename = substr($filename, 0,255); 328 329 return $filename; 330 } 331 332 /** 333 * Check if this object is a folder. 334 * 335 * @return boolean 336 */ 337 public function isFolder(){ 338// return is_dir($this->path); 339 return is_a($this, "\GO\Base\Fs\Folder"); //works with non existing files 340 } 341 342 /** 343 * Check if this object is a file. 344 * 345 * @return boolean 346 */ 347 public function isFile(){ 348// return is_file($this->path); 349 return is_a($this, "\GO\Base\Fs\File"); //works with non existing files 350 } 351 352 /** 353 * Rename a file or folder 354 * 355 * @param String $name 356 * @return boolean 357 */ 358 public function rename($name){ 359 $oldPath = $this->path; 360 $newPath = dirname($this->path).'/'.$name; 361 362 if(rename($oldPath,$newPath)) 363 { 364 $this->path = $newPath; 365 return true; 366 }else 367 { 368 return false; 369 } 370 } 371 372 /** 373 * Get the path without \GO::config()->file_storage_path. 374 * 375 * @return StringHelper 376 */ 377 public function stripFileStoragePath(){ 378 return str_replace(\GO::config()->file_storage_path,'', $this->path()); 379 } 380 381 /** 382 * Get the path without \GO::config()->root_path. 383 * 384 * @return StringHelper 385 */ 386 public function stripRootPath(){ 387 return str_replace(\GO::config()->root_path,'', $this->path()); 388 } 389 390 /** 391 * Get the path without \GO::config()->tmpdir. 392 * 393 * @return StringHelper 394 */ 395 public function stripTempPath(){ 396 return str_replace(\GO::config()->tmpdir,'', $this->path()); 397 } 398 399 /** 400 * Check if this is a temporary file. 401 * 402 * @return boolean 403 */ 404 public function isTempFile(){ 405 return strpos($this->path(), \GO::config()->tmpdir)===0; 406 } 407 408} 409