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