Failed Conditions
Push — master ( cbaf27...ca549e )
by Andreas
08:53 queued 04:43
created

io.php ➔ io_replaceInFile()   D

Complexity

Conditions 17
Paths 184

Size

Total Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
nc 184
nop 5
dl 0
loc 58
rs 4.5166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
            if(!DOKU_HAS_GZIP) return false;
111
            $ret = gzfile($file);
112
            if(is_array($ret)) $ret = join('', $ret);
113
        }else if(substr($file,-4) == '.bz2'){
114
            if(!DOKU_HAS_BZIP) return false;
115
            $ret = bzfile($file);
116
        }else{
117
            $ret = file_get_contents($file);
118
        }
119
    }
120
    if($ret === null) return false;
121
    if($ret !== false && $clean){
122
        return cleanText($ret);
123
    }else{
124
        return $ret;
125
    }
126
}
127
/**
128
 * Returns the content of a .bz2 compressed file as string
129
 *
130
 * @author marcel senf <[email protected]>
131
 * @author  Andreas Gohr <[email protected]>
132
 *
133
 * @param string $file filename
134
 * @param bool   $array return array of lines
135
 * @return string|array|bool content or false on error
136
 */
137
function bzfile($file, $array=false) {
138
    $bz = bzopen($file,"r");
139
    if($bz === false) return false;
140
141
    if($array) $lines = array();
142
    $str = '';
143
    while (!feof($bz)) {
144
        //8192 seems to be the maximum buffersize?
145
        $buffer = bzread($bz,8192);
146
        if(($buffer === false) || (bzerrno($bz) !== 0)) {
147
            return false;
148
        }
149
        $str = $str . $buffer;
150
        if($array) {
151
            $pos = strpos($str, "\n");
152
            while($pos !== false) {
153
                $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...
154
                $str = substr($str, $pos+1);
155
                $pos = strpos($str, "\n");
156
            }
157
        }
158
    }
159
    bzclose($bz);
160
    if($array) {
161
        if($str !== '') $lines[] = $str;
162
        return $lines;
163
    }
164
    return $str;
165
}
166
167
/**
168
 * Used to write out a DokuWiki page to file, and send IO_WIKIPAGE_WRITE events.
169
 *
170
 * This generates an action event and delegates to io_saveFile().
171
 * Action plugins are allowed to modify the page content in transit.
172
 * The file path should not be changed.
173
 * (The append parameter is set to false.)
174
 *
175
 * Event data:
176
 * $data[0]    The raw arguments for io_saveFile as an array.
177
 * $data[1]    ns: The colon separated namespace path minus the trailing page name. (false if root ns)
178
 * $data[2]    page_name: The wiki page name.
179
 * $data[3]    rev: The page revision, false for current wiki pages.
180
 *
181
 * @author Ben Coburn <[email protected]>
182
 *
183
 * @param string $file      filename
184
 * @param string $content
185
 * @param string $id        page id
186
 * @param int|bool $rev timestamp of revision
187
 * @return bool
188
 */
189
function io_writeWikiPage($file, $content, $id, $rev=false) {
190
    if (empty($rev)) { $rev = false; }
191
    if ($rev===false) { io_createNamespace($id); } // create namespaces as needed
192
    $data = array(array($file, $content, false), getNS($id), noNS($id), $rev);
193
    return trigger_event('IO_WIKIPAGE_WRITE', $data, '_io_writeWikiPage_action', false);
194
}
195
196
/**
197
 * Callback adapter for io_saveFile().
198
 * @author Ben Coburn <[email protected]>
199
 *
200
 * @param array $data event data
201
 * @return bool
202
 */
203
function _io_writeWikiPage_action($data) {
204
    if (is_array($data) && is_array($data[0]) && count($data[0])===3) {
205
        $ok = call_user_func_array('io_saveFile', $data[0]);
206
        // for attic files make sure the file has the mtime of the revision
207
        if($ok && is_int($data[3]) && $data[3] > 0) {
208
            @touch($data[0][0], $data[3]);
209
        }
210
        return $ok;
211
    } else {
212
        return false; //callback error
213
    }
214
}
215
216
/**
217
 * Internal function to save contents to a file.
218
 *
219
 * @author  Andreas Gohr <[email protected]>
220
 *
221
 * @param string $file filename path to file
222
 * @param string $content
223
 * @param bool   $append
224
 * @return bool true on success, otherwise false
225
 */
