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