1<?php 2/** 3 * File IO functions 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9 if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../').'/'); 10 require_once(DOKU_INC.'inc/common.php'); 11 require_once(DOKU_INC.'inc/HTTPClient.php'); 12 13/** 14 * Removes empty directories 15 * 16 * @todo use safemode hack 17 * @author Andreas Gohr <andi@splitbrain.org> 18 */ 19function io_sweepNS($id,$basedir='datadir'){ 20 global $conf; 21 22 //scan all namespaces 23 while(($id = getNS($id)) !== false){ 24 $dir = $conf[$basedir].'/'.utf8_encodeFN(str_replace(':','/',$id)); 25 26 //try to delete dir else return 27 if(!@rmdir($dir)) return; 28 } 29} 30 31/** 32 * Returns content of $file as cleaned string. 33 * 34 * Uses gzip if extension is .gz 35 * 36 * If you want to use the returned value in unserialize 37 * be sure to set $clean to false! 38 * 39 * @author Andreas Gohr <andi@splitbrain.org> 40 */ 41function io_readFile($file,$clean=true){ 42 $ret = ''; 43 if(@file_exists($file)){ 44 if(substr($file,-3) == '.gz'){ 45 $ret = join('',gzfile($file)); 46 }else{ 47 $ret = join('',file($file)); 48 } 49 } 50 if($clean){ 51 return cleanText($ret); 52 }else{ 53 return $ret; 54 } 55} 56 57/** 58 * Saves $content to $file. 59 * 60 * If the third parameter is set to true the given content 61 * will be appended. 62 * 63 * Uses gzip if extension is .gz 64 * 65 * @author Andreas Gohr <andi@splitbrain.org> 66 * @return bool true on success 67 */ 68function io_saveFile($file,$content,$append=false){ 69 global $conf; 70 $mode = ($append) ? 'ab' : 'wb'; 71 72 $fileexists = file_exists($file); 73 io_makeFileDir($file); 74 io_lock($file); 75 if(substr($file,-3) == '.gz'){ 76 $fh = @gzopen($file,$mode.'9'); 77 if(!$fh){ 78 msg("Writing $file failed",-1); 79 return false; 80 } 81 gzwrite($fh, $content); 82 gzclose($fh); 83 }else{ 84 $fh = @fopen($file,$mode); 85 if(!$fh){ 86 msg("Writing $file failed",-1); 87 return false; 88 } 89 fwrite($fh, $content); 90 fclose($fh); 91 } 92 93 if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']); 94 io_unlock($file); 95 return true; 96} 97 98/** 99 * Delete exact linematch for $badline from $file. 100 * 101 * Be sure to include the trailing newline in $badline 102 * 103 * Uses gzip if extension is .gz 104 * 105 * 2005-10-14 : added regex option -- Christopher Smith <chris@jalakai.co.uk> 106 * 107 * @author Steven Danz <steven-danz@kc.rr.com> 108 * @return bool true on success 109 */ 110function io_deleteFromFile($file,$badline,$regex=false){ 111 if (!@file_exists($file)) return true; 112 113 io_lock($file); 114 115 // load into array 116 if(substr($file,-3) == '.gz'){ 117 $lines = gzfile($file); 118 }else{ 119 $lines = file($file); 120 } 121 122 // remove all matching lines 123 if ($regex) { 124 $lines = preg_grep($badline,$lines,PREG_GREP_INVERT); 125 } else { 126 $pos = array_search($badline,$lines); //return null or false if not found 127 while(is_int($pos)){ 128 unset($lines[$pos]); 129 $pos = array_search($badline,$lines); 130 } 131 } 132 133 if(count($lines)){ 134 $content = join('',$lines); 135 if(substr($file,-3) == '.gz'){ 136 $fh = @gzopen($file,'wb9'); 137 if(!$fh){ 138 msg("Removing content from $file failed",-1); 139 return false; 140 } 141 gzwrite($fh, $content); 142 gzclose($fh); 143 }else{ 144 $fh = @fopen($file,'wb'); 145 if(!$fh){ 146 msg("Removing content from $file failed",-1); 147 return false; 148 } 149 fwrite($fh, $content); 150 fclose($fh); 151 } 152 }else{ 153 @unlink($file); 154 } 155 156 io_unlock($file); 157 return true; 158} 159 160/** 161 * Tries to lock a file 162 * 163 * Locking is only done for io_savefile and uses directories 164 * inside $conf['lockdir'] 165 * 166 * It waits maximal 3 seconds for the lock, after this time 167 * the lock is assumed to be stale and the function goes on 168 * 169 * @author Andreas Gohr <andi@splitbrain.org> 170 */ 171function io_lock($file){ 172 global $conf; 173 // no locking if safemode hack 174 if($conf['safemodehack']) return; 175 176 $lockDir = $conf['lockdir'].'/'.md5($file); 177 @ignore_user_abort(1); 178 179 $timeStart = time(); 180 do { 181 //waited longer than 3 seconds? -> stale lock 182 if ((time() - $timeStart) > 3) break; 183 $locked = @mkdir($lockDir, $conf['dmode']); 184 if($locked && $conf['dperm']) chmod($lockDir, $conf['dperm']); 185 } while ($locked === false); 186} 187 188/** 189 * Unlocks a file 190 * 191 * @author Andreas Gohr <andi@splitbrain.org> 192 */ 193function io_unlock($file){ 194 global $conf; 195 // no locking if safemode hack 196 if($conf['safemodehack']) return; 197 198 $lockDir = $conf['lockdir'].'/'.md5($file); 199 @rmdir($lockDir); 200 @ignore_user_abort(0); 201} 202 203/** 204 * Create the directory needed for the given file 205 * 206 * @author Andreas Gohr <andi@splitbrain.org> 207 */ 208function io_makeFileDir($file){ 209 global $conf; 210 211 $dir = dirname($file); 212 if(!@is_dir($dir)){ 213 io_mkdir_p($dir) || msg("Creating directory $dir failed",-1); 214 } 215} 216 217/** 218 * Creates a directory hierachy. 219 * 220 * @link http://www.php.net/manual/en/function.mkdir.php 221 * @author <saint@corenova.com> 222 * @author Andreas Gohr <andi@splitbrain.org> 223 */ 224function io_mkdir_p($target){ 225 global $conf; 226 if (@is_dir($target)||empty($target)) return 1; // best case check first 227 if (@file_exists($target) && !is_dir($target)) return 0; 228 //recursion 229 if (io_mkdir_p(substr($target,0,strrpos($target,'/')))){ 230 if($conf['safemodehack']){ 231 $dir = preg_replace('/^'.preg_quote(realpath($conf['ftp']['root']),'/').'/','', $target); 232 return io_mkdir_ftp($dir); 233 }else{ 234 $ret = @mkdir($target,$conf['dmode']); // crawl back up & create dir tree 235 if($ret && $conf['dperm']) chmod($target, $conf['dperm']); 236 return $ret; 237 } 238 } 239 return 0; 240} 241 242/** 243 * Creates a directory using FTP 244 * 245 * This is used when the safemode workaround is enabled 246 * 247 * @author <andi@splitbrain.org> 248 */ 249function io_mkdir_ftp($dir){ 250 global $conf; 251 252 if(!function_exists('ftp_connect')){ 253 msg("FTP support not found - safemode workaround not usable",-1); 254 return false; 255 } 256 257 $conn = @ftp_connect($conf['ftp']['host'],$conf['ftp']['port'],10); 258 if(!$conn){ 259 msg("FTP connection failed",-1); 260 return false; 261 } 262 263 if(!@ftp_login($conn, $conf['ftp']['user'], $conf['ftp']['pass'])){ 264 msg("FTP login failed",-1); 265 return false; 266 } 267 268 //create directory 269 $ok = @ftp_mkdir($conn, $dir); 270 //set permissions 271 @ftp_site($conn,sprintf("CHMOD %04o %s",$conf['dmode'],$dir)); 272 273 @ftp_close($conn); 274 return $ok; 275} 276 277/** 278 * downloads a file from the net and saves it 279 * 280 * if $useAttachment is false, 281 * - $file is the full filename to save the file, incl. path 282 * - if successful will return true, false otherwise 283 284 * if $useAttachment is true, 285 * - $file is the directory where the file should be saved 286 * - if successful will return the name used for the saved file, false otherwise 287 * 288 * @author Andreas Gohr <andi@splitbrain.org> 289 * @author Chris Smith <chris@jalakai.co.uk> 290 */ 291function io_download($url,$file,$useAttachment=false,$defaultName=''){ 292 global $conf; 293 $http = new DokuHTTPClient(); 294 $http->max_bodysize = 2*1024*1024; //max. 2MB 295 $http->timeout = 25; //max. 25 sec 296 297 $data = $http->get($url); 298 if(!$data) return false; 299 300 if ($useAttachment) { 301 $name = ''; 302 if (isset($http->resp_headers['content-disposition'])) { 303 $content_disposition = $http->resp_headers['content-disposition']; 304 $match=array(); 305 if (is_string($content_disposition) && 306 preg_match('/attachment;\s*filename\s*=\s*"([^"]*)"/i', $content_disposition, $match)) { 307 308 $name = basename($match[1]); 309 } 310 311 } 312 313 if (!$name) { 314 if (!$defaultName) return false; 315 $name = $defaultName; 316 } 317 318 $file = $file.$name; 319 } 320 321 $fileexists = file_exists($file); 322 $fp = @fopen($file,"w"); 323 if(!$fp) return false; 324 fwrite($fp,$data); 325 fclose($fp); 326 if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']); 327 if ($useAttachment) return $name; 328 return true; 329} 330 331/** 332 * Windows compatible rename 333 * 334 * rename() can not overwrite existing files on Windows 335 * this function will use copy/unlink instead 336 */ 337function io_rename($from,$to){ 338 global $conf; 339 if(!@rename($from,$to)){ 340 if(@copy($from,$to)){ 341 if($conf['fperm']) chmod($file, $conf['fperm']); 342 @unlink($from); 343 return true; 344 } 345 return false; 346 } 347 return true; 348} 349 350 351/** 352 * Runs an external command and returns it's output as string 353 * 354 * @author Harry Brueckner <harry_b@eml.cc> 355 * @author Andreas Gohr <andi@splitbrain.org> 356 * @deprecated 357 */ 358function io_runcmd($cmd){ 359 $fh = popen($cmd, "r"); 360 if(!$fh) return false; 361 $ret = ''; 362 while (!feof($fh)) { 363 $ret .= fread($fh, 8192); 364 } 365 pclose($fh); 366 return $ret; 367} 368 369/** 370 * Search a file for matching lines 371 * 372 * This is probably not faster than file()+preg_grep() but less 373 * memory intensive because not the whole file needs to be loaded 374 * at once. 375 * 376 * @author Andreas Gohr <andi@splitbrain.org> 377 * @param string $file The file to search 378 * @param string $pattern PCRE pattern 379 * @param int $max How many lines to return (0 for all) 380 * @param bool $baxkref When true returns array with backreferences instead of lines 381 * @return matching lines or backref, false on error 382 */ 383function io_grep($file,$pattern,$max=0,$backref=false){ 384 $fh = @fopen($file,'r'); 385 if(!$fh) return false; 386 $matches = array(); 387 388 $cnt = 0; 389 $line = ''; 390 while (!feof($fh)) { 391 $line .= fgets($fh, 4096); // read full line 392 if(substr($line,-1) != "\n") continue; 393 394 // check if line matches 395 if(preg_match($pattern,$line,$match)){ 396 if($backref){ 397 $matches[] = $match; 398 }else{ 399 $matches[] = $line; 400 } 401 $cnt++; 402 } 403 if($max && $max == $cnt) break; 404 $line = ''; 405 } 406 fclose($fh); 407 return $matches; 408} 409 410//Setup VIM: ex: et ts=2 enc=utf-8 : 411