226
function _io_saveFile($file, $content, $append) {
227
    global $conf;
228
    $mode = ($append) ? 'ab' : 'wb';
229
    $fileexists = file_exists($file);
230
231
    if(substr($file,-3) == '.gz'){
232
        if(!DOKU_HAS_GZIP) return false;
233
        $fh = @gzopen($file,$mode.'9');
234
        if(!$fh) return false;
235
        gzwrite($fh, $content);
236
        gzclose($fh);
237
    }else if(substr($file,-4) == '.bz2'){
238
        if(!DOKU_HAS_BZIP) return false;
239
        if($append) {
240
            $bzcontent = bzfile($file);
241
            if($bzcontent === false) return false;
242
            $content = $bzcontent.$content;
243
        }
244
        $fh = @bzopen($file,'w');
245
        if(!$fh) return false;
246
        bzwrite($fh, $content);
247
        bzclose($fh);
248
    }else{
249
        $fh = @fopen($file,$mode);
250
        if(!$fh) return false;
251
        fwrite($fh, $content);
252
        fclose($fh);
253
    }
254
255
    if(!$fileexists and !empty($conf['fperm'])) chmod($file, $conf['fperm']);
256
    return true;
257
}
258
259
/**
260
 * Saves $content to $file.
261
 *
262
 * If the third parameter is set to true the given content
263
 * will be appended.
264
 *
265
 * Uses gzip if extension is .gz
266
 * and bz2 if extension is .bz2
267
 *
268
 * @author  Andreas Gohr <[email protected]>
269
 *
270
 * @param string $file filename path to file
271
 * @param string $content
272
 * @param bool   $append
273
 * @return bool true on success, otherwise false
274
 */
275
function io_saveFile($file, $content, $append=false) {
276
    io_makeFileDir($file);
277
    io_lock($file);
278
    if(!_io_saveFile($file, $content, $append)) {
279
        msg("Writing $file failed",-1);
280
        io_unlock($file);
281
        return false;
282
    }
283
    io_unlock($file);
284
    return true;
285
}
286
287
/**
288
 * Replace one or more occurrences of a line in a file.
289
 *
290
 * The default, when $maxlines is 0 is to delete all matching lines then append a single line.
291
 * A regex that matches any part of the line will remove the entire line in this mode.
292
 * Captures in $newline are not available.
293
 *
294
 * Otherwise each line is matched and replaced individually, up to the first $maxlines lines
295
 * or all lines if $maxlines is -1. If $regex is true then captures can be used in $newline.
296
 *
297
 * Be sure to include the trailing newline in $oldline when replacing entire lines.
298
 *
299
 * Uses gzip if extension is .gz
300
 * and bz2 if extension is .bz2
301
 *
302
 * @author Steven Danz <[email protected]>
303
 * @author Christopher Smith <[email protected]>
304
 * @author Patrick Brown <[email protected]>
305
 *
306
 * @param string $file     filename
307
 * @param string $oldline  exact linematch to remove
308
 * @param string $newline  new line to insert
309
 * @param bool   $regex    use regexp?
310
 * @param int    $maxlines number of occurrences of the line to replace
311
 * @return bool true on success
312
 */
