Failed Conditions
Push — psr2 ( 3409ba...d44376 )
by Andreas
06:40
created

io.php ➔ io_lock()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 4
nop 1
dl 0
loc 18
rs 9.3554
c 0
b 0
f 0
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
/**
10
 * Removes empty directories
11
 *
12
 * Sends IO_NAMESPACE_DELETED events for 'pages' and 'media' namespaces.
13
 * Event data:
14
 * $data[0]    ns: The colon separated namespace path minus the trailing page name.
15
 * $data[1]    ns_type: 'pages' or 'media' namespace tree.
16
 *
17
 * @param string $id      - a pageid, the namespace of that id will be tried to deleted
18
 * @param string $basedir - the config name of the type to delete (datadir or mediadir usally)
19
 * @return bool - true if at least one namespace was deleted
20
 *
21
 * @author  Andreas Gohr <[email protected]>
22
 * @author Ben Coburn <[email protected]>
23
 */
24
function io_sweepNS($id,$basedir='datadir'){
25
    global $conf;
26
    $types = array ('datadir'=>'pages', 'mediadir'=>'media');
27
    $ns_type = (isset($types[$basedir])?$types[$basedir]:false);
28
29
    $delone = false;
30
31
    //scan all namespaces
32
    while(($id = getNS($id)) !== false){
33
        $dir = $conf[$basedir].'/'.utf8_encodeFN(str_replace(':','/',$id));
34
35
        //try to delete dir else return
36
        if(@rmdir($dir)) {
37
            if ($ns_type!==false) {
38
                $data = array($id, $ns_type);
39
                $delone = true; // we deleted at least one dir
40
                trigger_event('IO_NAMESPACE_DELETED', $data);
41
            }
42
        } else { return $delone; }
43
    }
44
    return $delone;
45
}
46
47
/**
48
 * Used to read in a DokuWiki page from file, and send IO_WIKIPAGE_READ events.
49
 *
50
 * Generates the action event which delegates to io_readFile().
51
 * Action plugins are allowed to modify the page content in transit.
52
 * The file path should not be changed.
53
 *
54
 * Event data:
55
 * $data[0]    The raw arguments for io_readFile as an array.
56
 * $data[1]    ns: The colon separated namespace path minus the trailing page name. (false if root ns)
57
 * $data[2]    page_name: The wiki page name.
58
 * $data[3]    rev: The page revision, false for current wiki pages.
59
 *
60
 * @author Ben Coburn <[email protected]>
61
 *
62
 * @param string   $file filename
63
 * @param string   $id page id
64
 * @param bool|int $rev revision timestamp
65
 * @return string
66
 */
67
function io_readWikiPage($file, $id, $rev=false) {
68
    if (empty($rev)) { $rev = false; }
69
    $data = array(array($file, true), getNS($id), noNS($id), $rev);
70
    return trigger_event('IO_WIKIPAGE_READ', $data, '_io_readWikiPage_action', false);
71
}
72
73
/**
74
 * Callback adapter for io_readFile().
75
 *
76
 * @author Ben Coburn <[email protected]>
77
 *
78
 * @param array $data event data
79
 * @return string
80
 */
81
function _io_readWikiPage_action($data) {
82
    if (is_array($data) && is_array($data[0]) && count($data[0])===2) {
83
        return call_user_func_array('io_readFile', $data[0]);
84
    } else {
85
        return ''; //callback error
86
    }
87
}
88
89
/**
90
 * Returns content of $file as cleaned string.
91
 *
92
 * Uses gzip if extension is .gz
93
 *
94
 * If you want to use the returned value in unserialize
95
 * be sure to set $clean to false!
96
 *
97
 * @author  Andreas Gohr <[email protected]>
98
 *
99
 * @param string $file  filename
100
 * @param bool   $clean
101
 * @return string|bool the file contents or false on error
102
 */
