splitbrain /
dokuwiki
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * File IO functions |
||
| 4 | * |
||
| 5 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) |
||
| 6 | * @author Andreas Gohr <[email protected]> |
||
| 7 | */ |
||
| 8 | |||
| 9 | if(!defined('DOKU_INC')) die('meh.'); |
||
| 10 | |||
| 11 | /** |
||
| 12 | * Removes empty directories |
||
| 13 | * |
||
| 14 | * Sends IO_NAMESPACE_DELETED events for 'pages' and 'media' namespaces. |
||
| 15 | * Event data: |
||
| 16 | * $data[0] ns: The colon separated namespace path minus the trailing page name. |
||
| 17 | * $data[1] ns_type: 'pages' or 'media' namespace tree. |
||
| 18 | * |
||
| 19 | * @todo use safemode hack |
||
| 20 | * @param string $id - a pageid, the namespace of that id will be tried to deleted |
||
| 21 | * @param string $basedir - the config name of the type to delete (datadir or mediadir usally) |
||
| 22 | * @return bool - true if at least one namespace was deleted |
||
| 23 | * |
||
| 24 | * @author Andreas Gohr <[email protected]> |
||
| 25 | * @author Ben Coburn <[email protected]> |
||
| 26 | */ |
||
| 27 | function io_sweepNS($id,$basedir='datadir'){ |
||
| 28 | global $conf; |
||
| 29 | $types = array ('datadir'=>'pages', 'mediadir'=>'media'); |
||
| 30 | $ns_type = (isset($types[$basedir])?$types[$basedir]:false); |
||
| 31 | |||
| 32 | $delone = false; |
||
| 33 | |||
| 34 | //scan all namespaces |
||
| 35 | while(($id = getNS($id)) !== false){ |
||
| 36 | $dir = $conf[$basedir].'/'.utf8_encodeFN(str_replace(':','/',$id)); |
||
| 37 | |||
| 38 | //try to delete dir else return |
||
| 39 | if(@rmdir($dir)) { |
||
| 40 | if ($ns_type!==false) { |
||
| 41 | $data = array($id, $ns_type); |
||
| 42 | $delone = true; // we deleted at least one dir |
||
| 43 | trigger_event('IO_NAMESPACE_DELETED', $data); |
||
| 44 | } |
||
| 45 | } else { return $delone; } |
||
| 46 | } |
||
| 47 | return $delone; |
||
| 48 | } |
||
| 49 | |||
| 50 | /** |
||
| 51 | * Used to read in a DokuWiki page from file, and send IO_WIKIPAGE_READ events. |
||
| 52 | * |
||
| 53 | * Generates the action event which delegates to io_readFile(). |
||
| 54 | * Action plugins are allowed to modify the page content in transit. |
||
| 55 | * The file path should not be changed. |
||
| 56 | * |
||
| 57 | * Event data: |
||
| 58 | * $data[0] The raw arguments for io_readFile as an array. |
||
| 59 | * $data[1] ns: The colon separated namespace path minus the trailing page name. (false if root ns) |
||
| 60 | * $data[2] page_name: The wiki page name. |
||
| 61 | * $data[3] rev: The page revision, false for current wiki pages. |
||
| 62 | * |
||
| 63 | * @author Ben Coburn <[email protected]> |
||
| 64 | * |
||
| 65 | * @param string $file filename |
||
| 66 | * @param string $id page id |
||
| 67 | * @param bool|int $rev revision timestamp |
||
| 68 | * @return string |
||
| 69 | */ |
||
| 70 | function io_readWikiPage($file, $id, $rev=false) { |
||
| 71 | if (empty($rev)) { $rev = false; } |
||
| 72 | $data = array(array($file, true), getNS($id), noNS($id), $rev); |
||
| 73 | return trigger_event('IO_WIKIPAGE_READ', $data, '_io_readWikiPage_action', false); |
||
| 74 | } |
||
| 75 | |||
| 76 | /** |
||
| 77 | * Callback adapter for io_readFile(). |
||
| 78 | * |
||
| 79 | * @author Ben Coburn <[email protected]> |
||
| 80 | * |
||
| 81 | * @param array $data event data |
||
| 82 | * @return string |
||
| 83 | */ |
||
| 84 | function _io_readWikiPage_action($data) { |
||
| 85 | if (is_array($data) && is_array($data[0]) && count($data[0])===2) { |
||
| 86 | return call_user_func_array('io_readFile', $data[0]); |
||
| 87 | } else { |
||
| 88 | return ''; //callback error |
||
| 89 | } |
||
| 90 | } |
||
| 91 | |||
| 92 | /** |
||
| 93 | * Returns content of $file as cleaned string. |
||
| 94 | * |
||
| 95 | * Uses gzip if extension is .gz |
||
| 96 | * |
||
| 97 | * If you want to use the returned value in unserialize |
||
| 98 | * be sure to set $clean to false! |
||
| 99 | * |
||
| 100 | * @author Andreas Gohr <[email protected]> |
||
| 101 | * |
||
| 102 | * @param string $file filename |
||
| 103 | * @param bool $clean |
||
| 104 | * @return string|bool the file contents or false on error |
||
| 105 | */ |
||
| 106 | function io_readFile($file,$clean=true){ |
||
| 107 | $ret = ''; |
||
| 108 | if(file_exists($file)){ |
||
| 109 | if(substr($file,-3) == '.gz'){ |
||
| 110 | $ret = gzfile($file); |
||
| 111 | if(is_array($ret)) $ret = join('', $ret); |
||
| 112 | }else if(substr($file,-4) == '.bz2'){ |
||
| 113 | $ret = bzfile($file); |
||
| 114 | }else{ |
||
| 115 | $ret = file_get_contents($file); |
||
| 116 | } |
||
| 117 | } |
||
| 118 | if($ret === null) return false; |
||
| 119 | if($ret !== false && $clean){ |
||
| 120 | return cleanText($ret); |
||
| 121 | }else{ |
||
| 122 | return $ret; |
||
| 123 | } |
||
| 124 | } |
||
| 125 | /** |
||
| 126 | * Returns the content of a .bz2 compressed file as string |
||
| 127 | * |
||
| 128 | * @author marcel senf <[email protected]> |
||
| 129 | * @author Andreas Gohr <[email protected]> |
||
| 130 | * |
||
| 131 | * @param string $file filename |
||
| 132 | * @param bool $array return array of lines |
||
| 133 | * @return string|array|bool content or false on error |
||
| 134 | */ |
||
| 135 | function bzfile($file, $array=false) { |
||
| 136 | $bz = bzopen($file,"r"); |
||
| 137 | if($bz === false) return false; |
||
| 138 | |||
| 139 | if($array) $lines = array(); |
||
| 140 | $str = ''; |
||
| 141 | while (!feof($bz)) { |
||
| 142 | //8192 seems to be the maximum buffersize? |
||
| 143 | $buffer = bzread($bz,8192); |
||
| 144 | if(($buffer === false) || (bzerrno($bz) !== 0)) { |
||
| 145 | return false; |
||
| 146 | } |
||
| 147 | $str = $str . $buffer; |
||
| 148 | if($array) { |
||
| 149 | $pos = strpos($str, "\n"); |
||
| 150 | while($pos !== false) { |
||
| 151 | $lines[] = substr($str, 0, $pos+1); |
||
| 152 | $str = substr($str, $pos+1); |
||
| 153 | $pos = strpos($str, "\n"); |
||
| 154 | } |
||
| 155 | } |
||
| 156 | } |
||
| 157 | bzclose($bz); |
||
| 158 | if($array) { |
||
| 159 | if($str !== '') $lines[] = $str; |
||
| 160 | return $lines; |
||
| 161 | } |
||
| 162 | return $str; |
||
| 163 | } |
||
| 164 | |||
| 165 | /** |
||
| 166 | * Used to write out a DokuWiki page to file, and send IO_WIKIPAGE_WRITE events. |
||
| 167 | * |
||
| 168 | * This generates an action event and delegates to io_saveFile(). |
||
| 169 | * Action plugins are allowed to modify the page content in transit. |
||
| 170 | * The file path should not be changed. |
||
| 171 | * (The append parameter is set to false.) |
||
| 172 | * |
||
| 173 | * Event data: |
||
| 174 | * $data[0] The raw arguments for io_saveFile as an array. |
||
| 175 | * $data[1] ns: The colon separated namespace path minus the trailing page name. (false if root ns) |
||
| 176 | * $data[2] page_name: The wiki page name. |
||
| 177 | * $data[3] rev: The page revision, false for current wiki pages. |
||
| 178 | * |
||
| 179 | * @author Ben Coburn <[email protected]> |
||
| 180 | * |
||
| 181 | * @param string $file filename |
||
| 182 | * @param string $content |
||
| 183 | * @param string $id page id |
||
| 184 | * @param int|bool $rev timestamp of revision |
||
| 185 | * @return bool |
||
| 186 | */ |
||
| 187 | function io_writeWikiPage($file, $content, $id, $rev=false) { |
||
| 188 | if (empty($rev)) { $rev = false; } |
||
| 189 | if ($rev===false) { io_createNamespace($id); } // create namespaces as needed |
||
| 190 | $data = array(array($file, $content, false), getNS($id), noNS($id), $rev); |
||
| 191 | return trigger_event('IO_WIKIPAGE_WRITE', $data, '_io_writeWikiPage_action', false); |
||
| 192 | } |
||
| 193 | |||
| 194 | /** |
||
| 195 | * Callback adapter for io_saveFile(). |
||
| 196 | * @author Ben Coburn <[email protected]> |
||
| 197 | * |
||
| 198 | * @param array $data event data |
||
| 199 | * @return bool |
||
| 200 | */ |
||
| 201 | function _io_writeWikiPage_action($data) { |
||
| 202 | if (is_array($data) && is_array($data[0]) && count($data[0])===3) { |
||
| 203 | return call_user_func_array('io_saveFile', $data[0]); |
||
| 204 | } else { |
||
| 205 | return false; //callback error |
||
| 206 | } |
||
| 207 | } |
||
| 208 | |||
| 209 | /** |
||
| 210 | * Internal function to save contents to a file. |
||
| 211 | * |
||
| 212 | * @author Andreas Gohr <[email protected]> |
||
| 213 | * |
||
| 214 | * @param string $file filename path to file |
||
| 215 | * @param string $content |
||
| 216 | * @param bool $append |
||
| 217 | * @return bool true on success, otherwise false |
||
| 218 | */ |
||
| 219 | function _io_saveFile($file, $content, $append) { |
||
| 220 | global $conf; |
||
| 221 | $mode = ($append) ? 'ab' : 'wb'; |
||
| 222 | $fileexists = file_exists($file); |
||
| 223 | |||
| 224 | if(substr($file,-3) == '.gz'){ |
||
| 225 | $fh = @gzopen($file,$mode.'9'); |
||
| 226 | if(!$fh) return false; |
||
| 227 | gzwrite($fh, $content); |
||
| 228 | gzclose($fh); |
||
| 229 | }else if(substr($file,-4) == '.bz2'){ |
||
| 230 | if($append) { |
||
| 231 | $bzcontent = bzfile($file); |
||
| 232 | if($bzcontent === false) return false; |
||
| 233 | $content = $bzcontent.$content; |
||
| 234 | } |
||
| 235 | $fh = @bzopen($file,'w'); |
||
| 236 | if(!$fh) return false; |
||
| 237 | bzwrite($fh, $content); |
||
| 238 | bzclose($fh); |
||
| 239 | }else{ |
||
| 240 | $fh = @fopen($file,$mode); |
||
| 241 | if(!$fh) return false; |
||
| 242 | fwrite($fh, $content); |
||
| 243 | fclose($fh); |
||
| 244 | } |
||
| 245 | |||
| 246 | if(!$fileexists and !empty($conf['fperm'])) chmod($file, $conf['fperm']); |
||
| 247 | return true; |
||
| 248 | } |
||
| 249 | |||
| 250 | /** |
||
| 251 | * Saves $content to $file. |
||
| 252 | * |
||
| 253 | * If the third parameter is set to true the given content |
||
| 254 | * will be appended. |
||
| 255 | * |
||
| 256 | * Uses gzip if extension is .gz |
||
| 257 | * and bz2 if extension is .bz2 |
||
| 258 | * |
||
| 259 | * @author Andreas Gohr <[email protected]> |
||
| 260 | * |
||
| 261 | * @param string $file filename path to file |
||
| 262 | * @param string $content |
||
| 263 | * @param bool $append |
||
| 264 | * @return bool true on success, otherwise false |
||
| 265 | */ |
||
| 266 | function io_saveFile($file, $content, $append=false) { |
||
| 267 | io_makeFileDir($file); |
||
| 268 | io_lock($file); |
||
| 269 | if(!_io_saveFile($file, $content, $append)) { |
||
| 270 | msg("Writing $file failed",-1); |
||
| 271 | io_unlock($file); |
||
| 272 | return false; |
||
| 273 | } |
||
| 274 | io_unlock($file); |
||
| 275 | return true; |
||
| 276 | } |
||
| 277 | |||
| 278 | /** |
||
| 279 | * Replace one or more occurrences of a line in a file. |
||
| 280 | * |
||
| 281 | * The default, when $maxlines is 0 is to delete all matching lines then append a single line. |
||
| 282 | * A regex that matches any part of the line will remove the entire line in this mode. |
||
| 283 | * Captures in $newline are not available. |
||
| 284 | * |
||
| 285 | * Otherwise each line is matched and replaced individually, up to the first $maxlines lines |
||
| 286 | * or all lines if $maxlines is -1. If $regex is true then captures can be used in $newline. |
||
| 287 | * |
||
| 288 | * Be sure to include the trailing newline in $oldline when replacing entire lines. |
||
| 289 | * |
||
| 290 | * Uses gzip if extension is .gz |
||
| 291 | * and bz2 if extension is .bz2 |
||
| 292 | * |
||
| 293 | * @author Steven Danz <[email protected]> |
||
| 294 | * @author Christopher Smith <[email protected]> |
||
| 295 | * @author Patrick Brown <[email protected]> |
||
| 296 | * |
||
| 297 | * @param string $file filename |
||
| 298 | * @param string $oldline exact linematch to remove |
||
| 299 | * @param string $newline new line to insert |
||
| 300 | * @param bool $regex use regexp? |
||
| 301 | * @param int $maxlines number of occurrences of the line to replace |
||
| 302 | * @return bool true on success |
||
| 303 | */ |
||
| 304 | function io_replaceInFile($file, $oldline, $newline, $regex=false, $maxlines=0) { |
||
| 305 | if ((string)$oldline === '') { |
||
| 306 | trigger_error('$oldline parameter cannot be empty in io_replaceInFile()', E_USER_WARNING); |
||
| 307 | return false; |
||
| 308 | } |
||
| 309 | |||
| 310 | if (!file_exists($file)) return true; |
||
| 311 | |||
| 312 | io_lock($file); |
||
| 313 | |||
| 314 | // load into array |
||
| 315 | if(substr($file,-3) == '.gz'){ |
||
| 316 | $lines = gzfile($file); |
||
| 317 | }else if(substr($file,-4) == '.bz2'){ |
||
| 318 | $lines = bzfile($file, true); |
||
| 319 | }else{ |
||
| 320 | $lines = file($file); |
||
| 321 | } |
||
| 322 | |||
| 323 | // make non-regexes into regexes |
||
| 324 | $pattern = $regex ? $oldline : '/^'.preg_quote($oldline,'/').'$/'; |
||
| 325 | $replace = $regex ? $newline : addcslashes($newline, '\$'); |
||
| 326 | |||
| 327 | // remove matching lines |
||
| 328 | if ($maxlines > 0) { |
||
| 329 | $count = 0; |
||
| 330 | $matched = 0; |
||
| 331 | while (($count < $maxlines) && (list($i,$line) = each($lines))) { |
||
| 332 | // $matched will be set to 0|1 depending on whether pattern is matched and line replaced |
||
| 333 | $lines[$i] = preg_replace($pattern, $replace, $line, -1, $matched); |
||
| 334 | if ($matched) $count++; |
||
| 335 | } |
||
| 336 | } else if ($maxlines == 0) { |
||
| 337 | $lines = preg_grep($pattern, $lines, PREG_GREP_INVERT); |
||
| 338 | |||
| 339 | if ((string)$newline !== ''){ |
||
| 340 | $lines[] = $newline; |
||
| 341 | } |
||
| 342 | } else { |
||
| 343 | $lines = preg_replace($pattern, $replace, $lines); |
||
| 344 | } |
||
| 345 | |||
| 346 | if(count($lines)){ |
||
| 347 | if(!_io_saveFile($file, join('',$lines), false)) { |
||
| 348 | msg("Removing content from $file failed",-1); |
||
| 349 | io_unlock($file); |
||
| 350 | return false; |
||
| 351 | } |
||
| 352 | }else{ |
||
| 353 | @unlink($file); |
||
| 354 | } |
||
| 355 | |||
| 356 | io_unlock($file); |
||
| 357 | return true; |
||
| 358 | } |
||
| 359 | |||
| 360 | /** |
||
| 361 | * Delete lines that match $badline from $file. |
||
| 362 | * |
||
| 363 | * Be sure to include the trailing newline in $badline |
||
| 364 | * |
||
| 365 | * @author Patrick Brown <[email protected]> |
||
| 366 | * |
||
| 367 | * @param string $file filename |
||
| 368 | * @param string $badline exact linematch to remove |
||
| 369 | * @param bool $regex use regexp? |
||
| 370 | * @return bool true on success |
||
| 371 | */ |
||
| 372 | function io_deleteFromFile($file,$badline,$regex=false){ |
||
| 373 | return io_replaceInFile($file,$badline,null,$regex,0); |
||
| 374 | } |
||
| 375 | |||
| 376 | /** |
||
| 377 | * Tries to lock a file |
||
| 378 | * |
||
| 379 | * Locking is only done for io_savefile and uses directories |
||
| 380 | * inside $conf['lockdir'] |
||
| 381 | * |
||
| 382 | * It waits maximal 3 seconds for the lock, after this time |
||
| 383 | * the lock is assumed to be stale and the function goes on |
||
| 384 | * |
||
| 385 | * @author Andreas Gohr <[email protected]> |
||
| 386 | * |
||
| 387 | * @param string $file filename |
||
| 388 | */ |
||
| 389 | function io_lock($file){ |
||
| 390 | global $conf; |
||
| 391 | // no locking if safemode hack |
||
| 392 | if($conf['safemodehack']) return; |
||
| 393 | |||
| 394 | $lockDir = $conf['lockdir'].'/'.md5($file); |
||
| 395 | @ignore_user_abort(1); |
||
| 396 | |||
| 397 | $timeStart = time(); |
||
| 398 | do { |
||
| 399 | //waited longer than 3 seconds? -> stale lock |
||
| 400 | if ((time() - $timeStart) > 3) break; |
||
| 401 | $locked = @mkdir($lockDir, $conf['dmode']); |
||
| 402 | if($locked){ |
||
| 403 | if(!empty($conf['dperm'])) chmod($lockDir, $conf['dperm']); |
||
| 404 | break; |
||
| 405 | } |
||
| 406 | usleep(50); |
||
| 407 | } while ($locked === false); |
||
| 408 | } |
||
| 409 | |||
| 410 | /** |
||
| 411 | * Unlocks a file |
||
| 412 | * |
||
| 413 | * @author Andreas Gohr <[email protected]> |
||
| 414 | * |
||
| 415 | * @param string $file filename |
||
| 416 | */ |
||
| 417 | function io_unlock($file){ |
||
| 418 | global $conf; |
||
| 419 | // no locking if safemode hack |
||
| 420 | if($conf['safemodehack']) return; |
||
| 421 | |||
| 422 | $lockDir = $conf['lockdir'].'/'.md5($file); |
||
| 423 | @rmdir($lockDir); |
||
| 424 | @ignore_user_abort(0); |
||
| 425 | } |
||
| 426 | |||
| 427 | /** |
||
| 428 | * Create missing namespace directories and send the IO_NAMESPACE_CREATED events |
||
| 429 | * in the order of directory creation. (Parent directories first.) |
||
| 430 | * |
||
| 431 | * Event data: |
||
| 432 | * $data[0] ns: The colon separated namespace path minus the trailing page name. |
||
| 433 | * $data[1] ns_type: 'pages' or 'media' namespace tree. |
||
| 434 | * |
||
| 435 | * @author Ben Coburn <[email protected]> |
||
| 436 | * |
||
| 437 | * @param string $id page id |
||
| 438 | * @param string $ns_type 'pages' or 'media' |
||
| 439 | */ |
||
| 440 | function io_createNamespace($id, $ns_type='pages') { |
||
| 441 | // verify ns_type |
||
| 442 | $types = array('pages'=>'wikiFN', 'media'=>'mediaFN'); |
||
| 443 | if (!isset($types[$ns_type])) { |
||
| 444 | trigger_error('Bad $ns_type parameter for io_createNamespace().'); |
||
| 445 | return; |
||
| 446 | } |
||
| 447 | // make event list |
||
| 448 | $missing = array(); |
||
| 449 | $ns_stack = explode(':', $id); |
||
| 450 | $ns = $id; |
||
| 451 | $tmp = dirname( $file = call_user_func($types[$ns_type], $ns) ); |
||
| 452 | while (!@is_dir($tmp) && !(file_exists($tmp) && !is_dir($tmp))) { |
||
| 453 | array_pop($ns_stack); |
||
| 454 | $ns = implode(':', $ns_stack); |
||
| 455 | if (strlen($ns)==0) { break; } |
||
| 456 | $missing[] = $ns; |
||
| 457 | $tmp = dirname(call_user_func($types[$ns_type], $ns)); |
||
| 458 | } |
||
| 459 | // make directories |
||
| 460 | io_makeFileDir($file); |
||
| 461 | // send the events |
||
| 462 | $missing = array_reverse($missing); // inside out |
||
| 463 | foreach ($missing as $ns) { |
||
| 464 | $data = array($ns, $ns_type); |
||
| 465 | trigger_event('IO_NAMESPACE_CREATED', $data); |
||
| 466 | } |
||
| 467 | } |
||
| 468 | |||
| 469 | /** |
||
| 470 | * Create the directory needed for the given file |
||
| 471 | * |
||
| 472 | * @author Andreas Gohr <[email protected]> |
||
| 473 | * |
||
| 474 | * @param string $file file name |
||
| 475 | */ |
||
| 476 | function io_makeFileDir($file){ |
||
| 477 | $dir = dirname($file); |
||
| 478 | if(!@is_dir($dir)){ |
||
| 479 | io_mkdir_p($dir) || msg("Creating directory $dir failed",-1); |
||
| 480 | } |
||
| 481 | } |
||
| 482 | |||
| 483 | /** |
||
| 484 | * Creates a directory hierachy. |
||
| 485 | * |
||
| 486 | * @link http://www.php.net/manual/en/function.mkdir.php |
||
| 487 | * @author <[email protected]> |
||
| 488 | * @author Andreas Gohr <[email protected]> |
||
| 489 | * |
||
| 490 | * @param string $target filename |
||
| 491 | * @return bool|int|string |
||
| 492 | */ |
||
| 493 | function io_mkdir_p($target){ |
||
| 494 | global $conf; |
||
| 495 | if (@is_dir($target)||empty($target)) return 1; // best case check first |
||
| 496 | if (file_exists($target) && !is_dir($target)) return 0; |
||
| 497 | //recursion |
||
| 498 | if (io_mkdir_p(substr($target,0,strrpos($target,'/')))){ |
||
| 499 | if($conf['safemodehack']){ |
||
| 500 | $dir = preg_replace('/^'.preg_quote(fullpath($conf['ftp']['root']),'/').'/','', $target); |
||
| 501 | return io_mkdir_ftp($dir); |
||
| 502 | }else{ |
||
| 503 | $ret = @mkdir($target,$conf['dmode']); // crawl back up & create dir tree |
||
| 504 | if($ret && !empty($conf['dperm'])) chmod($target, $conf['dperm']); |
||
| 505 | return $ret; |
||
| 506 | } |
||
| 507 | } |
||
| 508 | return 0; |
||
| 509 | } |
||
| 510 | |||
| 511 | /** |
||
| 512 | * Recursively delete a directory |
||
| 513 | * |
||
| 514 | * @author Andreas Gohr <[email protected]> |
||
| 515 | * @param string $path |
||
| 516 | * @param bool $removefiles defaults to false which will delete empty directories only |
||
| 517 | * @return bool |
||
| 518 | */ |
||
| 519 | function io_rmdir($path, $removefiles = false) { |
||
| 520 | if(!is_string($path) || $path == "") return false; |
||
| 521 | if(!file_exists($path)) return true; // it's already gone or was never there, count as success |
||
| 522 | |||
| 523 | if(is_dir($path) && !is_link($path)) { |
||
| 524 | $dirs = array(); |
||
| 525 | $files = array(); |
||
| 526 | |||
| 527 | if(!$dh = @opendir($path)) return false; |
||
| 528 | while(false !== ($f = readdir($dh))) { |
||
| 529 | if($f == '..' || $f == '.') continue; |
||
| 530 | |||
| 531 | // collect dirs and files first |
||
| 532 | if(is_dir("$path/$f") && !is_link("$path/$f")) { |
||
| 533 | $dirs[] = "$path/$f"; |
||
| 534 | } else if($removefiles) { |
||
| 535 | $files[] = "$path/$f"; |
||
| 536 | } else { |
||
| 537 | return false; // abort when non empty |
||
| 538 | } |
||
| 539 | |||
| 540 | } |
||
| 541 | closedir($dh); |
||
| 542 | |||
| 543 | // now traverse into directories first |
||
| 544 | foreach($dirs as $dir) { |
||
| 545 | if(!io_rmdir($dir, $removefiles)) return false; // abort on any error |
||
| 546 | } |
||
| 547 | |||
| 548 | // now delete files |
||
| 549 | foreach($files as $file) { |
||
| 550 | if(!@unlink($file)) return false; //abort on any error |
||
| 551 | } |
||
| 552 | |||
| 553 | // remove self |
||
| 554 | return @rmdir($path); |
||
| 555 | } else if($removefiles) { |
||
| 556 | return @unlink($path); |
||
| 557 | } |
||
| 558 | return false; |
||
| 559 | } |
||
| 560 | |||
| 561 | /** |
||
| 562 | * Creates a directory using FTP |
||
| 563 | * |
||
| 564 | * This is used when the safemode workaround is enabled |
||
| 565 | * |
||
| 566 | * @author <[email protected]> |
||
| 567 | * |
||
| 568 | * @param string $dir name of the new directory |
||
| 569 | * @return false|string |
||
| 570 | */ |
||
| 571 | function io_mkdir_ftp($dir){ |
||
| 572 | global $conf; |
||
| 573 | |||
| 574 | if(!function_exists('ftp_connect')){ |
||
| 575 | msg("FTP support not found - safemode workaround not usable",-1); |
||
| 576 | return false; |
||
| 577 | } |
||
| 578 | |||
| 579 | $conn = @ftp_connect($conf['ftp']['host'],$conf['ftp']['port'],10); |
||
| 580 | if(!$conn){ |
||
| 581 | msg("FTP connection failed",-1); |
||
| 582 | return false; |
||
| 583 | } |
||
| 584 | |||
| 585 | if(!@ftp_login($conn, $conf['ftp']['user'], conf_decodeString($conf['ftp']['pass']))){ |
||
| 586 | msg("FTP login failed",-1); |
||
| 587 | return false; |
||
| 588 | } |
||
| 589 | |||
| 590 | //create directory |
||
| 591 | $ok = @ftp_mkdir($conn, $dir); |
||
| 592 | //set permissions |
||
| 593 | @ftp_site($conn,sprintf("CHMOD %04o %s",$conf['dmode'],$dir)); |
||
| 594 | |||
| 595 | @ftp_close($conn); |
||
| 596 | return $ok; |
||
| 597 | } |
||
| 598 | |||
| 599 | /** |
||
| 600 | * Creates a unique temporary directory and returns |
||
| 601 | * its path. |
||
| 602 | * |
||
| 603 | * @author Michael Klier <[email protected]> |
||
| 604 | * |
||
| 605 | * @return false|string path to new directory or false |
||
| 606 | */ |
||
| 607 | function io_mktmpdir() { |
||
| 608 | global $conf; |
||
| 609 | |||
| 610 | $base = $conf['tmpdir']; |
||
| 611 | $dir = md5(uniqid(mt_rand(), true)); |
||
| 612 | $tmpdir = $base.'/'.$dir; |
||
| 613 | |||
| 614 | if(io_mkdir_p($tmpdir)) { |
||
| 615 | return($tmpdir); |
||
| 616 | } else { |
||
| 617 | return false; |
||
| 618 | } |
||
| 619 | } |
||
| 620 | |||
| 621 | /** |
||
| 622 | * downloads a file from the net and saves it |
||
| 623 | * |
||
| 624 | * if $useAttachment is false, |
||
| 625 | * - $file is the full filename to save the file, incl. path |
||
| 626 | * - if successful will return true, false otherwise |
||
| 627 | * |
||
| 628 | * if $useAttachment is true, |
||
| 629 | * - $file is the directory where the file should be saved |
||
| 630 | * - if successful will return the name used for the saved file, false otherwise |
||
| 631 | * |
||
| 632 | * @author Andreas Gohr <[email protected]> |
||
| 633 | * @author Chris Smith <[email protected]> |
||
| 634 | * |
||
| 635 | * @param string $url url to download |
||
| 636 | * @param string $file path to file or directory where to save |
||
| 637 | * @param bool $useAttachment if true: try to use name of download, uses otherwise $defaultName, false: uses $file as path to file |
||
| 638 | * @param string $defaultName fallback for if using $useAttachment |
||
| 639 | * @param int $maxSize maximum file size |
||
| 640 | * @return bool|string if failed false, otherwise true or the name of the file in the given dir |
||
| 641 | */ |
||
| 642 | function io_download($url,$file,$useAttachment=false,$defaultName='',$maxSize=2097152){ |
||
| 643 | global $conf; |
||
| 644 | $http = new DokuHTTPClient(); |
||
| 645 | $http->max_bodysize = $maxSize; |
||
|
0 ignored issues
–
show
|
|||
| 646 | $http->timeout = 25; //max. 25 sec |
||
|
0 ignored issues
–
show
The property
timeout cannot be accessed from this context as it is declared private in class HTTPClient.
This check looks for access to properties that are not accessible from the current context. If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class. Loading history...
|
|||
| 647 | $http->keep_alive = false; // we do single ops here, no need for keep-alive |
||
|
0 ignored issues
–
show
The property
keep_alive cannot be accessed from this context as it is declared private in class HTTPClient.
This check looks for access to properties that are not accessible from the current context. If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class. Loading history...
|
|||
| 648 | |||
| 649 | $data = $http->get($url); |
||
| 650 | if(!$data) return false; |
||
| 651 | |||
| 652 | $name = ''; |
||
| 653 | if ($useAttachment) { |
||
| 654 | if (isset($http->resp_headers['content-disposition'])) { |
||
|
0 ignored issues
–
show
The property
resp_headers cannot be accessed from this context as it is declared private in class HTTPClient.
This check looks for access to properties that are not accessible from the current context. If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class. Loading history...
|
|||
| 655 | $content_disposition = $http->resp_headers['content-disposition']; |
||
|
0 ignored issues
–
show
The property
resp_headers cannot be accessed from this context as it is declared private in class HTTPClient.
This check looks for access to properties that are not accessible from the current context. If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class. Loading history...
|
|||
| 656 | $match=array(); |
||
| 657 | if (is_string($content_disposition) && |
||
| 658 | preg_match('/attachment;\s*filename\s*=\s*"([^"]*)"/i', $content_disposition, $match)) { |
||
| 659 | |||
| 660 | $name = utf8_basename($match[1]); |
||
| 661 | } |
||
| 662 | |||
| 663 | } |
||
| 664 | |||
| 665 | if (!$name) { |
||
| 666 | if (!$defaultName) return false; |
||
| 667 | $name = $defaultName; |
||
| 668 | } |
||
| 669 | |||
| 670 | $file = $file.$name; |
||
| 671 | } |
||
| 672 | |||
| 673 | $fileexists = file_exists($file); |
||
| 674 | $fp = @fopen($file,"w"); |
||
| 675 | if(!$fp) return false; |
||
| 676 | fwrite($fp,$data); |
||
| 677 | fclose($fp); |
||
| 678 | if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']); |
||
| 679 | if ($useAttachment) return $name; |
||
| 680 | return true; |
||
| 681 | } |
||
| 682 | |||
| 683 | /** |
||
| 684 | * Windows compatible rename |
||
| 685 | * |
||
| 686 | * rename() can not overwrite existing files on Windows |
||
| 687 | * this function will use copy/unlink instead |
||
| 688 | * |
||
| 689 | * @param string $from |
||
| 690 | * @param string $to |
||
| 691 | * @return bool succes or fail |
||
| 692 | */ |
||
| 693 | function io_rename($from,$to){ |
||
| 694 | global $conf; |
||
| 695 | if(!@rename($from,$to)){ |
||
| 696 | if(@copy($from,$to)){ |
||
| 697 | if($conf['fperm']) chmod($to, $conf['fperm']); |
||
| 698 | @unlink($from); |
||
| 699 | return true; |
||
| 700 | } |
||
| 701 | return false; |
||
| 702 | } |
||
| 703 | return true; |
||
| 704 | } |
||
| 705 | |||
| 706 | /** |
||
| 707 | * Runs an external command with input and output pipes. |
||
| 708 | * Returns the exit code from the process. |
||
| 709 | * |
||
| 710 | * @author Tom N Harris <[email protected]> |
||
| 711 | * |
||
| 712 | * @param string $cmd |
||
| 713 | * @param string $input input pipe |
||
| 714 | * @param string $output output pipe |
||
| 715 | * @return int exit code from process |
||
| 716 | */ |
||
| 717 | function io_exec($cmd, $input, &$output){ |
||
| 718 | $descspec = array( |
||
| 719 | 0=>array("pipe","r"), |
||
| 720 | 1=>array("pipe","w"), |
||
| 721 | 2=>array("pipe","w")); |
||
| 722 | $ph = proc_open($cmd, $descspec, $pipes); |
||
| 723 | if(!$ph) return -1; |
||
| 724 | fclose($pipes[2]); // ignore stderr |
||
| 725 | fwrite($pipes[0], $input); |
||
| 726 | fclose($pipes[0]); |
||
| 727 | $output = stream_get_contents($pipes[1]); |
||
| 728 | fclose($pipes[1]); |
||
| 729 | return proc_close($ph); |
||
| 730 | } |
||
| 731 | |||
| 732 | /** |
||
| 733 | * Search a file for matching lines |
||
| 734 | * |
||
| 735 | * This is probably not faster than file()+preg_grep() but less |
||
| 736 | * memory intensive because not the whole file needs to be loaded |
||
| 737 | * at once. |
||
| 738 | * |
||
| 739 | * @author Andreas Gohr <[email protected]> |
||
| 740 | * @param string $file The file to search |
||
| 741 | * @param string $pattern PCRE pattern |
||
| 742 | * @param int $max How many lines to return (0 for all) |
||
| 743 | * @param bool $backref When true returns array with backreferences instead of lines |
||
| 744 | * @return array matching lines or backref, false on error |
||
| 745 | */ |
||
| 746 | function io_grep($file,$pattern,$max=0,$backref=false){ |
||
| 747 | $fh = @fopen($file,'r'); |
||
| 748 | if(!$fh) return false; |
||
| 749 | $matches = array(); |
||
| 750 | |||
| 751 | $cnt = 0; |
||
| 752 | $line = ''; |
||
| 753 | while (!feof($fh)) { |
||
| 754 | $line .= fgets($fh, 4096); // read full line |
||
| 755 | if(substr($line,-1) != "\n") continue; |
||
| 756 | |||
| 757 | // check if line matches |
||
| 758 | if(preg_match($pattern,$line,$match)){ |
||
| 759 | if($backref){ |
||
| 760 | $matches[] = $match; |
||
| 761 | }else{ |
||
| 762 | $matches[] = $line; |
||
| 763 | } |
||
| 764 | $cnt++; |
||
| 765 | } |
||
| 766 | if($max && $max == $cnt) break; |
||
| 767 | $line = ''; |
||
| 768 | } |
||
| 769 | fclose($fh); |
||
| 770 | return $matches; |
||
| 771 | } |
||
| 772 | |||
| 773 |
This check looks for access to properties that are not accessible from the current context.
If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.