313
function io_replaceInFile($file, $oldline, $newline, $regex=false, $maxlines=0) {
314
    if ((string)$oldline === '') {
315
        trigger_error('$oldline parameter cannot be empty in io_replaceInFile()', E_USER_WARNING);
316
        return false;
317
    }
318
319
    if (!file_exists($file)) return true;
320
321
    io_lock($file);
322
323
    // load into array
324
    if(substr($file,-3) == '.gz'){
325
        if(!DOKU_HAS_GZIP) return false;
326
        $lines = gzfile($file);
327
    }else if(substr($file,-4) == '.bz2'){
328
        if(!DOKU_HAS_BZIP) return false;
329
        $lines = bzfile($file, true);
330
    }else{
331
        $lines = file($file);
332
    }
333
334
    // make non-regexes into regexes
335
    $pattern = $regex ? $oldline : '/^'.preg_quote($oldline,'/').'$/';
336
    $replace = $regex ? $newline : addcslashes($newline, '\$');
337
338
    // remove matching lines
339
    if ($maxlines > 0) {
340
        $count = 0;
341
        $matched = 0;
342
        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...
343
            if($count >= $maxlines) break;
344
            // $matched will be set to 0|1 depending on whether pattern is matched and line replaced
345
            $lines[$i] = preg_replace($pattern, $replace, $line, -1, $matched);
346
            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...
347
        }
348
    } else if ($maxlines == 0) {
349
        $lines = preg_grep($pattern, $lines, PREG_GREP_INVERT);
350
351
        if ((string)$newline !== ''){
352
            $lines[] = $newline;
353
        }
354
    } else {
355
        $lines = preg_replace($pattern, $replace, $lines);
356
    }
357
358
    if(count($lines)){
359
        if(!_io_saveFile($file, join('',$lines), false)) {
360
            msg("Removing content from $file failed",-1);
361
            io_unlock($file);
362
            return false;
363
        }
364
    }else{
365
        @unlink($file);
366
    }
367
368
    io_unlock($file);
369
    return true;
370
}
371
372
/**
373
 * Delete lines that match $badline from $file.
374
 *
375
 * Be sure to include the trailing newline in $badline
376
 *
377
 * @author Patrick Brown <[email protected]>
378
 *
379
 * @param string $file    filename
380
 * @param string $badline exact linematch to remove
381
 * @param bool   $regex   use regexp?
382
 * @return bool true on success
383
 */
384
function io_deleteFromFile($file,$badline,$regex=false){
385
    return io_replaceInFile($file,$badline,null,$regex,0);
386
}
387
388
/**
389
 * Tries to lock a file
390
 *
391
 * Locking is only done for io_savefile and uses directories
392
 * inside $conf['lockdir']
393
 *
394
 * It waits maximal 3 seconds for the lock, after this time
395
 * the lock is assumed to be stale and the function goes on
396
 *
397
 * @author Andreas Gohr <[email protected]>
398
 *
399
 * @param string $file filename
400
 */
401
function io_lock($file){
402
    global $conf;
403
    // no locking if safemode hack
404
    if($conf['safemodehack']) return;
405
406
    $lockDir = $conf['lockdir'].'/'.md5($file);
407
    @ignore_user_abort(1);
408
409
    $timeStart = time();
410
    do {
411
        //waited longer than 3 seconds? -> stale lock
412
        if ((time() - $timeStart) > 3) break;
413
        $locked = @mkdir($lockDir, $conf['dmode']);
414
        if($locked){
415
            if(!empty($conf['dperm'])) chmod($lockDir, $conf['dperm']);
416
            break;
417
        }
418
        usleep(50);
419
    } while ($locked === false);
420
}
421
422
/**
423
 * Unlocks a file
424
 *
425
 * @author Andreas Gohr <[email protected]>
426
 *
427
 * @param string $file filename
428
 */
429
function io_unlock($file){
430
    global $conf;
431
    // no locking if safemode hack
432
    if($conf['safemodehack']) return;
433
434
    $lockDir = $conf['lockdir'].'/'.md5($file);
435
    @rmdir($lockDir);
436
    @ignore_user_abort(0);
437
}
438
439
/**
440
 * Create missing namespace directories and send the IO_NAMESPACE_CREATED events
441
 * in the order of directory creation. (Parent directories first.)
442
 *
443
 * Event data:
444
 * $data[0]    ns: The colon separated namespace path minus the trailing page name.
445
 * $data[1]    ns_type: 'pages' or 'media' namespace tree.
446
 *
447
 * @author Ben Coburn <[email protected]>
448
 *
449
 * @param string $id page id
450
 * @param string $ns_type 'pages' or 'media'
451
 */