103
function io_readFile($file,$clean=true){
104
    $ret = '';
105
    if(file_exists($file)){
106
        if(substr($file,-3) == '.gz'){
107
            if(!DOKU_HAS_GZIP) return false;
108
            $ret = gzfile($file);
109
            if(is_array($ret)) $ret = join('', $ret);
110
        }else if(substr($file,-4) == '.bz2'){
111
            if(!DOKU_HAS_BZIP) return false;
112
            $ret = bzfile($file);
113
        }else{
114
            $ret = file_get_contents($file);
115
        }
116
    }
117
    if($ret === null) return false;
118
    if($ret !== false && $clean){
119
        return cleanText($ret);
120
    }else{
121
        return $ret;
122
    }
123
}
124
/**
125
 * Returns the content of a .bz2 compressed file as string
126
 *
127
 * @author marcel senf <[email protected]>
128
 * @author  Andreas Gohr <[email protected]>
129
 *
130
 * @param string $file filename
131
 * @param bool   $array return array of lines
132
 * @return string|array|bool content or false on error
133
 */
134
function bzfile($file, $array=false) {
135
    $bz = bzopen($file,"r");
136
    if($bz === false) return false;
137
138
    if($array) $lines = array();
139
    $str = '';
140
    while (!feof($bz)) {
141
        //8192 seems to be the maximum buffersize?
142
        $buffer = bzread($bz,8192);
143
        if(($buffer === false) || (bzerrno($bz) !== 0)) {
144
            return false;
145
        }
146
        $str = $str . $buffer;
147
        if($array) {
148
            $pos = strpos($str, "\n");
149
            while($pos !== false) {
150
                $lines[] = substr($str, 0, $pos+1);
0 ignored issues
show
Bug introduced by
The variable $lines does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
151
                $str = substr($str, $pos+1);
152
                $pos = strpos($str, "\n");
153
            }
154
        }
155
    }
156
    bzclose($bz);
157
    if($array) {
158
        if($str !== '') $lines[] = $str;
159
        return $lines;
160
    }
161
    return $str;
162
}
163
164
/**
165
 * Used to write out a DokuWiki page to file, and send IO_WIKIPAGE_WRITE events.
166
 *
167
 * This generates an action event and delegates to io_saveFile().
168
 * Action plugins are allowed to modify the page content in transit.
169
 * The file path should not be changed.
170
 * (The append parameter is set to false.)
171
 *
172
 * Event data:
173
 * $data[0]    The raw arguments for io_saveFile as an array.
174
 * $data[1]    ns: The colon separated namespace path minus the trailing page name. (false if root ns)
175
 * $data[2]    page_name: The wiki page name.
176
 * $data[3]    rev: The page revision, false for current wiki pages.
177
 *
178
 * @author Ben Coburn <[email protected]>
179
 *
180
 * @param string $file      filename
181
 * @param string $content
182
 * @param string $id        page id
183
 * @param int|bool $rev timestamp of revision
184
 * @return bool
185
 */
186
function io_writeWikiPage($file, $content, $id, $rev=false) {
187
    if (empty($rev)) { $rev = false; }
188
    if ($rev===false) { io_createNamespace($id); } // create namespaces as needed
189
    $data = array(array($file, $content, false), getNS($id), noNS($id), $rev);
190
    return trigger_event('IO_WIKIPAGE_WRITE', $data, '_io_writeWikiPage_action', false);
191
}
192
193
/**
194
 * Callback adapter for io_saveFile().
195
 * @author Ben Coburn <[email protected]>
196
 *
197
 * @param array $data event data
198
 * @return bool
199
 */
200
function _io_writeWikiPage_action($data) {
201
    if (is_array($data) && is_array($data[0]) && count($data[0])===3) {
202
        $ok = call_user_func_array('io_saveFile', $data[0]);
203
        // for attic files make sure the file has the mtime of the revision
204
        if($ok && is_int($data[3]) && $data[3] > 0) {
205
            @touch($data[0][0], $data[3]);
206
        }
207
        return $ok;
208
    } else {
209
        return false; //callback error
210
    }
211
}
212
213
/**
214
 * Internal function to save contents to a file.
215
 *
216
 * @author  Andreas Gohr <[email protected]>
217
 *
218
 * @param string $file filename path to file
219
 * @param string $content
220
 * @param bool   $append
221
 * @return bool true on success, otherwise false
222
 */
