| Conditions | 81 |
| Paths | > 20000 |
| Total Lines | 334 |
| Code Lines | 160 |
| Lines | 62 |
| Ratio | 18.56 % |
| Changes | 0 | ||
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:
If many parameters/temporary variables are present:
| 1 | <?php |
||
| 22 | function showAttachment() |
||
| 23 | { |
||
| 24 | global $smcFunc, $modSettings, $maintenance, $context; |
||
| 25 | |||
| 26 | // Some defaults that we need. |
||
| 27 | $context['character_set'] = empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'ISO-8859-1' : $txt['lang_character_set']) : $modSettings['global_character_set']; |
||
|
|
|||
| 28 | $context['utf8'] = $context['character_set'] === 'UTF-8'; |
||
| 29 | |||
| 30 | // An early hook to set up global vars, clean cache and other early process. |
||
| 31 | call_integration_hook('integrate_pre_download_request'); |
||
| 32 | |||
| 33 | // This is done to clear any output that was made before now. |
||
| 34 | ob_end_clean(); |
||
| 35 | |||
| 36 | if (!empty($modSettings['enableCompressedOutput']) && !headers_sent() && ob_get_length() == 0) |
||
| 37 | { |
||
| 38 | View Code Duplication | if (@ini_get('zlib.output_compression') == '1' || @ini_get('output_handler') == 'ob_gzhandler') |
|
| 39 | $modSettings['enableCompressedOutput'] = 0; |
||
| 40 | |||
| 41 | else |
||
| 42 | ob_start('ob_gzhandler'); |
||
| 43 | } |
||
| 44 | |||
| 45 | if (empty($modSettings['enableCompressedOutput'])) |
||
| 46 | { |
||
| 47 | ob_start(); |
||
| 48 | header('Content-Encoding: none'); |
||
| 49 | } |
||
| 50 | |||
| 51 | // Better handling. |
||
| 52 | $attachId = isset($_REQUEST['attach']) ? (int) $_REQUEST['attach'] : (int) (isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0); |
||
| 53 | |||
| 54 | // We need a valid ID. |
||
| 55 | if (empty($attachId)) |
||
| 56 | { |
||
| 57 | header('HTTP/1.0 404 File Not Found'); |
||
| 58 | die('404 File Not Found'); |
||
| 59 | } |
||
| 60 | |||
| 61 | // A thumbnail has been requested? madness! madness I say! |
||
| 62 | $preview = isset($_REQUEST['preview']) ? $_REQUEST['preview'] : (isset($_REQUEST['type']) && $_REQUEST['type'] == 'preview' ? $_REQUEST['type'] : 0); |
||
| 63 | $showThumb = isset($_REQUEST['thumb']) || !empty($preview); |
||
| 64 | $attachTopic = isset($_REQUEST['topic']) ? (int) $_REQUEST['topic'] : 0; |
||
| 65 | |||
| 66 | // No access in strict maintenance mode or you don't have permission to see attachments. |
||
| 67 | if ((!empty($maintenance) && $maintenance == 2) || !allowedTo('view_attachments')) |
||
| 68 | { |
||
| 69 | header('HTTP/1.0 404 File Not Found'); |
||
| 70 | die('404 File Not Found'); |
||
| 71 | } |
||
| 72 | |||
| 73 | // Use cache when possible. |
||
| 74 | if (($cache = cache_get_data('attachment_lookup_id-' . $attachId)) != null) |
||
| 75 | list($file, $thumbFile) = $cache; |
||
| 76 | |||
| 77 | // Get the info from the DB. |
||
| 78 | if (empty($file) || empty($thumbFile) && !empty($file['id_thumb'])) |
||
| 79 | { |
||
| 80 | // Do we have a hook wanting to use our attachment system? We use $attachRequest to prevent accidental usage of $request. |
||
| 81 | $attachRequest = null; |
||
| 82 | call_integration_hook('integrate_download_request', array(&$attachRequest)); |
||
| 83 | if (!is_null($attachRequest) && $smcFunc['db_is_resource']($attachRequest)) |
||
| 84 | $request = $attachRequest; |
||
| 85 | |||
| 86 | else |
||
| 87 | { |
||
| 88 | // Make sure this attachment is on this board and load its info while we are at it. |
||
| 89 | $request = $smcFunc['db_query']('', ' |
||
| 90 | SELECT id_folder, filename, file_hash, fileext, id_attach, id_thumb, attachment_type, mime_type, approved, id_msg |
||
| 91 | FROM {db_prefix}attachments |
||
| 92 | WHERE id_attach = {int:attach} |
||
| 93 | LIMIT 1', |
||
| 94 | array( |
||
| 95 | 'attach' => $attachId, |
||
| 96 | ) |
||
| 97 | ); |
||
| 98 | } |
||
| 99 | |||
| 100 | // No attachment has been found. |
||
| 101 | if ($smcFunc['db_num_rows']($request) == 0) |
||
| 102 | { |
||
| 103 | header('HTTP/1.0 404 File Not Found'); |
||
| 104 | die('404 File Not Found'); |
||
| 105 | } |
||
| 106 | |||
| 107 | $file = $smcFunc['db_fetch_assoc']($request); |
||
| 108 | $smcFunc['db_free_result']($request); |
||
| 109 | |||
| 110 | // If theres a message ID stored, we NEED a topic ID. |
||
| 111 | if (!empty($file['id_msg']) && empty($attachTopic) && empty($preview)) |
||
| 112 | { |
||
| 113 | header('HTTP/1.0 404 File Not Found'); |
||
| 114 | die('404 File Not Found'); |
||
| 115 | } |
||
| 116 | |||
| 117 | // Previews doesn't have this info. |
||
| 118 | if (empty($preview)) |
||
| 119 | { |
||
| 120 | $request2 = $smcFunc['db_query']('', ' |
||
| 121 | SELECT a.id_msg |
||
| 122 | FROM {db_prefix}attachments AS a |
||
| 123 | INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg AND m.id_topic = {int:current_topic}) |
||
| 124 | INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board}) |
||
| 125 | WHERE a.id_attach = {int:attach} |
||
| 126 | LIMIT 1', |
||
| 127 | array( |
||
| 128 | 'attach' => $attachId, |
||
| 129 | 'current_topic' => $attachTopic, |
||
| 130 | ) |
||
| 131 | ); |
||
| 132 | |||
| 133 | // The provided topic must match the one stored in the DB for this particular attachment, also. |
||
| 134 | if ($smcFunc['db_num_rows']($request2) == 0) |
||
| 135 | { |
||
| 136 | header('HTTP/1.0 404 File Not Found'); |
||
| 137 | die('404 File Not Found'); |
||
| 138 | } |
||
| 139 | |||
| 140 | $smcFunc['db_free_result']($request2); |
||
| 141 | } |
||
| 142 | |||
| 143 | // set filePath and ETag time |
||
| 144 | $file['filePath'] = getAttachmentFilename($file['filename'], $attachId, $file['id_folder'], false, $file['file_hash']); |
||
| 145 | // ensure variant attachment compatibility |
||
| 146 | $filePath = pathinfo($file['filePath']); |
||
| 147 | $file['filePath'] = !file_exists($file['filePath']) ? substr($file['filePath'], 0, -(strlen($filePath['extension']) + 1)) : $file['filePath']; |
||
| 148 | $file['etag'] = '"' . md5_file($file['filePath']) . '"'; |
||
| 149 | |||
| 150 | // now get the thumbfile! |
||
| 151 | $thumbFile = array(); |
||
| 152 | if (!empty($file['id_thumb'])) |
||
| 153 | { |
||
| 154 | $request = $smcFunc['db_query']('', ' |
||
| 155 | SELECT id_folder, filename, file_hash, fileext, id_attach, attachment_type, mime_type, approved, id_member |
||
| 156 | FROM {db_prefix}attachments |
||
| 157 | WHERE id_attach = {int:thumb_id} |
||
| 158 | LIMIT 1', |
||
| 159 | array( |
||
| 160 | 'thumb_id' => $file['id_thumb'], |
||
| 161 | ) |
||
| 162 | ); |
||
| 163 | |||
| 164 | $thumbFile = $smcFunc['db_fetch_assoc']($request); |
||
| 165 | $smcFunc['db_free_result']($request); |
||
| 166 | |||
| 167 | // Got something! replace the $file var with the thumbnail info. |
||
| 168 | View Code Duplication | if ($thumbFile) |
|
| 169 | { |
||
| 170 | $attachId = $thumbFile['id_attach']; |
||
| 171 | |||
| 172 | // set filePath and ETag time |
||
| 173 | $thumbFile['filePath'] = getAttachmentFilename($thumbFile['filename'], $attachId, $thumbFile['id_folder'], false, $thumbFile['file_hash']); |
||
| 174 | $thumbFile['etag'] = '"' . md5_file($thumbFile['filePath']) . '"'; |
||
| 175 | } |
||
| 176 | } |
||
| 177 | |||
| 178 | // Cache it. |
||
| 179 | if (!empty($file) || !empty($thumbFile)) |
||
| 180 | cache_put_data('attachment_lookup_id-' . $file['id_attach'], array($file, $thumbFile), mt_rand(850, 900)); |
||
| 181 | } |
||
| 182 | |||
| 183 | // Replace the normal file with its thumbnail if it has one! |
||
| 184 | if (!empty($showThumb) && !empty($thumbFile)) |
||
| 185 | $file = $thumbFile; |
||
| 186 | |||
| 187 | // No point in a nicer message, because this is supposed to be an attachment anyway... |
||
| 188 | View Code Duplication | if (!file_exists($file['filePath'])) |
|
| 189 | { |
||
| 190 | header((preg_match('~HTTP/1\.[01]~i', $_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0') . ' 404 Not Found'); |
||
| 191 | header('Content-Type: text/plain; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); |
||
| 192 | |||
| 193 | // We need to die like this *before* we send any anti-caching headers as below. |
||
| 194 | die('File not found.'); |
||
| 195 | } |
||
| 196 | |||
| 197 | // If it hasn't been modified since the last time this attachment was retrieved, there's no need to display it again. |
||
| 198 | View Code Duplication | if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) |
|
| 199 | { |
||
| 200 | list($modified_since) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE']); |
||
| 201 | if (strtotime($modified_since) >= filemtime($file['filePath'])) |
||
| 202 | { |
||
| 203 | ob_end_clean(); |
||
| 204 | |||
| 205 | // Answer the question - no, it hasn't been modified ;). |
||
| 206 | header('HTTP/1.1 304 Not Modified'); |
||
| 207 | exit; |
||
| 208 | } |
||
| 209 | } |
||
| 210 | |||
| 211 | // Check whether the ETag was sent back, and cache based on that... |
||
| 212 | $eTag = '"' . substr($_REQUEST['attach'] . $file['filePath'] . filemtime($file['filePath']), 0, 64) . '"'; |
||
| 213 | View Code Duplication | if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) && strpos($_SERVER['HTTP_IF_NONE_MATCH'], $eTag) !== false) |
|
| 214 | { |
||
| 215 | ob_end_clean(); |
||
| 216 | |||
| 217 | header('HTTP/1.1 304 Not Modified'); |
||
| 218 | exit; |
||
| 219 | } |
||
| 220 | |||
| 221 | // If this is a partial download, we need to determine what data range to send |
||
| 222 | $range = 0; |
||
| 223 | $size = filesize($file['filePath']); |
||
| 224 | if (isset($_SERVER['HTTP_RANGE'])) |
||
| 225 | { |
||
| 226 | list($a, $range) = explode("=", $_SERVER['HTTP_RANGE'], 2); |
||
| 227 | list($range) = explode(",", $range, 2); |
||
| 228 | list($range, $range_end) = explode("-", $range); |
||
| 229 | $range = intval($range); |
||
| 230 | $range_end = !$range_end ? $size - 1 : intval($range_end); |
||
| 231 | $new_length = $range_end - $range + 1; |
||
| 232 | } |
||
| 233 | |||
| 234 | // Update the download counter (unless it's a thumbnail or resuming an incomplete download). |
||
| 235 | if ($file['attachment_type'] != 3 && empty($showThumb) && $range === 0) |
||
| 236 | $smcFunc['db_query']('attach_download_increase', ' |
||
| 237 | UPDATE LOW_PRIORITY {db_prefix}attachments |
||
| 238 | SET downloads = downloads + 1 |
||
| 239 | WHERE id_attach = {int:id_attach}', |
||
| 240 | array( |
||
| 241 | 'id_attach' => $attachId, |
||
| 242 | ) |
||
| 243 | ); |
||
| 244 | |||
| 245 | // Send the attachment headers. |
||
| 246 | header('Pragma: '); |
||
| 247 | |||
| 248 | if (!isBrowser('gecko')) |
||
| 249 | header('Content-Transfer-Encoding: binary'); |
||
| 250 | |||
| 251 | header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT'); |
||
| 252 | header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($file['filePath'])) . ' GMT'); |
||
| 253 | header('Accept-Ranges: bytes'); |
||
| 254 | header('Connection: close'); |
||
| 255 | header('ETag: ' . $eTag); |
||
| 256 | |||
| 257 | // Make sure the mime type warrants an inline display. |
||
| 258 | if (isset($_REQUEST['image']) && !empty($file['mime_type']) && strpos($file['mime_type'], 'image/') !== 0) |
||
| 259 | unset($_REQUEST['image']); |
||
| 260 | |||
| 261 | // Does this have a mime type? |
||
| 262 | elseif (!empty($file['mime_type']) && (isset($_REQUEST['image']) || !in_array($file['fileext'], array('jpg', 'gif', 'jpeg', 'x-ms-bmp', 'png', 'psd', 'tiff', 'iff')))) |
||
| 263 | header('Content-Type: ' . strtr($file['mime_type'], array('image/bmp' => 'image/x-ms-bmp'))); |
||
| 264 | |||
| 265 | View Code Duplication | else |
|
| 266 | { |
||
| 267 | header('Content-Type: ' . (isBrowser('ie') || isBrowser('opera') ? 'application/octetstream' : 'application/octet-stream')); |
||
| 268 | if (isset($_REQUEST['image'])) |
||
| 269 | unset($_REQUEST['image']); |
||
| 270 | } |
||
| 271 | |||
| 272 | // Convert the file to UTF-8, cuz most browsers dig that. |
||
| 273 | $utf8name = !$context['utf8'] && function_exists('iconv') ? iconv($context['character_set'], 'UTF-8', $file['filename']) : (!$context['utf8'] && function_exists('mb_convert_encoding') ? mb_convert_encoding($file['filename'], 'UTF-8', $context['character_set']) : $file['filename']); |
||
| 274 | $disposition = !isset($_REQUEST['image']) ? 'attachment' : 'inline'; |
||
| 275 | |||
| 276 | // Different browsers like different standards... |
||
| 277 | View Code Duplication | if (isBrowser('firefox')) |
|
| 278 | header('Content-Disposition: ' . $disposition . '; filename*=UTF-8\'\'' . rawurlencode(preg_replace_callback('~&#(\d{3,8});~', 'fixchar__callback', $utf8name))); |
||
| 279 | |||
| 280 | elseif (isBrowser('opera')) |
||
| 281 | header('Content-Disposition: ' . $disposition . '; filename="' . preg_replace_callback('~&#(\d{3,8});~', 'fixchar__callback', $utf8name) . '"'); |
||
| 282 | |||
| 283 | elseif (isBrowser('ie')) |
||
| 284 | header('Content-Disposition: ' . $disposition . '; filename="' . urlencode(preg_replace_callback('~&#(\d{3,8});~', 'fixchar__callback', $utf8name)) . '"'); |
||
| 285 | |||
| 286 | else |
||
| 287 | header('Content-Disposition: ' . $disposition . '; filename="' . $utf8name . '"'); |
||
| 288 | |||
| 289 | // If this has an "image extension" - but isn't actually an image - then ensure it isn't cached cause of silly IE. |
||
| 290 | View Code Duplication | if (!isset($_REQUEST['image']) && in_array($file['fileext'], array('gif', 'jpg', 'bmp', 'png', 'jpeg', 'tiff'))) |
|
| 291 | header('Cache-Control: no-cache'); |
||
| 292 | |||
| 293 | else |
||
| 294 | header('Cache-Control: max-age=' . (525600 * 60) . ', private'); |
||
| 295 | |||
| 296 | // Multipart and resuming support |
||
| 297 | if (isset($_SERVER['HTTP_RANGE'])) |
||
| 298 | { |
||
| 299 | header("HTTP/1.1 206 Partial Content"); |
||
| 300 | header("Content-Length: $new_length"); |
||
| 301 | header("Content-Range: bytes $range-$range_end/$size"); |
||
| 302 | } |
||
| 303 | else |
||
| 304 | header("Content-Length: " . $size); |
||
| 305 | |||
| 306 | |||
| 307 | // Try to buy some time... |
||
| 308 | @set_time_limit(600); |
||
| 309 | |||
| 310 | // For multipart/resumable downloads, send the requested chunk(s) of the file |
||
| 311 | if (isset($_SERVER['HTTP_RANGE'])) |
||
| 312 | { |
||
| 313 | while (@ob_get_level() > 0) |
||
| 314 | @ob_end_clean(); |
||
| 315 | |||
| 316 | // 40 kilobytes is a good-ish amount |
||
| 317 | $chunksize = 40 * 1024; |
||
| 318 | $bytes_sent = 0; |
||
| 319 | |||
| 320 | $fp = fopen($file['filePath'], 'rb'); |
||
| 321 | |||
| 322 | fseek($fp, $range); |
||
| 323 | |||
| 324 | while (!feof($fp) && (!connection_aborted()) && ($bytes_sent < $new_length)) |
||
| 325 | { |
||
| 326 | $buffer = fread($fp, $chunksize); |
||
| 327 | echo($buffer); |
||
| 328 | flush(); |
||
| 329 | $bytes_sent += strlen($buffer); |
||
| 330 | } |
||
| 331 | fclose($fp); |
||
| 332 | } |
||
| 333 | |||
| 334 | // Since we don't do output compression for files this large... |
||
| 335 | elseif ($size > 4194304) |
||
| 336 | { |
||
| 337 | // Forcibly end any output buffering going on. |
||
| 338 | while (@ob_get_level() > 0) |
||
| 339 | @ob_end_clean(); |
||
| 340 | |||
| 341 | $fp = fopen($file['filePath'], 'rb'); |
||
| 342 | while (!feof($fp)) |
||
| 343 | { |
||
| 344 | echo fread($fp, 8192); |
||
| 345 | flush(); |
||
| 346 | } |
||
| 347 | fclose($fp); |
||
| 348 | } |
||
| 349 | |||
| 350 | // On some of the less-bright hosts, readfile() is disabled. It's just a faster, more byte safe, version of what's in the if. |
||
| 351 | elseif (@readfile($file['filePath']) === null) |
||
| 352 | echo file_get_contents($file['filePath']); |
||
| 353 | |||
| 354 | die(); |
||
| 355 | } |
||
| 356 | |||
| 357 | ?> |
This check looks for calls to
isset(...)orempty()on variables that are yet undefined. These calls will always produce the same result and can be removed.This is most likely caused by the renaming of a variable or the removal of a function/method parameter.