452
function io_createNamespace($id, $ns_type='pages') {
453
    // verify ns_type
454
    $types = array('pages'=>'wikiFN', 'media'=>'mediaFN');
455
    if (!isset($types[$ns_type])) {
456
        trigger_error('Bad $ns_type parameter for io_createNamespace().');
457
        return;
458
    }
459
    // make event list
460
    $missing = array();
461
    $ns_stack = explode(':', $id);
462
    $ns = $id;
463
    $tmp = dirname( $file = call_user_func($types[$ns_type], $ns) );
464
    while (!@is_dir($tmp) && !(file_exists($tmp) && !is_dir($tmp))) {
465
        array_pop($ns_stack);
466
        $ns = implode(':', $ns_stack);
467
        if (strlen($ns)==0) { break; }
468
        $missing[] = $ns;
469
        $tmp = dirname(call_user_func($types[$ns_type], $ns));
470
    }
471
    // make directories
472
    io_makeFileDir($file);
473
    // send the events
474
    $missing = array_reverse($missing); // inside out
475
    foreach ($missing as $ns) {
476
        $data = array($ns, $ns_type);
477
        trigger_event('IO_NAMESPACE_CREATED', $data);
478
    }
479
}
480
481
/**
482
 * Create the directory needed for the given file
483
 *
484
 * @author  Andreas Gohr <[email protected]>
485
 *
486
 * @param string $file file name
487
 */
488
function io_makeFileDir($file){
489
    $dir = dirname($file);
490
    if(!@is_dir($dir)){
491
        io_mkdir_p($dir) || msg("Creating directory $dir failed",-1);
492
    }
493
}
494
495
/**
496
 * Creates a directory hierachy.
497
 *
498
 * @link    http://php.net/manual/en/function.mkdir.php
499
 * @author  <[email protected]>
500
 * @author  Andreas Gohr <[email protected]>
501
 *
502
 * @param string $target filename
503
 * @return bool|int|string
504
 */
505
function io_mkdir_p($target){
506
    global $conf;
507
    if (@is_dir($target)||empty($target)) return 1; // best case check first
508
    if (file_exists($target) && !is_dir($target)) return 0;
509
    //recursion
510
    if (io_mkdir_p(substr($target,0,strrpos($target,'/')))){
511
        if($conf['safemodehack']){
512
            $dir = preg_replace('/^'.preg_quote(fullpath($conf['ftp']['root']),'/').'/','', $target);
513
            return io_mkdir_ftp($dir);
514
        }else{
515
            $ret = @mkdir($target,$conf['dmode']); // crawl back up & create dir tree
516
            if($ret && !empty($conf['dperm'])) chmod($target, $conf['dperm']);
517
            return $ret;
518
        }
519
    }
520
    return 0;
521
}
522
523
/**
524
 * Recursively delete a directory
525
 *
526
 * @author Andreas Gohr <[email protected]>
527
 * @param string $path
528
 * @param bool   $removefiles defaults to false which will delete empty directories only
529
 * @return bool
530
 */
531
function io_rmdir($path, $removefiles = false) {
532
    if(!is_string($path) || $path == "") return false;
533
    if(!file_exists($path)) return true; // it's already gone or was never there, count as success
534
535
    if(is_dir($path) && !is_link($path)) {
536
        $dirs  = array();
537
        $files = array();
538
539
        if(!$dh = @opendir($path)) return false;
540
        while(false !== ($f = readdir($dh))) {
541
            if($f == '..' || $f == '.') continue;
542
543
            // collect dirs and files first
544
            if(is_dir("$path/$f") && !is_link("$path/$f")) {
545
                $dirs[] = "$path/$f";
546
            } else if($removefiles) {
547
                $files[] = "$path/$f";
548
            } else {
549
                return false; // abort when non empty
550
            }
551
552
        }
553
        closedir($dh);
554
555
        // now traverse into  directories first
556
        foreach($dirs as $dir) {
557
            if(!io_rmdir($dir, $removefiles)) return false; // abort on any error
558
        }
559
560
        // now delete files
561
        foreach($files as $file) {
562
            if(!@unlink($file)) return false; //abort on any error
563
        }
564
565
        // remove self
566
        return @rmdir($path);
567
    } else if($removefiles) {
568
        return @unlink($path);
569
    }
570
    return false;
571
}
572
573
/**
574
 * Creates a directory using FTP
575
 *
576
 * This is used when the safemode workaround is enabled
577
 *
578
 * @author <[email protected]>
579
 *
580
 * @param string $dir name of the new directory
581
 * @return false|string
582
 */