223
function _io_saveFile($file, $content, $append) {
224
    global $conf;
225
    $mode = ($append) ? 'ab' : 'wb';
226
    $fileexists = file_exists($file);
227
228
    if(substr($file,-3) == '.gz'){
229
        if(!DOKU_HAS_GZIP) return false;
230
        $fh = @gzopen($file,$mode.'9');
231
        if(!$fh) return false;
232
        gzwrite($fh, $content);
233
        gzclose($fh);
234
    }else if(substr($file,-4) == '.bz2'){
235
        if(!DOKU_HAS_BZIP) return false;
236
        if($append) {
237
            $bzcontent = bzfile($file);
238
            if($bzcontent === false) return false;
239
            $content = $bzcontent.$content;
240
        }
241
        $fh = @bzopen($file,'w');
242
        if(!$fh) return false;
243
        bzwrite($fh, $content);
244
        bzclose($fh);
245
    }else{
246
        $fh = @fopen($file,$mode);
247
        if(!$fh) return false;
248
        fwrite($fh, $content);
249
        fclose($fh);
250
    }
251
252
    if(!$fileexists and !empty($conf['fperm'])) chmod($file, $conf['fperm']);
253
    return true;
254
}
255
256
/**
257
 * Saves $content to $file.
258
 *
259
 * If the third parameter is set to true the given content
260
 * will be appended.
261
 *
262
 * Uses gzip if extension is .gz
263
 * and bz2 if extension is .bz2
264
 *
265
 * @author  Andreas Gohr <[email protected]>
266
 *
267
 * @param string $file filename path to file
268
 * @param string $content
269
 * @param bool   $append
270
 * @return bool true on success, otherwise false
271
 */
272
function io_saveFile($file, $content, $append=false) {
273
    io_makeFileDir($file);
274
    io_lock($file);
275
    if(!_io_saveFile($file, $content, $append)) {
276
        msg("Writing $file failed",-1);
277
        io_unlock($file);
278
        return false;
279
    }
280
    io_unlock($file);
281
    return true;
282
}
283
284
/**
285
 * Replace one or more occurrences of a line in a file.
286
 *
287
 * The default, when $maxlines is 0 is to delete all matching lines then append a single line.
288
 * A regex that matches any part of the line will remove the entire line in this mode.
289
 * Captures in $newline are not available.
290
 *
291
 * Otherwise each line is matched and replaced individually, up to the first $maxlines lines
292
 * or all lines if $maxlines is -1. If $regex is true then captures can be used in $newline.
293
 *
294
 * Be sure to include the trailing newline in $oldline when replacing entire lines.
295
 *
296
 * Uses gzip if extension is .gz
297
 * and bz2 if extension is .bz2
298
 *
299
 * @author Steven Danz <[email protected]>
300
 * @author Christopher Smith <[email protected]>
301
 * @author Patrick Brown <[email protected]>
302
 *
303
 * @param string $file     filename
304
 * @param string $oldline  exact linematch to remove
305
 * @param string $newline  new line to insert
306
 * @param bool   $regex    use regexp?
307
 * @param int    $maxlines number of occurrences of the line to replace
308
 * @return bool true on success
309
 */
310
function io_replaceInFile($file, $oldline, $newline, $regex=false, $maxlines=0) {
311
    if ((string)$oldline === '') {
312
        trigger_error('$oldline parameter cannot be empty in io_replaceInFile()', E_USER_WARNING);
313
        return false;
314
    }
315
316
    if (!file_exists($file)) return true;
317
318
    io_lock($file);
319
320
    // load into array
321
    if(substr($file,-3) == '.gz'){
322
        if(!DOKU_HAS_GZIP) return false;
323
        $lines = gzfile($file);
324
    }else if(substr($file,-4) == '.bz2'){
325
        if(!DOKU_HAS_BZIP) return false;
326
        $lines = bzfile($file, true);
327
    }else{
328
        $lines = file($file);
329
    }
330
331
    // make non-regexes into regexes
332
    $pattern = $regex ? $oldline : '/^'.preg_quote($oldline,'/').'$/';
333
    $replace = $regex ? $newline : addcslashes($newline, '\$');
334
335
    // remove matching lines
336
    if ($maxlines > 0) {
337
        $count = 0;
338
        $matched = 0;
339
        foreach($lines as $i => $line) {
0 ignored issues
show
Bug introduced by
The expression $lines of type false|array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
340
            if($count >= $maxlines) break;
341
            // $matched will be set to 0|1 depending on whether pattern is matched and line replaced
342
            $lines[$i] = preg_replace($pattern, $replace, $line, -1, $matched);
343
            if ($matched) $count++;
0 ignored issues
show
Bug Best Practice introduced by
The expression $matched of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
344
        }
345
    } else if ($maxlines == 0) {
346
        $lines = preg_grep($pattern, $lines, PREG_GREP_INVERT);
347
348
        if ((string)$newline !== ''){
349
            $lines[] = $newline;
350
        }
351
    } else {
352
        $lines = preg_replace($pattern, $replace, $lines);
353
    }
354
355
    if(count($lines)){
356
        if(!_io_saveFile($file, join('',$lines), false)) {
357
            msg("Removing content from $file failed",-1);
358
            io_unlock($file);
359
            return false;
360
        }
361
    }else{
362
        @unlink($file);
363
    }
364
365
    io_unlock($file);
366
    return true;
367
}
368
369
/**
370
 * Delete lines that match $badline from $file.
371
 *
372
 * Be sure to include the trailing newline in $badline
373
 *
374
 * @author Patrick Brown <[email protected]>
375
 *
376
 * @param string $file    filename
377
 * @param string $badline exact linematch to remove
378
 * @param bool   $regex   use regexp?
379
 * @return bool true on success
380
 */
