Passed
Push — patch_1-1-9 ( 70f5ab...f2626d )
by Spuds
21:21 queued 15:06
created

Attachment_Controller::_send_headers()   C

Complexity

Conditions 14
Paths 20

Size

Total Lines 78
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 14
eloc 36
dl 0
loc 78
rs 6.2666
c 1
b 0
f 0
nc 20
nop 8

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * Attachment display.
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
13
 *
14
 * @version 1.1.9
15
 *
16
 */
17
18
use ElkArte\Errors\AttachmentErrorContext;
19
20
/**
21
 * Attachment_Controller class.
22
 *
23
 * What it does:
24
 *
25
 * - Handles the downloading of an attachment or avatar
26
 * - Handles the uploading of attachments via Ajax
27
 * - increments the download count where applicable
28
 *
29
 * @package Attachments
30
 */
31
class Attachment_Controller extends Action_Controller
32
{
33
	/**
34
	 * {@inheritdoc }
35
	 */
36
	public function needTheme($action = '')
37
	{
38
		global $modSettings, $user_info, $maintenance;
39
40
		// If guests are not allowed to browse and the use is a guest... kick him!
41
		if (empty($modSettings['allow_guestAccess']) && $user_info['is_guest'])
42
		{
43
			return true;
44
		}
45
46
		// If not in maintenance or allowed to use the forum in maintenance
47
		if (empty($maintenance) || allowedTo('admin_forum'))
48
		{
49
			$sa = $this->_req->get('sa');
50
51
			return ($sa === 'ulattach' || $sa === 'rmattach') ? true : false;
52
		}
53
		// else... politely kick him (or her).
54
		else
55
		{
56
			return true;
57
		}
58
	}
59
60
	/**
61
	 * {@inheritdoc }
62
	 */
63
	public function trackStats($action = '')
64
	{
65
		return false;
66
	}
67
68
	/**
69
	 * The default action is to download an attachment.
70
	 * This allows ?action=attachment to be forwarded to action_dlattach()
71
	 */
72
	public function action_index()
73
	{
74
		// add an subaction array to act accordingly
75
		$subActions = array(
76
			'dlattach' => array($this, 'action_dlattach'),
77
			'tmpattach' => array($this, 'action_tmpattach'),
78
			'ulattach' => array($this, 'action_ulattach'),
79
			'rmattach' => array($this, 'action_rmattach'),
80
		);
81
82
		// Setup the action handler
83
		$action = new Action('attachments');
84
		$subAction = $action->initialize($subActions, 'dlattach');
85
86
		// Call the action
87
		$action->dispatch($subAction);
88
	}
89
90
	/**
91
	 * Function to upload attachments via ajax calls
92
	 *
93
	 * - Currently called by drag drop attachment functionality
94
	 * - Pass the form data with session vars
95
	 * - Responds back with errors or file data
96
	 */
97
	public function action_ulattach()
98
	{
99
		global $context, $modSettings, $txt;
100
101
		$resp_data = array();
102
		loadLanguage('Errors');
103
		$context['attachments']['can']['post'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_attachments')));
104
105
		// Set up the template details
106
		$template_layers = Template_Layers::instance();
107
		$template_layers->removeAll();
108
		loadTemplate('Json');
109
		$context['sub_template'] = 'send_json';
110
111
		// Make sure the session is still valid
112
		if (checkSession('request', '', false) != '')
113
		{
114
			$context['json_data'] = array('result' => false, 'data' => $txt['session_timeout_file_upload']);
115
			return false;
116
		}
117
118
		// We should have files, otherwise why are we here?
119
		if (isset($_FILES['attachment']))
120
		{
121
			loadLanguage('Post');
122
123
			$attach_errors = AttachmentErrorContext::context();
124
			$attach_errors->activate();
125
126
			if ($context['attachments']['can']['post'] && empty($this->_req->post->from_qr))
127
			{
128
				require_once(SUBSDIR . '/Attachments.subs.php');
129
130
				$process = $this->_req->getPost('msg', 'intval', '');
131
				processAttachments($process);
132
			}
133
134
			// Any mistakes?
135
			if ($attach_errors->hasErrors())
136
			{
137
				$errors = $attach_errors->prepareErrors();
138
139
				// Bad news for you, the attachments did not process, lets tell them why
140
				foreach ($errors as $key => $error)
141
				{
142
					$resp_data[] = $error;
143
				}
144
145
				$context['json_data'] = array('result' => false, 'data' => $resp_data);
146
			}
147
			// No errors, lets get the details of what we have for our response back
148
			else
149
			{
150
				foreach ($_SESSION['temp_attachments'] as $attachID => $val)
151
				{
152
					// We need to grab the name anyhow
153
					if (!empty($val['tmp_name']))
154
					{
155
						$resp_data = array(
156
							'name' => $val['name'],
157
							'attachid' => $val['public_attachid'],
158
							'size' => $val['size']
159
						);
160
					}
161
				}
162
163
				$context['json_data'] = array('result' => true, 'data' => $resp_data);
164
			}
165
		}
166
		// Could not find the files you claimed to have sent
167
		else
168
			$context['json_data'] = array('result' => false, 'data' => $txt['no_files_uploaded']);
169
	}
170
171
	/**
172
	 * Function to remove attachments which were added via ajax calls
173
	 *
174
	 * What it does:
175
	 *
176
	 * - Currently called by drag drop attachment functionality
177
	 * - Requires file name and file path
178
	 * - Responds back with success or error
179
	 */
180
	public function action_rmattach()
181
	{
182
		global $context, $txt, $user_info;
183
184
		// Prepare the template so we can respond with json
185
		$template_layers = Template_Layers::instance();
186
		$template_layers->removeAll();
187
		loadTemplate('Json');
188
		$context['sub_template'] = 'send_json';
189
190
		// Make sure the session is valid
191
		if (checkSession('request', '', false) !== '')
192
		{
193
			loadLanguage('Errors');
194
			$context['json_data'] = array('result' => false, 'data' => $txt['session_timeout']);
195
196
			return false;
197
		}
198
199
		// We need a filename and path or we are not going any further
200
		if (isset($this->_req->post->attachid))
201
		{
202
			$result = false;
203
			if (!empty($_SESSION['temp_attachments']))
204
			{
205
				require_once(SUBSDIR . '/Attachments.subs.php');
206
207
				$attachId = getAttachmentIdFromPublic($this->_req->post->attachid);
208
209
				$result = removeTempAttachById($attachId);
210
				if ($result === true)
211
				{
212
					$context['json_data'] = array('result' => true);
213
				}
214
			}
215
216
			if ($result !== true)
217
			{
218
				require_once(SUBSDIR . '/ManageAttachments.subs.php');
219
				$attachId = $this->_req->getPost('attachid', 'intval');
220
				if (canRemoveAttachment($attachId, $user_info['id']))
221
				{
222
					$result_tmp = removeAttachments(array('id_attach' => $attachId), '', true);
223
					if (!empty($result_tmp))
224
					{
225
						$context['json_data'] = array('result' => true);
226
						$result = true;
227
					}
228
					else
229
					{
230
						$result = $result_tmp;
231
					}
232
				}
233
			}
234
235
			if ($result !== true)
236
			{
237
				loadLanguage('Errors');
238
				$context['json_data'] = array('result' => false, 'data' => $txt[!empty($result) ? $result : 'attachment_not_found']);
239
			}
240
		}
241
		else
242
		{
243
			loadLanguage('Errors');
244
			$context['json_data'] = array('result' => false, 'data' => $txt['attachment_not_found']);
245
		}
246
	}
247
248
	/**
249
	 * Generates a language image based on text for display.
250
	 *
251
	 * @param null|string $text
252
	 * @throws \Elk_Exception
253
	 */
254
	public function action_no_attach($text = null)
255
	{
256
		global $txt;
257
258
		require_once(SUBSDIR . '/Graphics.subs.php');
259
		if ($text === null)
260
		{
261
			loadLanguage('Errors');
262
			$text = $txt['attachment_not_found'];
263
		}
264
265
		$this->_send_headers('no_image', 'no_image', 'image/png', false, 'inline', 'no_image.png', true, false);
266
267
		$img = generateTextImage($text, 200);
268
269
		if ($img === false)
270
		{
271
			throw new Elk_Exception('no_access', false);
272
		}
273
274
		echo $img;
275
		obExit(false);
276
	}
277
278
	/**
279
	 * Downloads an attachment or avatar, and increments the download count.
280
	 *
281
	 * What it does:
282
	 *
283
	 * - It requires the view_attachments permission. (not for avatars!)
284
	 * - It disables the session parser, and clears any previous output.
285
	 * - It is accessed via the query string ?action=dlattach.
286
	 * - Views to attachments and avatars do not increase hits and are not logged
287
	 *   in the "Who's Online" log.
288
	 */
289
	public function action_dlattach()
290
	{
291
		global $modSettings, $user_info, $context, $topic, $board, $settings, $txt;
292
293
		// Some defaults that we need.
294
		$context['no_last_modified'] = true;
295
		$filename = null;
296
297
		// Make sure some attachment was requested!
298
		if (!isset($this->_req->query->attach))
299
		{
300
			if (!isset($this->_req->query->id))
301
				$this->action_no_attach();
302
303
			if ($this->_req->query->id === 'ila')
304
			{
305
				loadLanguage('index');
306
				$this->action_no_attach($txt['awaiting_approval']);
307
			}
308
		}
309
310
		// We need to do some work on attachments and avatars.
311
		require_once(SUBSDIR . '/Attachments.subs.php');
312
313
		// Temporary attachment, special case...
314
		if (isset($this->_req->query->attach) && strpos($this->_req->query->attach, 'post_tmp_' . $user_info['id'] . '_') !== false)
315
		{
316
			$this->action_tmpattach();
317
			return;
318
		}
319
		else
320
		{
321
			$id_attach = isset($this->_req->query->attach)
322
				? (int) $this->_req->query->attach
323
				: (int) $this->_req->query->id;
324
		}
325
326
		if ($this->_req->getQuery('type') === 'avatar')
327
		{
328
			$attachment = getAvatar($id_attach);
329
330
			$is_avatar = true;
331
			$this->_req->query->image = true;
332
		}
333
		// This is just a regular attachment...
334
		else
335
		{
336
			if (empty($topic) && !empty($id_attach))
337
			{
338
				$id_board = 0;
339
				$id_topic = 0;
340
				$attachPos = getAttachmentPosition($id_attach);
341
				if ($attachPos !== false)
342
				{
343
					list($id_board, $id_topic) = array_values($attachPos);
0 ignored issues
show
Bug introduced by
It seems like $attachPos can also be of type true; however, parameter $array of array_values() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

343
					list($id_board, $id_topic) = array_values(/** @scrutinizer ignore-type */ $attachPos);
Loading history...
344
				}
345
			}
346
			else
347
			{
348
				$id_board = $board;
349
				$id_topic = $topic;
350
			}
351
352
			isAllowedTo('view_attachments', $id_board);
353
354
			if ($this->_req->getQuery('thumb') === null)
355
			{
356
				$attachment = getAttachmentFromTopic($id_attach, $id_topic);
357
			}
358
			else
359
			{
360
				$this->_req->query->image = true;
361
				$attachment = getAttachmentThumbFromTopic($id_attach, $id_topic);
362
363
				// 1 is the file name, no file name, no thumbnail, no image.
364
				if (empty($attachment[1]))
365
				{
366
					$full_attach = getAttachmentFromTopic($id_attach, $id_topic);
367
					$attachment[1] = !empty($full_attach[1]) ? $full_attach[1] : '';
368
					$attachment[4] = 0;
369
					$attachment[5] = 0;
370
					$attachment[7] = $full_attach[7];
371
					$attachment[8] = $full_attach[8];
372
373
					// return mime type ala mimetype extension
374
					$check = returnMimeThumb(!empty($full_attach[3]) ? $full_attach[3] : 'default');
375
376
					if ($check !== false)
0 ignored issues
show
introduced by
The condition $check !== false is always true.
Loading history...
377
					{
378
						$attachment[3] = 'png';
379
						$attachment[6] = 'image/png';
380
						$filename = $check;
381
					}
382
					else
383
					{
384
						if (!is_array($modSettings['attachmentUploadDir']))
385
						{
386
							$attachmentUploadDir = unserialize($modSettings['attachmentUploadDir']);
387
						}
388
						else
389
						{
390
							$attachmentUploadDir = $modSettings['attachmentUploadDir'];
391
						}
392
393
						$filename = $attachmentUploadDir[$modSettings['currentAttachmentUploadDir']] . '/' . $attachment[1];
394
					}
395
396
					if (substr(get_finfo_mime($filename), 0, 5) !== 'image')
0 ignored issues
show
Bug introduced by
It seems like get_finfo_mime($filename) can also be of type boolean; however, parameter $string of substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

396
					if (substr(/** @scrutinizer ignore-type */ get_finfo_mime($filename), 0, 5) !== 'image')
Loading history...
397
					{
398
						$attachment[3] = 'png';
399
						$attachment[6] = 'image/png';
400
						$filename = $settings['theme_dir'] . '/images/mime_images/default.png';
401
					}
402
403
				}
404
			}
405
		}
406
407
		if (empty($attachment))
408
		{
409
			return $this->action_no_attach();
410
		}
411
412
		list ($id_folder, $real_filename, $file_hash, $file_ext, $id_attach, $attachment_type, $mime_type, $is_approved, $id_member) = $attachment;
413
414
		// If it isn't yet approved, do they have permission to view it?
415
		if (!$is_approved && ($id_member == 0 || $user_info['id'] != $id_member) && ($attachment_type == 0 || $attachment_type == 3))
416
			isAllowedTo('approve_posts', $id_board);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $id_board does not seem to be defined for all execution paths leading up to this point.
Loading history...
417
418
		// Update the download counter (unless it's a thumbnail or an avatar).
419
		if (!empty($id_attach) && empty($is_avatar) || $attachment_type != 3)
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! empty($id_attach) && ...| $attachment_type != 3, Probably Intended Meaning: ! empty($id_attach) && (... $attachment_type != 3)
Loading history...
420
		{
421
			increaseDownloadCounter($id_attach);
422
		}
423
424
		if ($filename === null)
425
		{
426
			$filename = getAttachmentFilename($real_filename, $id_attach, $id_folder, false, $file_hash);
427
		}
428
429
		$eTag = '"' . substr($id_attach . $real_filename . @filemtime($filename), 0, 64) . '"';
0 ignored issues
show
Bug introduced by
Are you sure @filemtime($filename) of type false|integer can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

429
		$eTag = '"' . substr($id_attach . $real_filename . /** @scrutinizer ignore-type */ @filemtime($filename), 0, 64) . '"';
Loading history...
430
		$use_compression = $this->useCompression($mime_type);
431
		$disposition = !isset($this->_req->query->image) ? 'attachment' : 'inline';
432
		$do_cache = false === (!isset($this->_req->query->image) && getValidMimeImageType($file_ext) !== '');
433
434
		// Make sure the mime type warrants an inline display.
435
		if (isset($this->_req->query->image) && !empty($mime_type) && strpos($mime_type, 'image/') !== 0)
436
		{
437
			unset($this->_req->query->image);
438
			$mime_type = '';
439
		}
440
		// Does this have a mime type?
441
		elseif (empty($mime_type) || !(isset($this->_req->query->image) || getValidMimeImageType($file_ext) === ''))
442
		{
443
			$mime_type = '';
444
			if (isset($this->_req->query->image))
445
				unset($this->_req->query->image);
446
		}
447
448
		$this->_send_headers($filename, $eTag, $mime_type, $use_compression, $disposition, $real_filename, $do_cache);
449
		$this->send_file($filename, $mime_type);
450
451
		obExit(false);
452
	}
453
454
	/**
455
	 * If the mime type benefits from compression e.g. text/xyz and gzencode is
456
	 * available and the user agent accepts gzip, then return true, else false
457
	 *
458
	 * @param string $mime_type
459
	 * @return bool if we should compress the file
460
	 */
461
	public function useCompression($mime_type)
462
	{
463
		global $modSettings;
464
465
		// Support is available on the server
466
		if (!function_exists('gzencode') || empty($modSettings['enableCompressedOutput']))
467
		{
468
			return false;
469
		}
470
471
		// Not compressible, or not supported / requested by client
472
		if (!preg_match('~^(?:text/|application/(?:json|xml|rss\+xml)$)~i', $mime_type)
473
			|| (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) || strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === false))
474
		{
475
			return false;
476
		}
477
478
		return true;
479
	}
480
481
	/**
482
	 * Sends the requested file to the user.  If the file is compressible e.g.
483
	 * has a mine type of text/??? may compress the file prior to sending.
484
	 *
485
	 * @param string $filename
486
	 * @param string $mime_type
487
	 */
488
	public function send_file($filename, $mime_type)
489
	{
490
		$body = file_get_contents($filename);
491
		$use_compression = $this->useCompression($mime_type);
492
		$length = filesize($filename);
493
494
		// If we can/should compress this file
495
		if ($use_compression && strlen($body) > 250)
496
		{
497
			$body = gzencode($body, 2);
498
			$length = strlen($body);
499
			header('Content-Encoding: gzip');
500
			header('Vary: Accept-Encoding');
501
		}
502
503
		// Someone is getting a present
504
		if (!empty($length))
505
		{
506
			header('Content-Length: ' . $length);
507
		}
508
509
		// Forcibly end any output buffering going on.
510
		while (ob_get_level() > 0)
511
		{
512
			@ob_end_clean();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ob_end_clean(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

512
			/** @scrutinizer ignore-unhandled */ @ob_end_clean();

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
513
		}
514
515
		echo $body;
516
	}
517
518
	/**
519
	 * Simplified version of action_dlattach to send out thumbnails while creating
520
	 * or editing a message.
521
	 */
522
	public function action_tmpattach()
523
	{
524
		global $modSettings, $user_info, $topic;
525
526
		// Make sure some attachment was requested!
527
		if (!isset($this->_req->query->attach))
528
		{
529
			return $this->action_no_attach();
530
		}
531
532
		// We need to do some work on attachments and avatars.
533
		require_once(SUBSDIR . '/Attachments.subs.php');
534
		require_once(SUBSDIR . '/Graphics.subs.php');
535
536
		try
537
		{
538
			if (empty($topic) || (string) (int) $this->_req->query->attach !== (string) $this->_req->query->attach)
539
			{
540
				$attach_data = getTempAttachById($this->_req->query->attach);
541
				$file_ext = pathinfo($attach_data['name'], PATHINFO_EXTENSION);
542
				$filename = $attach_data['tmp_name'];
543
				$id_attach = $attach_data['attachid'];
544
				$real_filename = $attach_data['name'];
545
				$mime_type = $attach_data['type'];
546
			}
547
			else
548
			{
549
				$id_attach = $this->_req->getQuery('attach', 'intval', -1);
550
551
				isAllowedTo('view_attachments');
552
				$attachment = getAttachmentFromTopic($id_attach, $topic);
553
554
				if (empty($attachment))
555
				{
556
					return $this->action_no_attach();
557
				}
558
559
				list ($id_folder, $real_filename, $file_hash, $file_ext, $id_attach, $attachment_type, $mime_type, $is_approved, $id_member) = $attachment;
560
561
				// If it isn't yet approved, do they have permission to view it?
562
				if (!$is_approved && ($id_member == 0 || $user_info['id'] != $id_member) && ($attachment_type == 0 || $attachment_type == 3))
563
					isAllowedTo('approve_posts');
564
565
				$filename = getAttachmentFilename($real_filename, $id_attach, $id_folder, false, $file_hash);
566
			}
567
		}
568
		catch (\Exception $e)
569
		{
570
			throw new Elk_Exception($e->getMessage(), false);
571
		}
572
		$resize = true;
573
574
		// Return mime type ala mimetype extension
575
		if (substr(get_finfo_mime($filename), 0, 5) !== 'image')
0 ignored issues
show
Bug introduced by
It seems like get_finfo_mime($filename) can also be of type boolean; however, parameter $string of substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

575
		if (substr(/** @scrutinizer ignore-type */ get_finfo_mime($filename), 0, 5) !== 'image')
Loading history...
576
		{
577
			$checkMime = returnMimeThumb($file_ext);
578
			$mime_type = 'image/png';
579
			$resize = false;
580
			$filename = $checkMime;
581
		}
582
583
		$eTag = '"' . substr($id_attach . $real_filename . filemtime($filename), 0, 64) . '"';
584
		$use_compression = $this->useCompression($mime_type);
585
		$do_cache = false === (!isset($this->_req->query->image) && getValidMimeImageType($file_ext) !== '');
586
587
		$this->_send_headers($filename, $eTag, $mime_type, $use_compression, 'inline', $real_filename, $do_cache);
588
589
		$max_width = $this->_req->is_set('thumb') && !empty($modSettings['attachmentThumbWidth']) ? $modSettings['attachmentThumbWidth'] : 250;
590
		$max_height = $this->_req->is_set('thumb') && !empty($modSettings['attachmentThumbHeight']) ? $modSettings['attachmentThumbHeight'] : 250;
591
		$format = setDefaultFormat($filename);
592
		if ($resize && resizeImageFile($filename, $filename . '_thumb', $max_width, $max_height, $format, false, false))
593
		{
594
			if (!empty($modSettings['attachment_autorotate']))
595
			{
596
				autoRotateImage($filename . '_thumb');
597
			}
598
599
			$filename = $filename . '_thumb';
600
		}
601
602
		$this->send_file($filename, $mime_type);
603
604
		obExit(false);
605
	}
606
607
	/**
608
	 * Takes care of sending out the most common headers.
609
	 *
610
	 * @param string $filename Full path+file name of the file in the filesystem
611
	 * @param string $eTag ETag cache validator
612
	 * @param string $mime_type The mime-type of the file
613
	 * @param boolean $use_compression If use gzip compression - Deprecated since 1.1.9
614
	 * @param string $disposition The value of the Content-Disposition header
615
	 * @param string $real_filename The original name of the file
616
	 * @param boolean $do_cache If send the a max-age header or not
617
	 * @param boolean $check_filename When false, any check on $filename is skipped
618
	 */
619
	protected function _send_headers($filename, $eTag, $mime_type, $use_compression, $disposition, $real_filename, $do_cache, $check_filename = true)
0 ignored issues
show
Unused Code introduced by
The parameter $use_compression is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

619
	protected function _send_headers($filename, $eTag, $mime_type, /** @scrutinizer ignore-unused */ $use_compression, $disposition, $real_filename, $do_cache, $check_filename = true)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
620
	{
621
		global $txt;
622
623
		// No point in a nicer message, because this is supposed to be an attachment anyway...
624
		if ($check_filename === true && !file_exists($filename))
625
		{
626
			loadLanguage('Errors');
627
628
			header((preg_match('~HTTP/1\.[01]~i', $_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0') . ' 404 Not Found');
629
			header('Content-Type: text/plain; charset=UTF-8');
630
631
			// We need to die like this *before* we send any anti-caching headers as below.
632
			die('404 - ' . $txt['attachment_not_found']);
633
		}
634
635
		// If it hasn't been modified since the last time this attachment was retrieved, there's no need to display it again.
636
		if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']))
637
		{
638
			list ($modified_since) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE']);
639
			if ($check_filename === false || strtotime($modified_since) >= filemtime($filename))
640
			{
641
				@ob_end_clean();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ob_end_clean(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

641
				/** @scrutinizer ignore-unhandled */ @ob_end_clean();

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
642
643
				// Answer the question - no, it hasn't been modified ;).
644
				header('HTTP/1.1 304 Not Modified');
645
				exit;
646
			}
647
		}
648
649
		// Check whether the ETag was sent back, and cache based on that...
650
		if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) && strpos($_SERVER['HTTP_IF_NONE_MATCH'], $eTag) !== false)
651
		{
652
			@ob_end_clean();
653
654
			header('HTTP/1.1 304 Not Modified');
655
			exit;
656
		}
657
658
		// Send the attachment headers.
659
		header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT');
660
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $check_filename === true ? filemtime($filename) : time() - 525600 * 60) . ' GMT');
661
		header('Accept-Ranges: bytes');
662
		header('Connection: close');
663
		header('ETag: ' . $eTag);
664
665
		if (!empty($mime_type) && strpos($mime_type, 'image/') === 0)
666
		{
667
			header('Content-Type: ' . $mime_type);
668
		}
669
		else
670
		{
671
			header('Content-Type: application/octet-stream');
672
		}
673
674
		// Different browsers like different standards...
675
		$filename = str_replace('"', '', $real_filename);
676
677
		// Send as UTF-8 if the name requires that
678
		$altName = '';
679
		if (preg_match('~[\x80-\xFF]~', $filename))
680
			$altName = "; filename*=UTF-8''" . rawurlencode($filename);
681
682
		header('Content-Disposition: ' . $disposition . '; filename="' . $filename . '"' . $altName);
683
684
		// If this has an "image extension" - but isn't actually an image - then ensure it isn't cached cause of silly IE.
685
		if ($do_cache === true)
686
		{
687
			header('Cache-Control: max-age=' . (525600 * 60) . ', private');
688
		}
689
		else
690
		{
691
			header('Pragma: no-cache');
692
			header('Cache-Control: no-cache');
693
		}
694
695
		// Try to buy some time...
696
		detectServer()->setTimeLimit(600);
697
	}
698
}
699