583
function io_mkdir_ftp($dir){
584
    global $conf;
585
586
    if(!function_exists('ftp_connect')){
587
        msg("FTP support not found - safemode workaround not usable",-1);
588
        return false;
589
    }
590
591
    $conn = @ftp_connect($conf['ftp']['host'],$conf['ftp']['port'],10);
592
    if(!$conn){
593
        msg("FTP connection failed",-1);
594
        return false;
595
    }
596
597
    if(!@ftp_login($conn, $conf['ftp']['user'], conf_decodeString($conf['ftp']['pass']))){
598
        msg("FTP login failed",-1);
599
        return false;
600
    }
601
602
    //create directory
603
    $ok = @ftp_mkdir($conn, $dir);
604
    //set permissions
605
    @ftp_site($conn,sprintf("CHMOD %04o %s",$conf['dmode'],$dir));
606
607
    @ftp_close($conn);
608
    return $ok;
609
}
610
611
/**
612
 * Creates a unique temporary directory and returns
613
 * its path.
614
 *
615
 * @author Michael Klier <[email protected]>
616
 *
617
 * @return false|string path to new directory or false
618
 */
619
function io_mktmpdir() {
620
    global $conf;
621
622
    $base = $conf['tmpdir'];
623
    $dir  = md5(uniqid(mt_rand(), true));
624
    $tmpdir = $base.'/'.$dir;
625
626
    if(io_mkdir_p($tmpdir)) {
627
        return($tmpdir);
628
    } else {
629
        return false;
630
    }
631
}
632
633
/**
634
 * downloads a file from the net and saves it
635
 *
636
 * if $useAttachment is false,
637
 * - $file is the full filename to save the file, incl. path
638
 * - if successful will return true, false otherwise
639
 *
640
 * if $useAttachment is true,
641
 * - $file is the directory where the file should be saved
642
 * - if successful will return the name used for the saved file, false otherwise
643
 *
644
 * @author Andreas Gohr <[email protected]>
645
 * @author Chris Smith <[email protected]>
646
 *
647
 * @param string $url           url to download
648
 * @param string $file          path to file or directory where to save
649
 * @param bool   $useAttachment if true: try to use name of download, uses otherwise $defaultName, false: uses $file as path to file
650
 * @param string $defaultName   fallback for if using $useAttachment
651
 * @param int    $maxSize       maximum file size
652
 * @return bool|string          if failed false, otherwise true or the name of the file in the given dir
653
 */
654
function io_download($url,$file,$useAttachment=false,$defaultName='',$maxSize=2097152){
655
    global $conf;
656
    $http = new DokuHTTPClient();
657
    $http->max_bodysize = $maxSize;
658
    $http->timeout = 25; //max. 25 sec
659
    $http->keep_alive = false; // we do single ops here, no need for keep-alive
660
661
    $data = $http->get($url);
662
    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...
663
664
    $name = '';
665
    if ($useAttachment) {
666
        if (isset($http->resp_headers['content-disposition'])) {
667
            $content_disposition = $http->resp_headers['content-disposition'];
668
            $match=array();
669
            if (is_string($content_disposition) &&
670
                    preg_match('/attachment;\s*filename\s*=\s*"([^"]*)"/i', $content_disposition, $match)) {
671
672
                $name = utf8_basename($match[1]);
673
            }
674
675
        }
676
677
        if (!$name) {
678
            if (!$defaultName) return false;
679
            $name = $defaultName;
680
        }
681
682
        $file = $file.$name;
683
    }
684
685
    $fileexists = file_exists($file);
686
    $fp = @fopen($file,"w");
687
    if(!$fp) return false;
688
    fwrite($fp,$data);
689
    fclose($fp);
690
    if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
691
    if ($useAttachment) return $name;
692
    return true;
693
}
694
695
/**
696
 * Windows compatible rename
697
 *
698
 * rename() can not overwrite existing files on Windows
699
 * this function will use copy/unlink instead
700
 *
701
 * @param string $from
702
 * @param string $to
703
 * @return bool succes or fail
704
 */