381
function io_deleteFromFile($file,$badline,$regex=false){
382
    return io_replaceInFile($file,$badline,null,$regex,0);
383
}
384
385
/**
386
 * Tries to lock a file
387
 *
388
 * Locking is only done for io_savefile and uses directories
389
 * inside $conf['lockdir']
390
 *
391
 * It waits maximal 3 seconds for the lock, after this time
392
 * the lock is assumed to be stale and the function goes on
393
 *
394
 * @author Andreas Gohr <[email protected]>
395
 *
396
 * @param string $file filename
397
 */
398
function io_lock($file){
399
    global $conf;
400
401
    $lockDir = $conf['lockdir'].'/'.md5($file);
402
    @ignore_user_abort(1);
403
404
    $timeStart = time();
405
    do {
406
        //waited longer than 3 seconds? -> stale lock
407
        if ((time() - $timeStart) > 3) break;
408
        $locked = @mkdir($lockDir, $conf['dmode']);
409
        if($locked){
410
            if(!empty($conf['dperm'])) chmod($lockDir, $conf['dperm']);
411
            break;
412
        }
413
        usleep(50);
414
    } while ($locked === false);
415
}
416
417
/**
418
 * Unlocks a file
419
 *
420
 * @author Andreas Gohr <[email protected]>
421
 *
422
 * @param string $file filename
423
 */
424
function io_unlock($file){
425
    global $conf;
426
427
    $lockDir = $conf['lockdir'].'/'.md5($file);
428
    @rmdir($lockDir);
429
    @ignore_user_abort(0);
430
}
431
432
/**
433
 * Create missing namespace directories and send the IO_NAMESPACE_CREATED events
434
 * in the order of directory creation. (Parent directories first.)
435
 *
436
 * Event data:
437
 * $data[0]    ns: The colon separated namespace path minus the trailing page name.
438
 * $data[1]    ns_type: 'pages' or 'media' namespace tree.
439
 *
440
 * @author Ben Coburn <[email protected]>
441
 *
442
 * @param string $id page id
443
 * @param string $ns_type 'pages' or 'media'
444
 */
445
function io_createNamespace($id, $ns_type='pages') {
446
    // verify ns_type
447
    $types = array('pages'=>'wikiFN', 'media'=>'mediaFN');
448
    if (!isset($types[$ns_type])) {
449
        trigger_error('Bad $ns_type parameter for io_createNamespace().');
450
        return;
451
    }
452
    // make event list
453
    $missing = array();
454
    $ns_stack = explode(':', $id);
455
    $ns = $id;
456
    $tmp = dirname( $file = call_user_func($types[$ns_type], $ns) );
457
    while (!@is_dir($tmp) && !(file_exists($tmp) && !is_dir($tmp))) {
458
        array_pop($ns_stack);
459
        $ns = implode(':', $ns_stack);
460
        if (strlen($ns)==0) { break; }
461
        $missing[] = $ns;
462
        $tmp = dirname(call_user_func($types[$ns_type], $ns));
463
    }
464
    // make directories
465
    io_makeFileDir($file);
466
    // send the events
467
    $missing = array_reverse($missing); // inside out
468
    foreach ($missing as $ns) {
469
        $data = array($ns, $ns_type);
470
        trigger_event('IO_NAMESPACE_CREATED', $data);
471
    }
472
}
473
474
/**
475
 * Create the directory needed for the given file
476
 *
477
 * @author  Andreas Gohr <[email protected]>
478
 *
479
 * @param string $file file name
480
 */
