Completed
Push — ghaction ( 43957e...7bbf38 )
by Andreas
02:52
created

fetch.functions.php ➔ sendFile()   C

Complexity

Conditions 9
Paths 144

Size

Total Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
nc 144
nop 7
dl 0
loc 69
rs 6.8274
c 0
b 0
f 0

How to fix   Long Method   

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
 * Functions used by lib/exe/fetch.php
4
 * (not included by other parts of dokuwiki)
5
 */
6
7
/**
8
 * Set headers and send the file to the client
9
 *
10
 * The $cache parameter influences how long files may be kept in caches, the $public parameter
11
 * influences if this caching may happen in public proxis or in the browser cache only FS#2734
12
 *
13
 * This function will abort the current script when a 304 is sent or file sending is handled
14
 * through x-sendfile
15
 *
16
 * @param string $file local file to send
17
 * @param string $mime mime type of the file
18
 * @param bool $dl set to true to force a browser download
19
 * @param int $cache remaining cache time in seconds (-1 for $conf['cache'], 0 for no-cache)
20
 * @param bool $public is this a public ressource or a private one?
21
 * @param string $orig original file to send - the file name will be used for the Content-Disposition
22
 * @param array $csp The ContentSecurityPolicy to send
23
 * @author Andreas Gohr <[email protected]>
24
 * @author Ben Coburn <[email protected]>
25
 * @author Gerry Weissbach <[email protected]>
26
 *
27
 */
28
function sendFile($file, $mime, $dl, $cache, $public = false, $orig = null, $csp=[]) {
29
    global $conf;
30
    // send mime headers
31
    header("Content-Type: $mime");
32
33
    // send security policy if given
34
    if (!empty($csp)) dokuwiki\HTTP\Headers::contentSecurityPolicy($csp);
35
36
    // calculate cache times
37
    if($cache == -1) {
38
        $maxage  = max($conf['cachetime'], 3600); // cachetime or one hour
39
        $expires = time() + $maxage;
40
    } else if($cache > 0) {
41
        $maxage  = $cache; // given time
42
        $expires = time() + $maxage;
43
    } else { // $cache == 0
44
        $maxage  = 0;
45
        $expires = 0; // 1970-01-01
46
    }
47
48
    // smart http caching headers
49
    if($maxage) {
50
        if($public) {
51
            // cache publically
52
            header('Expires: '.gmdate("D, d M Y H:i:s", $expires).' GMT');
53
            header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.$maxage);
54
        } else {
55
            // cache in browser
56
            header('Expires: '.gmdate("D, d M Y H:i:s", $expires).' GMT');
57
            header('Cache-Control: private, no-transform, max-age='.$maxage);
58
        }
59
    } else {
60
        // no cache at all
61
        header('Expires: Thu, 01 Jan 1970 00:00:00 GMT');
62
        header('Cache-Control: no-cache, no-transform');
63
    }
64
65
    //send important headers first, script stops here if '304 Not Modified' response
66
    $fmtime = @filemtime($file);
67
    http_conditionalRequest($fmtime);
68
69
    // Use the current $file if is $orig is not set.
70
    if ( $orig == null ) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $orig of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
71
        $orig = $file;
72
    }
73
74
    //download or display?
75
    if ($dl) {
76
        header('Content-Disposition: attachment;' . rfc2231_encode(
77
                'filename', \dokuwiki\Utf8\PhpString::basename($orig)) . ';'
78
        );
79
    } else {
80
        header('Content-Disposition: inline;' . rfc2231_encode(
81
                'filename', \dokuwiki\Utf8\PhpString::basename($orig)) . ';'
82
        );
83
    }
84
85
    //use x-sendfile header to pass the delivery to compatible webservers
86
    http_sendfile($file);
87
88
    // send file contents
89
    $fp = @fopen($file, "rb");
90
    if($fp) {
91
        http_rangeRequest($fp, filesize($file), $mime);
92
    } else {
93
        http_status(500);
94
        print "Could not read $file - bad permissions?";
95
    }