705
function io_rename($from,$to){
706
    global $conf;
707
    if(!@rename($from,$to)){
708
        if(@copy($from,$to)){
709
            if($conf['fperm']) chmod($to, $conf['fperm']);
710
            @unlink($from);
711
            return true;
712
        }
713
        return false;
714
    }
715
    return true;
716
}
717
718
/**
719
 * Runs an external command with input and output pipes.
720
 * Returns the exit code from the process.
721
 *
722
 * @author Tom N Harris <[email protected]>
723
 *
724
 * @param string $cmd
725
 * @param string $input  input pipe
726
 * @param string $output output pipe
727
 * @return int exit code from process
728
 */
729
function io_exec($cmd, $input, &$output){
730
    $descspec = array(
731
            0=>array("pipe","r"),
732
            1=>array("pipe","w"),
733
            2=>array("pipe","w"));
734
    $ph = proc_open($cmd, $descspec, $pipes);
735
    if(!$ph) return -1;
736
    fclose($pipes[2]); // ignore stderr
737
    fwrite($pipes[0], $input);
738
    fclose($pipes[0]);
739
    $output = stream_get_contents($pipes[1]);
740
    fclose($pipes[1]);
741
    return proc_close($ph);
742
}
743
744
/**
745
 * Search a file for matching lines
746
 *
747
 * This is probably not faster than file()+preg_grep() but less
748
 * memory intensive because not the whole file needs to be loaded
749
 * at once.
750
 *
751
 * @author Andreas Gohr <[email protected]>
752
 * @param  string $file    The file to search
753
 * @param  string $pattern PCRE pattern
754
 * @param  int    $max     How many lines to return (0 for all)
755
 * @param  bool   $backref When true returns array with backreferences instead of lines
756
 * @return array matching lines or backref, false on error
757
 */
758
function io_grep($file,$pattern,$max=0,$backref=false){
759
    $fh = @fopen($file,'r');
760
    if(!$fh) return false;
761
    $matches = array();
762
763
    $cnt  = 0;
764
    $line = '';
765
    while (!feof($fh)) {
766
        $line .= fgets($fh, 4096);  // read full line
767
        if(substr($line,-1) != "\n") continue;
768
769
        // check if line matches
770
        if(preg_match($pattern,$line,$match)){
771
            if($backref){
772
                $matches[] = $match;
773
            }else{
774
                $matches[] = $line;
775
            }
776
            $cnt++;
777
        }
778
        if($max && $max == $cnt) break;
779
        $line = '';
780
    }
781
    fclose($fh);
782
    return $matches;
783
}
784
785
786
/**
787
 * Get size of contents of a file, for a compressed file the uncompressed size
788
 * Warning: reading uncompressed size of content of bz-files requires uncompressing
789
 *
790
 * @author  Gerrit Uitslag <[email protected]>
791
 *
792
 * @param string $file filename path to file
793
 * @return int size of file
794
 */
795
function io_getSizeFile($file) {
796
    if (!file_exists($file)) return 0;
797
798
    if(substr($file,-3) == '.gz'){
799
        $fp = @fopen($file, "rb");
800
        if($fp === false) return 0;
801
802
        fseek($fp, -4, SEEK_END);
803
        $buffer = fread($fp, 4);
804
        fclose($fp);
805
        $array = unpack("V", $buffer);
806
        $uncompressedsize = end($array);
807
    }else if(substr($file,-4) == '.bz2'){
808
        if(!DOKU_HAS_BZIP) return 0;
809
810
        $bz = bzopen($file,"r");
811
        if($bz === false) return 0;
812
813
        $uncompressedsize = 0;
814
        while (!feof($bz)) {
815
            //8192 seems to be the maximum buffersize?
816
            $buffer = bzread($bz,8192);
817
            if(($buffer === false) || (bzerrno($bz) !== 0)) {
818
                return 0;
819
            }
820
            $uncompressedsize += strlen($buffer);
821
        }
822
    }else{
823
        $uncompressedsize = filesize($file);
824
    }
825
826
    return $uncompressedsize;
827
 }
828