Completed
Push — sidebaracl ( 7a112d...7c3e4a )
by Andreas
04:38
created

inc/io.php (5 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

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
The property max_bodysize 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...
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