96
}
97
98
/**
99
 * Try an rfc2231 compatible encoding. This ensures correct
100
 * interpretation of filenames outside of the ASCII set.
101
 * This seems to be needed for file names with e.g. umlauts that
102
 * would otherwise decode wrongly in IE.
103
 *
104
 * There is no additional checking, just the encoding and setting the key=value for usage in headers
105
 *
106
 * @author Gerry Weissbach <[email protected]>
107
 * @param string $name      name of the field to be set in the header() call
108
 * @param string $value     value of the field to be set in the header() call
109
 * @param string $charset   used charset for the encoding of value
110
 * @param string $lang      language used.
111
 * @return string           in the format " name=value" for values WITHOUT special characters
112
 * @return string           in the format " name*=charset'lang'value" for values WITH special characters
113
 */
114
function rfc2231_encode($name, $value, $charset='utf-8', $lang='en') {
115
    $internal = preg_replace_callback(
116
        '/[\x00-\x20*\'%()<>@,;:\\\\"\/[\]?=\x80-\xFF]/',
117
        function ($match) {
118
            return rawurlencode($match[0]);
119
        },
120
        $value
121
    );
122
    if ( $value != $internal ) {
123
        return ' '.$name.'*='.$charset."'".$lang."'".$internal;
124
    } else {
125
        return ' '.$name.'="'.$value.'"';
126
    }
127
}
128
129
/**
130
 * Check for media for preconditions and return correct status code
131
 *
132
 * READ: MEDIA, MIME, EXT, CACHE
133
 * WRITE: MEDIA, FILE, array( STATUS, STATUSMESSAGE )
134
 *
135
 * @author Gerry Weissbach <[email protected]>
136
 *
137
 * @param string $media  reference to the media id
138
 * @param string $file   reference to the file variable
139
 * @param string $rev
140
 * @param int    $width
141
 * @param int    $height
142
 * @return array as array(STATUS, STATUSMESSAGE)
143
 */
144
function checkFileStatus(&$media, &$file, $rev = '', $width=0, $height=0) {
145
    global $MIME, $EXT, $CACHE, $INPUT;
146
147
    //media to local file
148
    if(media_isexternal($media)) {
149
        //check token for external image and additional for resized and cached images
150
        if(media_get_token($media, $width, $height) !== $INPUT->str('tok')) {
151
            return array(412, 'Precondition Failed');
152
        }
153
        //handle external images
154
        if(strncmp($MIME, 'image/', 6) == 0) $file = media_get_from_URL($media, $EXT, $CACHE);
155
        if(!$file) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $file 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...
156
            //download failed - redirect to original URL
157
            return array(302, $media);
158
        }
159
    } else {
160
        $media = cleanID($media);
161
        if(empty($media)) {
162
            return array(400, 'Bad request');
163
        }
164
        // check token for resized images
165
        if (($width || $height) && media_get_token($media, $width, $height) !== $INPUT->str('tok')) {
166
            return array(412, 'Precondition Failed');
167
        }
168
169
        //check permissions (namespace only)
170
        if(auth_quickaclcheck(getNS($media).':X') < AUTH_READ) {
171
            return array(403, 'Forbidden');
172
        }
173
        $file = mediaFN($media, $rev);
174
    }
175
176
    //check file existance
177
    if(!file_exists($file)) {
178
        return array(404, 'Not Found');
179
    }
180
181
    return array(200, null);
182
}
183
184
/**
185
 * Returns the wanted cachetime in seconds
186
 *
187
 * Resolves named constants
188
 *
189
 * @author  Andreas Gohr <[email protected]>
190
 *
191
 * @param string $cache
192
 * @return int cachetime in seconds
193
 */
194
function calc_cache($cache) {
195
    global $conf;
196
197
    if(strtolower($cache) == 'nocache') return 0; //never cache
198
    if(strtolower($cache) == 'recache') return $conf['cachetime']; //use standard cache
199
    return -1; //cache endless
200
}
201