xref: /dokuwiki/inc/io.php (revision 685bdd2e6c508c47a360b2dbf1beea318f7f99cd)
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){
185      if($conf['dperm']) chmod($lockDir, $conf['dperm']);
186      break;
187    }
188    usleep(50);
189  } while ($locked === false);
190}
191
192/**
193 * Unlocks a file
194 *
195 * @author Andreas Gohr <andi@splitbrain.org>
196 */
197function io_unlock($file){
198  global $conf;
199  // no locking if safemode hack
200  if($conf['safemodehack']) return;
201
202  $lockDir = $conf['lockdir'].'/'.md5($file);
203  @rmdir($lockDir);
204  @ignore_user_abort(0);
205}
206
207/**
208 * Create the directory needed for the given file
209 *
210 * @author  Andreas Gohr <andi@splitbrain.org>
211 */
212function io_makeFileDir($file){
213  global $conf;
214
215  $dir = dirname($file);
216  if(!@is_dir($dir)){
217    io_mkdir_p($dir) || msg("Creating directory $dir failed",-1);
218  }
219}
220
221/**
222 * Creates a directory hierachy.
223 *
224 * @link    http://www.php.net/manual/en/function.mkdir.php
225 * @author  <saint@corenova.com>
226 * @author  Andreas Gohr <andi@splitbrain.org>
227 */
228function io_mkdir_p($target){
229  global $conf;
230  if (@is_dir($target)||empty($target)) return 1; // best case check first
231  if (@file_exists($target) && !is_dir($target)) return 0;
232  //recursion
233  if (io_mkdir_p(substr($target,0,strrpos($target,'/')))){
234    if($conf['safemodehack']){
235      $dir = preg_replace('/^'.preg_quote(realpath($conf['ftp']['root']),'/').'/','', $target);
236      return io_mkdir_ftp($dir);
237    }else{
238      $ret = @mkdir($target,$conf['dmode']); // crawl back up & create dir tree
239      if($ret && $conf['dperm']) chmod($target, $conf['dperm']);
240      return $ret;
241    }
242  }
243  return 0;
244}
245
246/**
247 * Creates a directory using FTP
248 *
249 * This is used when the safemode workaround is enabled
250 *
251 * @author <andi@splitbrain.org>
252 */
253function io_mkdir_ftp($dir){
254  global $conf;
255
256  if(!function_exists('ftp_connect')){
257    msg("FTP support not found - safemode workaround not usable",-1);
258    return false;
259  }
260
261  $conn = @ftp_connect($conf['ftp']['host'],$conf['ftp']['port'],10);
262  if(!$conn){
263    msg("FTP connection failed",-1);
264    return false;
265  }
266
267  if(!@ftp_login($conn, $conf['ftp']['user'], $conf['ftp']['pass'])){
268    msg("FTP login failed",-1);
269    return false;
270  }
271
272  //create directory
273  $ok = @ftp_mkdir($conn, $dir);
274  //set permissions
275  @ftp_site($conn,sprintf("CHMOD %04o %s",$conf['dmode'],$dir));
276
277  @ftp_close($conn);
278  return $ok;
279}
280
281/**
282 * downloads a file from the net and saves it
283 *
284 * if $useAttachment is false,
285 * - $file is the full filename to save the file, incl. path
286 * - if successful will return true, false otherwise
287
288 * if $useAttachment is true,
289 * - $file is the directory where the file should be saved
290 * - if successful will return the name used for the saved file, false otherwise
291 *
292 * @author Andreas Gohr <andi@splitbrain.org>
293 * @author Chris Smith <chris@jalakai.co.uk>
294 */
295function io_download($url,$file,$useAttachment=false,$defaultName=''){
296  global $conf;
297  $http = new DokuHTTPClient();
298  $http->max_bodysize = 2*1024*1024; //max. 2MB
299  $http->timeout = 25; //max. 25 sec
300
301  $data = $http->get($url);
302  if(!$data) return false;
303
304  if ($useAttachment) {
305    $name = '';
306      if (isset($http->resp_headers['content-disposition'])) {
307      $content_disposition = $http->resp_headers['content-disposition'];
308      $match=array();
309      if (is_string($content_disposition) &&
310          preg_match('/attachment;\s*filename\s*=\s*"([^"]*)"/i', $content_disposition, $match)) {
311
312          $name = basename($match[1]);
313      }
314
315    }
316
317    if (!$name) {
318        if (!$defaultName) return false;
319        $name = $defaultName;
320    }
321
322    $file = $file.$name;
323  }
324
325  $fileexists = file_exists($file);
326  $fp = @fopen($file,"w");
327  if(!$fp) return false;
328  fwrite($fp,$data);
329  fclose($fp);
330  if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
331  if ($useAttachment) return $name;
332  return true;
333}
334
335/**
336 * Windows compatible rename
337 *
338 * rename() can not overwrite existing files on Windows
339 * this function will use copy/unlink instead
340 */
341function io_rename($from,$to){
342  global $conf;
343  if(!@rename($from,$to)){
344    if(@copy($from,$to)){
345      if($conf['fperm']) chmod($file, $conf['fperm']);
346      @unlink($from);
347      return true;
348    }
349    return false;
350  }
351  return true;
352}
353
354
355/**
356 * Runs an external command and returns it's output as string
357 *
358 * @author Harry Brueckner <harry_b@eml.cc>
359 * @author Andreas Gohr <andi@splitbrain.org>
360 * @deprecated
361 */
362function io_runcmd($cmd){
363  $fh = popen($cmd, "r");
364  if(!$fh) return false;
365  $ret = '';
366  while (!feof($fh)) {
367    $ret .= fread($fh, 8192);
368  }
369  pclose($fh);
370  return $ret;
371}
372
373/**
374 * Search a file for matching lines
375 *
376 * This is probably not faster than file()+preg_grep() but less
377 * memory intensive because not the whole file needs to be loaded
378 * at once.
379 *
380 * @author Andreas Gohr <andi@splitbrain.org>
381 * @param  string $file    The file to search
382 * @param  string $pattern PCRE pattern
383 * @param  int    $max     How many lines to return (0 for all)
384 * @param  bool   $baxkref When true returns array with backreferences instead of lines
385 * @return matching lines or backref, false on error
386 */
387function io_grep($file,$pattern,$max=0,$backref=false){
388  $fh = @fopen($file,'r');
389  if(!$fh) return false;
390  $matches = array();
391
392  $cnt  = 0;
393  $line = '';
394  while (!feof($fh)) {
395    $line .= fgets($fh, 4096);  // read full line
396    if(substr($line,-1) != "\n") continue;
397
398    // check if line matches
399    if(preg_match($pattern,$line,$match)){
400      if($backref){
401        $matches[] = $match;
402      }else{
403        $matches[] = $line;
404      }
405      $cnt++;
406    }
407    if($max && $max == $cnt) break;
408    $line = '';
409  }
410  fclose($fh);
411  return $matches;
412}
413
414//Setup VIM: ex: et ts=2 enc=utf-8 :
415