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

Attachment_Controller::action_tmpattach()   F

Complexity

Conditions 20
Paths 592

Size

Total Lines 83
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 20
eloc 44
c 2
b 0
f 0
nc 592
nop 0
dl 0
loc 83
rs 0.5666

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
/**
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