481
function io_makeFileDir($file){
482
    $dir = dirname($file);
483
    if(!@is_dir($dir)){
484
        io_mkdir_p($dir) || msg("Creating directory $dir failed",-1);
485
    }
486
}
487
488
/**
489
 * Creates a directory hierachy.
490
 *
491
 * @link    http://php.net/manual/en/function.mkdir.php
492
 * @author  <[email protected]>
493
 * @author  Andreas Gohr <[email protected]>
494
 *
495
 * @param string $target filename
496
 * @return bool|int|string
497
 */
498
function io_mkdir_p($target){
499
    global $conf;
500
    if (@is_dir($target)||empty($target)) return 1; // best case check first
501
    if (file_exists($target) && !is_dir($target)) return 0;
502
    //recursion
503
    if (io_mkdir_p(substr($target,0,strrpos($target,'/')))){
504
        $ret = @mkdir($target,$conf['dmode']); // crawl back up & create dir tree
505
        if($ret && !empty($conf['dperm'])) chmod($target, $conf['dperm']);
506
        return $ret;
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 unique temporary directory and returns
563
 * its path.
564
 *
565
 * @author Michael Klier <[email protected]>
566
 *
567
 * @return false|string path to new directory or false
568
 */
569
function io_mktmpdir() {
570
    global $conf;
571
572
    $base = $conf['tmpdir'];
573
    $dir  = md5(uniqid(mt_rand(), true));
574
    $tmpdir = $base.'/'.$dir;
575
576
    if(io_mkdir_p($tmpdir)) {
577
        return($tmpdir);
578
    } else {
579
        return false;
580
    }
581
}
582
583
/**
584
 * downloads a file from the net and saves it
585
 *
586
 * if $useAttachment is false,
587
 * - $file is the full filename to save the file, incl. path
588
 * - if successful will return true, false otherwise
589
 *
590
 * if $useAttachment is true,
591
 * - $file is the directory where the file should be saved
592
 * - if successful will return the name used for the saved file, false otherwise
593
 *
594
 * @author Andreas Gohr <[email protected]>
595
 * @author Chris Smith <[email protected]>
596
 *
597
 * @param string $url           url to download
598
 * @param string $file          path to file or directory where to save
599
 * @param bool   $useAttachment true: try to use name of download, uses otherwise $defaultName
600
 *                              false: uses $file as path to file
601
 * @param string $defaultName   fallback for if using $useAttachment
602
 * @param int    $maxSize       maximum file size
603
 * @return bool|string          if failed false, otherwise true or the name of the file in the given dir
604
 */
605
function io_download($url,$file,$useAttachment=false,$defaultName='',$maxSize=2097152){
606
    global $conf;
607
    $http = new DokuHTTPClient();
608
    $http->max_bodysize = $maxSize;
609
    $http->timeout = 25; //max. 25 sec
610
    $http->keep_alive = false; // we do single ops here, no need for keep-alive
611
612
    $data = $http->get($url);
613
    if(!$data) return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression $data of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
614
615
    $name = '';
616
    if ($useAttachment) {
617
        if (isset($http->resp_headers['content-disposition'])) {
618
            $content_disposition = $http->resp_headers['content-disposition'];
619
            $match=array();
620
            if (is_string($content_disposition) &&
621
                    preg_match('/attachment;\s*filename\s*=\s*"([^"]*)"/i', $content_disposition, $match)) {
622
623
                $name = utf8_basename($match[1]);
624
            }
625
626
        }
627
628
        if (!$name) {
629
            if (!$defaultName) return false;
630
            $name = $defaultName;
631
        }
632
633
        $file = $file.$name;
634
    }
635
636
    $fileexists = file_exists($file);
637
    $fp = @fopen($file,"w");
638
    if(!$fp) return false;
639
    fwrite($fp,$data);
640
    fclose($fp);
641
    if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
642
    if ($useAttachment) return $name;
643
    return true;
644
}
645
646
/**
647
 * Windows compatible rename
648
 *
649
 * rename() can not overwrite existing files on Windows
650
 * this function will use copy/unlink instead
651
 *
652
 * @param string $from
653
 * @param string $to
654
 * @return bool succes or fail
655
 */
656
function io_rename($from,$to){
657
    global $conf;
658
    if(!@rename($from,$to)){
659
        if(@copy($from,$to)){
660
            if($conf['fperm']) chmod($to, $conf['fperm']);
661
            @unlink($from);
662
            return true;
663
        }
664
        return false;
665
    }
666
    return true;
667
}
668
669
/**
670
 * Runs an external command with input and output pipes.
671
 * Returns the exit code from the process.
672
 *
673
 * @author Tom N Harris <[email protected]>
674
 *
675
 * @param string $cmd
676
 * @param string $input  input pipe
677
 * @param string $output output pipe
678
 * @return int exit code from process
679
 */
680
function io_exec($cmd, $input, &$output){
681
    $descspec = array(
682
            0=>array("pipe","r"),
683
            1=>array("pipe","w"),
684
            2=>array("pipe","w"));
685
    $ph = proc_open($cmd, $descspec, $pipes);
686
    if(!$ph) return -1;
687
    fclose($pipes[2]); // ignore stderr
688
    fwrite($pipes[0], $input);
689
    fclose($pipes[0]);
690
    $output = stream_get_contents($pipes[1]);
691
    fclose($pipes[1]);
692
    return proc_close($ph);
693
}
694
695
/**
696
 * Search a file for matching lines
697
 *
698
 * This is probably not faster than file()+preg_grep() but less
699
 * memory intensive because not the whole file needs to be loaded
700
 * at once.
701
 *
702
 * @author Andreas Gohr <[email protected]>
703
 * @param  string $file    The file to search
704
 * @param  string $pattern PCRE pattern
705
 * @param  int    $max     How many lines to return (0 for all)
706
 * @param  bool   $backref When true returns array with backreferences instead of lines
707
 * @return array matching lines or backref, false on error
708
 */
709
function io_grep($file,$pattern,$max=0,$backref=false){
710
    $fh = @fopen($file,'r');
711
    if(!$fh) return false;
712
    $matches = array();
713
714
    $cnt  = 0;
715
    $line = '';
716
    while (!feof($fh)) {
717
        $line .= fgets($fh, 4096);  // read full line
718
        if(substr($line,-1) != "\n") continue;
719
720
        // check if line matches
721
        if(preg_match($pattern,$line,$match)){
722
            if($backref){
723
                $matches[] = $match;
724
            }else{
725
                $matches[] = $line;
726
            }
727
            $cnt++;
728
        }
729
        if($max && $max == $cnt) break;
730
        $line = '';
731
    }
732
    fclose($fh);
733
    return $matches;
734
}
735
736
737
/**
738
 * Get size of contents of a file, for a compressed file the uncompressed size
739
 * Warning: reading uncompressed size of content of bz-files requires uncompressing
740
 *
741
 * @author  Gerrit Uitslag <[email protected]>
742
 *
743
 * @param string $file filename path to file
744
 * @return int size of file
745
 */
746
function io_getSizeFile($file) {
747
    if (!file_exists($file)) return 0;
748
749
    if(substr($file,-3) == '.gz'){
750
        $fp = @fopen($file, "rb");
751
        if($fp === false) return 0;
752
753
        fseek($fp, -4, SEEK_END);
754
        $buffer = fread($fp, 4);
755
        fclose($fp);
756
        $array = unpack("V", $buffer);
757
        $uncompressedsize = end($array);
758
    }else if(substr($file,-4) == '.bz2'){
759
        if(!DOKU_HAS_BZIP) return 0;
760
761
        $bz = bzopen($file,"r");
762
        if($bz === false) return 0;
763
764
        $uncompressedsize = 0;
765
        while (!feof($bz)) {
766
            //8192 seems to be the maximum buffersize?
767
            $buffer = bzread($bz,8192);
768
            if(($buffer === false) || (bzerrno($bz) !== 0)) {
769
                return 0;
770
            }
771
            $uncompressedsize += strlen($buffer);
772
        }
773
    }else{
774
        $uncompressedsize = filesize($file);
775
    }
776
777
    return $uncompressedsize;
778
 }
779