xref: /dokuwiki/inc/io.php (revision 77b9890307337d49a0f519746f6a6f4ee1d61334)
1ed7b5f09Sandi<?php
215fae107Sandi/**
315fae107Sandi * File IO functions
415fae107Sandi *
515fae107Sandi * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
615fae107Sandi * @author     Andreas Gohr <andi@splitbrain.org>
715fae107Sandi */
815fae107Sandi
9ed7b5f09Sandi  if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../').'/');
10ed7b5f09Sandi  require_once(DOKU_INC.'inc/common.php');
119b307a83SAndreas Gohr  require_once(DOKU_INC.'inc/HTTPClient.php');
12f3f0262cSandi
13f3f0262cSandi/**
1453d6ccfeSandi * Removes empty directories
1553d6ccfeSandi *
1653d6ccfeSandi * @todo use safemode hack
1753d6ccfeSandi * @author  Andreas Gohr <andi@splitbrain.org>
1853d6ccfeSandi */
19755f1e03SAndreas Gohrfunction io_sweepNS($id,$basedir='datadir'){
2053d6ccfeSandi  global $conf;
2153d6ccfeSandi
2253d6ccfeSandi  //scan all namespaces
2353d6ccfeSandi  while(($id = getNS($id)) !== false){
24755f1e03SAndreas Gohr    $dir = $conf[$basedir].'/'.utf8_encodeFN(str_replace(':','/',$id));
2553d6ccfeSandi
2653d6ccfeSandi    //try to delete dir else return
2753d6ccfeSandi    if(!@rmdir($dir)) return;
2853d6ccfeSandi  }
2953d6ccfeSandi}
3053d6ccfeSandi
3153d6ccfeSandi/**
3215fae107Sandi * Returns content of $file as cleaned string.
3315fae107Sandi *
3415fae107Sandi * Uses gzip if extension is .gz
3515fae107Sandi *
36ee4c4a1bSAndreas Gohr * If you want to use the returned value in unserialize
37ee4c4a1bSAndreas Gohr * be sure to set $clean to false!
38ee4c4a1bSAndreas Gohr *
3915fae107Sandi * @author  Andreas Gohr <andi@splitbrain.org>
40f3f0262cSandi */
41e34c0709SAndreas Gohrfunction io_readFile($file,$clean=true){
42f3f0262cSandi  $ret = '';
43f3f0262cSandi  if(@file_exists($file)){
44f3f0262cSandi    if(substr($file,-3) == '.gz'){
45f3f0262cSandi      $ret = join('',gzfile($file));
46f3f0262cSandi    }else{
47f3f0262cSandi      $ret = join('',file($file));
48f3f0262cSandi    }
49f3f0262cSandi  }
50e34c0709SAndreas Gohr  if($clean){
51f3f0262cSandi    return cleanText($ret);
52e34c0709SAndreas Gohr  }else{
53e34c0709SAndreas Gohr    return $ret;
54e34c0709SAndreas Gohr  }
55f3f0262cSandi}
56f3f0262cSandi
57f3f0262cSandi/**
5815fae107Sandi * Saves $content to $file.
59f3f0262cSandi *
601380fc45SAndreas Gohr * If the third parameter is set to true the given content
611380fc45SAndreas Gohr * will be appended.
621380fc45SAndreas Gohr *
6315fae107Sandi * Uses gzip if extension is .gz
6415fae107Sandi *
6515fae107Sandi * @author  Andreas Gohr <andi@splitbrain.org>
6615fae107Sandi * @return bool true on success
67f3f0262cSandi */
681380fc45SAndreas Gohrfunction io_saveFile($file,$content,$append=false){
69ac9115b0STroels Liebe Bentsen  global $conf;
701380fc45SAndreas Gohr  $mode = ($append) ? 'ab' : 'wb';
711380fc45SAndreas Gohr
72ac9115b0STroels Liebe Bentsen  $fileexists = file_exists($file);
73f3f0262cSandi  io_makeFileDir($file);
7490eb8392Sandi  io_lock($file);
75f3f0262cSandi  if(substr($file,-3) == '.gz'){
761380fc45SAndreas Gohr    $fh = @gzopen($file,$mode.'9');
77f3f0262cSandi    if(!$fh){
78f3f0262cSandi      msg("Writing $file failed",-1);
79f3f0262cSandi      return false;
80f3f0262cSandi    }
81f3f0262cSandi    gzwrite($fh, $content);
82f3f0262cSandi    gzclose($fh);
83f3f0262cSandi  }else{
841380fc45SAndreas Gohr    $fh = @fopen($file,$mode);
85f3f0262cSandi    if(!$fh){
86f3f0262cSandi      msg("Writing $file failed",-1);
87f3f0262cSandi      return false;
88f3f0262cSandi    }
89f3f0262cSandi    fwrite($fh, $content);
90f3f0262cSandi    fclose($fh);
91f3f0262cSandi  }
92ac9115b0STroels Liebe Bentsen
931ca31cfeSAndreas Gohr  if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
9490eb8392Sandi  io_unlock($file);
95f3f0262cSandi  return true;
96f3f0262cSandi}
97f3f0262cSandi
98f3f0262cSandi/**
991380fc45SAndreas Gohr * Delete exact linematch for $badline from $file.
1001380fc45SAndreas Gohr *
1011380fc45SAndreas Gohr * Be sure to include the trailing newline in $badline
102b158d625SSteven Danz *
103b158d625SSteven Danz * Uses gzip if extension is .gz
104b158d625SSteven Danz *
1058b06d178Schris * 2005-10-14 : added regex option -- Christopher Smith <chris@jalakai.co.uk>
1068b06d178Schris *
107b158d625SSteven Danz * @author Steven Danz <steven-danz@kc.rr.com>
108b158d625SSteven Danz * @return bool true on success
109b158d625SSteven Danz */
1108b06d178Schrisfunction io_deleteFromFile($file,$badline,$regex=false){
1111380fc45SAndreas Gohr  if (!@file_exists($file)) return true;
1121380fc45SAndreas Gohr
113b158d625SSteven Danz  io_lock($file);
1141380fc45SAndreas Gohr
1151380fc45SAndreas Gohr  // load into array
116b158d625SSteven Danz  if(substr($file,-3) == '.gz'){
1171380fc45SAndreas Gohr    $lines = gzfile($file);
118b158d625SSteven Danz  }else{
1191380fc45SAndreas Gohr    $lines = file($file);
120b158d625SSteven Danz  }
121b158d625SSteven Danz
1221380fc45SAndreas Gohr  // remove all matching lines
1238b06d178Schris  if ($regex) {
1248b06d178Schris    $lines = preg_grep($badline,$lines,PREG_GREP_INVERT);
1258b06d178Schris  } else {
1261380fc45SAndreas Gohr    $pos = array_search($badline,$lines); //return null or false if not found
1271380fc45SAndreas Gohr    while(is_int($pos)){
1281380fc45SAndreas Gohr      unset($lines[$pos]);
1291380fc45SAndreas Gohr      $pos = array_search($badline,$lines);
130b158d625SSteven Danz    }
1318b06d178Schris  }
132b158d625SSteven Danz
1331380fc45SAndreas Gohr  if(count($lines)){
1341380fc45SAndreas Gohr    $content = join('',$lines);
135b158d625SSteven Danz    if(substr($file,-3) == '.gz'){
136b158d625SSteven Danz      $fh = @gzopen($file,'wb9');
137b158d625SSteven Danz      if(!$fh){
138b158d625SSteven Danz        msg("Removing content from $file failed",-1);
139b158d625SSteven Danz        return false;
140b158d625SSteven Danz      }
141b158d625SSteven Danz      gzwrite($fh, $content);
142b158d625SSteven Danz      gzclose($fh);
143b158d625SSteven Danz    }else{
144b158d625SSteven Danz      $fh = @fopen($file,'wb');
145b158d625SSteven Danz      if(!$fh){
146b158d625SSteven Danz        msg("Removing content from $file failed",-1);
147b158d625SSteven Danz        return false;
148b158d625SSteven Danz      }
149b158d625SSteven Danz      fwrite($fh, $content);
150b158d625SSteven Danz      fclose($fh);
151b158d625SSteven Danz    }
152b158d625SSteven Danz  }else{
153b158d625SSteven Danz    @unlink($file);
154b158d625SSteven Danz  }
155b158d625SSteven Danz
156b158d625SSteven Danz  io_unlock($file);
157b158d625SSteven Danz  return true;
158b158d625SSteven Danz}
159b158d625SSteven Danz
160b158d625SSteven Danz/**
16190eb8392Sandi * Tries to lock a file
16290eb8392Sandi *
16390eb8392Sandi * Locking is only done for io_savefile and uses directories
16490eb8392Sandi * inside $conf['lockdir']
16590eb8392Sandi *
16690eb8392Sandi * It waits maximal 3 seconds for the lock, after this time
16790eb8392Sandi * the lock is assumed to be stale and the function goes on
16890eb8392Sandi *
16990eb8392Sandi * @author Andreas Gohr <andi@splitbrain.org>
17090eb8392Sandi */
17190eb8392Sandifunction io_lock($file){
17290eb8392Sandi  global $conf;
17390eb8392Sandi  // no locking if safemode hack
17490eb8392Sandi  if($conf['safemodehack']) return;
17590eb8392Sandi
17690eb8392Sandi  $lockDir = $conf['lockdir'].'/'.md5($file);
17790eb8392Sandi  @ignore_user_abort(1);
17890eb8392Sandi
17990eb8392Sandi  $timeStart = time();
18090eb8392Sandi  do {
18190eb8392Sandi    //waited longer than 3 seconds? -> stale lock
18290eb8392Sandi    if ((time() - $timeStart) > 3) break;
18344881d27STroels Liebe Bentsen    $locked = @mkdir($lockDir, $conf['dmode']);
184*77b98903SAndreas Gohr    if($locked){
185*77b98903SAndreas Gohr      if($conf['dperm']) chmod($lockDir, $conf['dperm']);
186*77b98903SAndreas Gohr      break;
187*77b98903SAndreas Gohr    }
188*77b98903SAndreas Gohr    usleep(50);
18990eb8392Sandi  } while ($locked === false);
19090eb8392Sandi}
19190eb8392Sandi
19290eb8392Sandi/**
19390eb8392Sandi * Unlocks a file
19490eb8392Sandi *
19590eb8392Sandi * @author Andreas Gohr <andi@splitbrain.org>
19690eb8392Sandi */
19790eb8392Sandifunction io_unlock($file){
19890eb8392Sandi  global $conf;
19990eb8392Sandi  // no locking if safemode hack
20090eb8392Sandi  if($conf['safemodehack']) return;
20190eb8392Sandi
20290eb8392Sandi  $lockDir = $conf['lockdir'].'/'.md5($file);
20390eb8392Sandi  @rmdir($lockDir);
20490eb8392Sandi  @ignore_user_abort(0);
20590eb8392Sandi}
20690eb8392Sandi
20790eb8392Sandi/**
208f3f0262cSandi * Create the directory needed for the given file
20915fae107Sandi *
21015fae107Sandi * @author  Andreas Gohr <andi@splitbrain.org>
211f3f0262cSandi */
212f3f0262cSandifunction io_makeFileDir($file){
213f3f0262cSandi  global $conf;
214f3f0262cSandi
215f3f0262cSandi  $dir = dirname($file);
2160d8850c4SAndreas Gohr  if(!@is_dir($dir)){
217f3f0262cSandi    io_mkdir_p($dir) || msg("Creating directory $dir failed",-1);
218f3f0262cSandi  }
219f3f0262cSandi}
220f3f0262cSandi
221f3f0262cSandi/**
222f3f0262cSandi * Creates a directory hierachy.
223f3f0262cSandi *
22415fae107Sandi * @link    http://www.php.net/manual/en/function.mkdir.php
225f3f0262cSandi * @author  <saint@corenova.com>
2263dc3a5f1Sandi * @author  Andreas Gohr <andi@splitbrain.org>
227f3f0262cSandi */
228f3f0262cSandifunction io_mkdir_p($target){
2293dc3a5f1Sandi  global $conf;
2300d8850c4SAndreas Gohr  if (@is_dir($target)||empty($target)) return 1; // best case check first
231f3f0262cSandi  if (@file_exists($target) && !is_dir($target)) return 0;
2323dc3a5f1Sandi  //recursion
2333dc3a5f1Sandi  if (io_mkdir_p(substr($target,0,strrpos($target,'/')))){
2343dc3a5f1Sandi    if($conf['safemodehack']){
235034138e2SRainer Weinhold      $dir = preg_replace('/^'.preg_quote(realpath($conf['ftp']['root']),'/').'/','', $target);
236034138e2SRainer Weinhold      return io_mkdir_ftp($dir);
2373dc3a5f1Sandi    }else{
23844881d27STroels Liebe Bentsen      $ret = @mkdir($target,$conf['dmode']); // crawl back up & create dir tree
2391ca31cfeSAndreas Gohr      if($ret && $conf['dperm']) chmod($target, $conf['dperm']);
24044881d27STroels Liebe Bentsen      return $ret;
2413dc3a5f1Sandi    }
2423dc3a5f1Sandi  }
243f3f0262cSandi  return 0;
244f3f0262cSandi}
245f3f0262cSandi
246f3f0262cSandi/**
2473dc3a5f1Sandi * Creates a directory using FTP
2483dc3a5f1Sandi *
2493dc3a5f1Sandi * This is used when the safemode workaround is enabled
2503dc3a5f1Sandi *
2513dc3a5f1Sandi * @author <andi@splitbrain.org>
2523dc3a5f1Sandi */
2533dc3a5f1Sandifunction io_mkdir_ftp($dir){
2543dc3a5f1Sandi  global $conf;
2553dc3a5f1Sandi
2563dc3a5f1Sandi  if(!function_exists('ftp_connect')){
2573dc3a5f1Sandi    msg("FTP support not found - safemode workaround not usable",-1);
2583dc3a5f1Sandi    return false;
2593dc3a5f1Sandi  }
2603dc3a5f1Sandi
2613dc3a5f1Sandi  $conn = @ftp_connect($conf['ftp']['host'],$conf['ftp']['port'],10);
2623dc3a5f1Sandi  if(!$conn){
2633dc3a5f1Sandi    msg("FTP connection failed",-1);
2643dc3a5f1Sandi    return false;
2653dc3a5f1Sandi  }
2663dc3a5f1Sandi
2673dc3a5f1Sandi  if(!@ftp_login($conn, $conf['ftp']['user'], $conf['ftp']['pass'])){
2683dc3a5f1Sandi    msg("FTP login failed",-1);
2693dc3a5f1Sandi    return false;
2703dc3a5f1Sandi  }
2713dc3a5f1Sandi
2723dc3a5f1Sandi  //create directory
273034138e2SRainer Weinhold  $ok = @ftp_mkdir($conn, $dir);
2741ca31cfeSAndreas Gohr  //set permissions
2751ca31cfeSAndreas Gohr  @ftp_site($conn,sprintf("CHMOD %04o %s",$conf['dmode'],$dir));
2763dc3a5f1Sandi
277034138e2SRainer Weinhold  @ftp_close($conn);
2783dc3a5f1Sandi  return $ok;
2793dc3a5f1Sandi}
2803dc3a5f1Sandi
2813dc3a5f1Sandi/**
28273ccfcb9Schris * downloads a file from the net and saves it
28373ccfcb9Schris *
28473ccfcb9Schris * if $useAttachment is false,
28573ccfcb9Schris * - $file is the full filename to save the file, incl. path
28673ccfcb9Schris * - if successful will return true, false otherwise
28773ccfcb9Schris
28873ccfcb9Schris * if $useAttachment is true,
28973ccfcb9Schris * - $file is the directory where the file should be saved
29073ccfcb9Schris * - if successful will return the name used for the saved file, false otherwise
291b625487dSandi *
292b625487dSandi * @author Andreas Gohr <andi@splitbrain.org>
29373ccfcb9Schris * @author Chris Smith <chris@jalakai.co.uk>
294b625487dSandi */
29573ccfcb9Schrisfunction io_download($url,$file,$useAttachment=false,$defaultName=''){
296ac9115b0STroels Liebe Bentsen  global $conf;
2979b307a83SAndreas Gohr  $http = new DokuHTTPClient();
2989b307a83SAndreas Gohr  $http->max_bodysize = 2*1024*1024; //max. 2MB
2999b307a83SAndreas Gohr  $http->timeout = 25; //max. 25 sec
3009b307a83SAndreas Gohr
3019b307a83SAndreas Gohr  $data = $http->get($url);
3029b307a83SAndreas Gohr  if(!$data) return false;
3039b307a83SAndreas Gohr
30473ccfcb9Schris  if ($useAttachment) {
30573ccfcb9Schris    $name = '';
30673ccfcb9Schris      if (isset($http->resp_headers['content-disposition'])) {
30773ccfcb9Schris      $content_disposition = $http->resp_headers['content-disposition'];
308ce070a9fSchris      $match=array();
30973ccfcb9Schris      if (is_string($content_disposition) &&
310ce070a9fSchris          preg_match('/attachment;\s*filename\s*=\s*"([^"]*)"/i', $content_disposition, $match)) {
31173ccfcb9Schris
31273ccfcb9Schris          $name = basename($match[1]);
31373ccfcb9Schris      }
31473ccfcb9Schris
31573ccfcb9Schris    }
31673ccfcb9Schris
31773ccfcb9Schris    if (!$name) {
31873ccfcb9Schris        if (!$defaultName) return false;
31973ccfcb9Schris        $name = $defaultName;
32073ccfcb9Schris    }
32173ccfcb9Schris
32273ccfcb9Schris    $file = $file.$name;
32373ccfcb9Schris  }
32473ccfcb9Schris
325ac9115b0STroels Liebe Bentsen  $fileexists = file_exists($file);
3269b307a83SAndreas Gohr  $fp = @fopen($file,"w");
327b625487dSandi  if(!$fp) return false;
3289b307a83SAndreas Gohr  fwrite($fp,$data);
329b625487dSandi  fclose($fp);
3301ca31cfeSAndreas Gohr  if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
33173ccfcb9Schris  if ($useAttachment) return $name;
332b625487dSandi  return true;
333b625487dSandi}
334b625487dSandi
335b625487dSandi/**
336ac9115b0STroels Liebe Bentsen * Windows compatible rename
337bf5e5a5bSAndreas Gohr *
338bf5e5a5bSAndreas Gohr * rename() can not overwrite existing files on Windows
339bf5e5a5bSAndreas Gohr * this function will use copy/unlink instead
340bf5e5a5bSAndreas Gohr */
341bf5e5a5bSAndreas Gohrfunction io_rename($from,$to){
342ac9115b0STroels Liebe Bentsen  global $conf;
343bf5e5a5bSAndreas Gohr  if(!@rename($from,$to)){
344bf5e5a5bSAndreas Gohr    if(@copy($from,$to)){
3451ca31cfeSAndreas Gohr      if($conf['fperm']) chmod($file, $conf['fperm']);
346bf5e5a5bSAndreas Gohr      @unlink($from);
347bf5e5a5bSAndreas Gohr      return true;
348bf5e5a5bSAndreas Gohr    }
349bf5e5a5bSAndreas Gohr    return false;
350bf5e5a5bSAndreas Gohr  }
351bf5e5a5bSAndreas Gohr  return true;
352bf5e5a5bSAndreas Gohr}
353bf5e5a5bSAndreas Gohr
354bf5e5a5bSAndreas Gohr
355bf5e5a5bSAndreas Gohr/**
356f3f0262cSandi * Runs an external command and returns it's output as string
35715fae107Sandi *
35815fae107Sandi * @author Harry Brueckner <harry_b@eml.cc>
35915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
3603dc3a5f1Sandi * @deprecated
361f3f0262cSandi */
362f3f0262cSandifunction io_runcmd($cmd){
363f3f0262cSandi  $fh = popen($cmd, "r");
364f3f0262cSandi  if(!$fh) return false;
365f3f0262cSandi  $ret = '';
366f3f0262cSandi  while (!feof($fh)) {
367f3f0262cSandi    $ret .= fread($fh, 8192);
368f3f0262cSandi  }
369f3f0262cSandi  pclose($fh);
370f3f0262cSandi  return $ret;
371f3f0262cSandi}
372f3f0262cSandi
3737421c3ccSAndreas Gohr/**
3747421c3ccSAndreas Gohr * Search a file for matching lines
3757421c3ccSAndreas Gohr *
3767421c3ccSAndreas Gohr * This is probably not faster than file()+preg_grep() but less
3777421c3ccSAndreas Gohr * memory intensive because not the whole file needs to be loaded
3787421c3ccSAndreas Gohr * at once.
3797421c3ccSAndreas Gohr *
3807421c3ccSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
3817421c3ccSAndreas Gohr * @param  string $file    The file to search
3827421c3ccSAndreas Gohr * @param  string $pattern PCRE pattern
3837421c3ccSAndreas Gohr * @param  int    $max     How many lines to return (0 for all)
3847421c3ccSAndreas Gohr * @param  bool   $baxkref When true returns array with backreferences instead of lines
3857421c3ccSAndreas Gohr * @return matching lines or backref, false on error
3867421c3ccSAndreas Gohr */
3877421c3ccSAndreas Gohrfunction io_grep($file,$pattern,$max=0,$backref=false){
3887421c3ccSAndreas Gohr  $fh = @fopen($file,'r');
3897421c3ccSAndreas Gohr  if(!$fh) return false;
3907421c3ccSAndreas Gohr  $matches = array();
3917421c3ccSAndreas Gohr
3927421c3ccSAndreas Gohr  $cnt  = 0;
3937421c3ccSAndreas Gohr  $line = '';
3947421c3ccSAndreas Gohr  while (!feof($fh)) {
3957421c3ccSAndreas Gohr    $line .= fgets($fh, 4096);  // read full line
3967421c3ccSAndreas Gohr    if(substr($line,-1) != "\n") continue;
3977421c3ccSAndreas Gohr
3987421c3ccSAndreas Gohr    // check if line matches
3997421c3ccSAndreas Gohr    if(preg_match($pattern,$line,$match)){
4007421c3ccSAndreas Gohr      if($backref){
4017421c3ccSAndreas Gohr        $matches[] = $match;
4027421c3ccSAndreas Gohr      }else{
4037421c3ccSAndreas Gohr        $matches[] = $line;
4047421c3ccSAndreas Gohr      }
4057421c3ccSAndreas Gohr      $cnt++;
4067421c3ccSAndreas Gohr    }
4077421c3ccSAndreas Gohr    if($max && $max == $cnt) break;
4087421c3ccSAndreas Gohr    $line = '';
4097421c3ccSAndreas Gohr  }
4107421c3ccSAndreas Gohr  fclose($fh);
4117421c3ccSAndreas Gohr  return $matches;
4127421c3ccSAndreas Gohr}
4137421c3ccSAndreas Gohr
414340756e4Sandi//Setup VIM: ex: et ts=2 enc=utf-8 :
415