Subversion-Projekte lars-tiefland.ci

Revision

Revision 2115 | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
2115 lars 1
<?php
2116 lars 2
 
2115 lars 3
/*
2116 lars 4
* jQuery File Upload Plugin PHP Class
5
* https://github.com/blueimp/jQuery-File-Upload
6
*
7
* Copyright 2010, Sebastian Tschan
8
* https://blueimp.net
9
*
10
* Licensed under the MIT license:
11
* https://opensource.org/licenses/MIT
12
*/
2115 lars 13
 
2116 lars 14
$GLOBALS["web"] = $webs;
15
$GLOBALS["web_rechte"] = $web_rechte;
16
$GLOBALS["site"] = $site;
17
if (preg_match("/\.local$/", $_SERVER["SERVER_NAME"]))
18
{
19
	//$GLOBALS["site"] .= ".local";
20
}
21
$options = null;
22
 
23
$real_url = getDokDomain();
24
$real_url .= "/images/upload/";
25
$real_folder = $GLOBALS["web"]["verzeichnis"] . "/images/upload/";
26
 
27
$folder = "";
28
$GLOBALS["folder"] = "";
29
if (isset($_POST["folder"]) && $_POST["folder"])
30
{
31
	$folder = Weban_Utils::clean_global_input("folder");
32
} elseif (isset($_GET["folder"]) && $_GET["folder"])
33
{
34
	$folder = Weban_Utils::clean_global_input("folder", "get");
35
}
36
 
37
$bvKonf = "";
38
if (isset($GLOBALS["web_rechte"]["admin"]["toolbox"]["bildverwaltung"]))
39
{
40
	$bvKonf = $GLOBALS["web_rechte"]["admin"]["toolbox"]["bildverwaltung"];
41
}
42
if (!$bvKonf)
43
{
44
	$bvKonf = "Bild;;651;651";
45
}
46
$GLOBALS["Imagedaten"] = array_chunk(explode(";", $bvKonf), 4);
47
if ($folder)
48
{
49
	$folder = rtrim($folder, "/");
50
	$GLOBALS["folder"] = $folder;
51
	$folder .= "/";
52
	$real_folder .= $folder;
53
	$real_url .= $folder;
54
	$options["upload_dir"] = $real_folder;
55
	$options["upload_url"] = $real_url;
56
	foreach ($GLOBALS["Imagedaten"] as $set)
57
	{
58
		$options["image_versions"][$set[0]] = array(
59
			"upload_dir" => $real_folder . $set[1] . "/",
60
			"upload_url" => $real_url . $set[1] . "/",
61
			"max_width" => $set[2],
62
			"max_height" => $set[3],
63
			);
64
	}
65
	if (!file_exists($options["upload_dir"] . "/orig/"))
66
	{
67
		mkdir($options["upload_dir"] . "/orig/", 0755, true);
68
	}
69
}
70
 
2115 lars 71
class UploadHandler
72
{
73
 
2116 lars 74
	protected $options;
2115 lars 75
 
2116 lars 76
	// PHP File Upload error message codes:
77
	// http://php.net/manual/en/features.file-upload.errors.php
78
	protected $error_messages = array(
79
		1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
80
		2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
81
		3 => 'The uploaded file was only partially uploaded',
82
		4 => 'No file was uploaded',
83
		6 => 'Missing a temporary folder',
84
		7 => 'Failed to write file to disk',
85
		8 => 'A PHP extension stopped the file upload',
86
		'post_max_size' => 'The uploaded file exceeds the post_max_size directive in php.ini',
87
		'max_file_size' => 'File is too big',
88
		'min_file_size' => 'File is too small',
89
		'accept_file_types' => 'Filetype not allowed',
90
		'max_number_of_files' => 'Maximum number of files exceeded',
91
		'max_width' => 'Image exceeds maximum width',
92
		'min_width' => 'Image requires a minimum width',
93
		'max_height' => 'Image exceeds maximum height',
94
		'min_height' => 'Image requires a minimum height',
95
		'abort' => 'File upload aborted',
96
		'image_resize' => 'Failed to resize image');
2115 lars 97
 
2116 lars 98
	protected $image_objects = array();
2115 lars 99
 
2116 lars 100
	public function __construct($options = null, $initialize = true, $error_messages = null)
101
	{
102
		$this->response = array();
103
		$this->options = array(
104
			'script_url' => $this->get_full_url() . '/' . $this->basename($this->
105
				get_server_var('SCRIPT_NAME')),
106
			'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')) . '/files/',
107
			'upload_url' => $this->get_full_url() . '/files/',
108
			'input_stream' => 'php://input',
109
			'user_dirs' => false,
110
			'mkdir_mode' => 0755,
111
			'param_name' => 'files',
112
			// Set the following option to 'POST', if your server does not support
113
			// DELETE requests. This is a parameter sent to the client:
114
			'delete_type' => 'DELETE',
115
			'access_control_allow_origin' => '*',
116
			'access_control_allow_credentials' => false,
117
			'access_control_allow_methods' => array(
118
				'OPTIONS',
119
				'HEAD',
120
				'GET',
121
				'POST',
122
				'PUT',
123
				'PATCH',
124
				'DELETE'),
125
			'access_control_allow_headers' => array(
126
				'Content-Type',
127
				'Content-Range',
128
				'Content-Disposition'),
129
			// By default, allow redirects to the referer protocol+host:
130
			'redirect_allow_target' => '/^' . preg_quote(parse_url($this->get_server_var('HTTP_REFERER'),
131
				PHP_URL_SCHEME) . '://' . parse_url($this->get_server_var('HTTP_REFERER'),
132
				PHP_URL_HOST) . '/', // Trailing slash to not match subdomains by mistake
133
				'/' // preg_quote delimiter param
134
				) . '/',
135
			// Enable to provide file downloads via GET requests to the PHP script:
136
			//     1. Set to 1 to download files via readfile method through PHP
137
			//     2. Set to 2 to send a X-Sendfile header for lighttpd/Apache
138
			//     3. Set to 3 to send a X-Accel-Redirect header for nginx
139
			// If set to 2 or 3, adjust the upload_url option to the base path of
140
			// the redirect parameter, e.g. '/files/'.
141
			'download_via_php' => false,
142
			// Read files in chunks to avoid memory limits when download_via_php
143
			// is enabled, set to 0 to disable chunked reading of files:
144
			'readfile_chunk_size' => 10 * 1024 * 1024, // 10 MiB
145
			// Defines which files can be displayed inline when downloaded:
146
			'inline_file_types' => '/\.(gif|jpe?g|png)$/i',
147
			// Defines which files (based on their names) are accepted for upload:
148
			'accept_file_types' => '/.+$/i',
149
			// The php.ini settings upload_max_filesize and post_max_size
150
			// take precedence over the following max_file_size setting:
151
			'max_file_size' => null,
152
			'min_file_size' => 1,
153
			// The maximum number of files for the upload directory:
154
			'max_number_of_files' => null,
155
			// Defines which files are handled as image files:
156
			'image_file_types' => '/\.(gif|jpe?g|png)$/i',
157
			// Use exif_imagetype on all files to correct file extensions:
158
			'correct_image_extensions' => false,
159
			// Image resolution restrictions:
160
			'max_width' => null,
161
			'max_height' => null,
162
			'min_width' => 1,
163
			'min_height' => 1,
164
			// Set the following option to false to enable resumable uploads:
165
			'discard_aborted_uploads' => true,
166
			// Set to 0 to use the GD library to scale and orient images,
167
			// set to 1 to use imagick (if installed, falls back to GD),
168
			// set to 2 to use the ImageMagick convert binary directly:
169
			'image_library' => 1,
170
			// Uncomment the following to define an array of resource limits
171
			// for imagick:
172
			/*
173
			'imagick_resource_limits' => array(
174
			imagick::RESOURCETYPE_MAP => 32,
175
			imagick::RESOURCETYPE_MEMORY => 32
176
			),
177
			*/
178
			// Command or path for to the ImageMagick convert binary:
179
			'convert_bin' => 'convert',
180
			// Uncomment the following to add parameters in front of each
181
			// ImageMagick convert call (the limit constraints seem only
182
			// to have an effect if put in front):
183
			/*
184
			'convert_params' => '-limit memory 32MiB -limit map 32MiB',
185
			*/
186
			// Command or path for to the ImageMagick identify binary:
187
			'identify_bin' => 'identify',
188
			'print_response' => true);
189
		foreach ($GLOBALS["Imagedaten"] as $set)
190
		{
191
			$this->options["image_versions"][$set[0]] = array(
192
				"upload_dir" => $real_folder . $set[1] . "/",
193
				"upload_url" => $real_url . $set[1] . "/",
194
				"max_width" => $set[2],
195
				"max_height" => $set[3],
196
				);
197
		}
198
		if ($options)
199
		{
200
			$this->options = $options + $this->options;
201
		}
202
		if ($error_messages)
203
		{
204
			$this->error_messages = $error_messages + $this->error_messages;
205
		}
206
		if ($initialize)
207
		{
208
			$this->initialize();
209
		}
210
	}
2115 lars 211
 
2116 lars 212
	protected function initialize()
213
	{
214
		switch ($this->get_server_var('REQUEST_METHOD'))
215
		{
216
			case 'OPTIONS':
217
			case 'HEAD':
218
				$this->head();
219
				break;
220
			case 'GET':
221
				$this->get($this->options['print_response']);
222
				break;
223
			case 'PATCH':
224
			case 'PUT':
225
			case 'POST':
226
				$this->post($this->options['print_response']);
227
				break;
228
			case 'DELETE':
229
				$this->delete($this->options['print_response']);
230
				break;
231
			default:
232
				$this->header('HTTP/1.1 405 Method Not Allowed');
233
		}
234
	}
2115 lars 235
 
2116 lars 236
	protected function get_full_url()
237
	{
238
		$https = !empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'on') === 0 ||
239
			!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'],
240
			'https') === 0;
241
		return ($https ? 'https://' : 'http://') . (!empty($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'] .
242
			'@' : '') . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME'] .
243
			($https && $_SERVER['SERVER_PORT'] === 443 || $_SERVER['SERVER_PORT'] === 80 ?
244
			'' : ':' . $_SERVER['SERVER_PORT']))) . substr($_SERVER['SCRIPT_NAME'], 0,
245
			strrpos($_SERVER['SCRIPT_NAME'], '/'));
246
	}
2115 lars 247
 
2116 lars 248
	protected function get_user_id()
249
	{
250
		@session_start();
251
		return session_id();
252
	}
2115 lars 253
 
2116 lars 254
	protected function get_user_path()
255
	{
256
		if ($this->options['user_dirs'])
257
		{
258
			return $this->get_user_id() . '/';
259
		}
260
		return '';
261
	}
2115 lars 262
 
2116 lars 263
	protected function get_upload_path($file_name = null, $version = null)
264
	{
265
		$file_name = $file_name ? $file_name : '';
266
		if (empty($version))
267
		{
268
			$version_path = '';
269
		} else
270
		{
271
			$version_dir = @$this->options['image_versions'][$version]['upload_dir'];
272
			if ($version_dir)
273
			{
274
				return $version_dir . $this->get_user_path() . $file_name;
275
			}
276
			$version_path = $version . '/';
277
		}
278
		return $this->options['upload_dir'] . $this->get_user_path() . $version_path . $file_name;
279
	}
2115 lars 280
 
2116 lars 281
	protected function get_query_separator($url)
282
	{
283
		return strpos($url, '?') === false ? '?' : '&';
284
	}
2115 lars 285
 
2116 lars 286
	protected function get_download_url($file_name, $version = null, $direct = false)
287
	{
288
		if (!$direct && $this->options['download_via_php'])
289
		{
290
			$url = $this->options['script_url'] . $this->get_query_separator($this->options['script_url']) .
291
				$this->get_singular_param_name() . '=' . rawurlencode($file_name);
292
			if ($version)
293
			{
294
				$url .= '&version=' . rawurlencode($version);
295
			}
296
			return $url . '&download=1';
297
		}
298
		if (empty($version))
299
		{
300
			$version_path = '';
301
		} else
302
		{
303
			$version_url = @$this->options['image_versions'][$version]['upload_url'];
304
			if ($version_url)
305
			{
306
				return $version_url . $this->get_user_path() . rawurlencode($file_name);
307
			}
308
			$version_path = rawurlencode($version) . '/';
309
		}
310
		return $this->options['upload_url'] . $this->get_user_path() . $version_path .
311
			rawurlencode($file_name);
312
	}
2115 lars 313
 
2116 lars 314
	protected function set_additional_file_properties($file)
315
	{
316
		$file->deleteUrl = $this->options['script_url'] . $this->get_query_separator($this->
317
			options['script_url']) . $this->get_singular_param_name() . '=' . rawurlencode($file->
318
			name);
319
		$file->deleteType = $this->options['delete_type'];
320
		if ($file->deleteType !== 'DELETE')
321
		{
322
			$file->deleteUrl .= '&_method=DELETE';
323
		}
324
		if ($this->options['access_control_allow_credentials'])
325
		{
326
			$file->deleteWithCredentials = true;
327
		}
328
	}
2115 lars 329
 
2116 lars 330
	// Fix for overflowing signed 32 bit integers,
331
	// works for sizes up to 2^32-1 bytes (4 GiB - 1):
332
	protected function fix_integer_overflow($size)
333
	{
334
		if ($size < 0)
335
		{
336
			$size += 2.0 * (PHP_INT_MAX + 1);
337
		}
338
		return $size;
339
	}
2115 lars 340
 
2116 lars 341
	protected function get_file_size($file_path, $clear_stat_cache = false)
342
	{
343
		if ($clear_stat_cache)
344
		{
345
			if (version_compare(PHP_VERSION, '5.3.0') >= 0)
346
			{
347
				clearstatcache(true, $file_path);
348
			} else
349
			{
350
				clearstatcache();
351
			}
352
		}
353
		return $this->fix_integer_overflow(filesize($file_path));
354
	}
2115 lars 355
 
2116 lars 356
	protected function is_valid_file_object($file_name)
357
	{
358
		$file_path = $this->get_upload_path($file_name);
359
		if (is_file($file_path) && $file_name[0] !== '.')
360
		{
361
			return true;
362
		}
363
		return false;
364
	}
2115 lars 365
 
2116 lars 366
	protected function get_file_object($file_name)
367
	{
368
		if ($this->is_valid_file_object($file_name))
369
		{
370
			$file = new \stdClass();
371
			$file->name = $file_name;
372
			$file->size = $this->get_file_size($this->get_upload_path($file_name));
373
			$file->url = $this->get_download_url($file->name);
374
			foreach ($this->options['image_versions'] as $version => $options)
375
			{
376
				if (!empty($version))
377
				{
378
					if (is_file($this->get_upload_path($file_name, $version)))
379
					{
380
						$file->{$version . 'Url'} = $this->get_download_url($file->name, $version);
381
					}
382
				}
383
			}
384
			$this->set_additional_file_properties($file);
385
			return $file;
386
		}
387
		return null;
388
	}
2115 lars 389
 
2116 lars 390
	protected function get_file_objects($iteration_method = 'get_file_object')
391
	{
392
		$upload_dir = $this->get_upload_path();
393
		if (!is_dir($upload_dir))
394
		{
395
			return array();
396
		}
397
		return array_values(array_filter(array_map(array($this, $iteration_method),
398
			scandir($upload_dir))));
399
	}
2115 lars 400
 
2116 lars 401
	protected function count_file_objects()
402
	{
403
		return count($this->get_file_objects('is_valid_file_object'));
404
	}
2115 lars 405
 
2116 lars 406
	protected function get_error_message($error)
407
	{
408
		return isset($this->error_messages[$error]) ? $this->error_messages[$error] : $error;
409
	}
2115 lars 410
 
2116 lars 411
	public function get_config_bytes($val)
412
	{
413
		$val = trim($val);
414
		$last = strtolower($val[strlen($val) - 1]);
415
		$val = (int)$val;
416
		switch ($last)
417
		{
418
			case 'g':
419
				$val *= 1024;
420
			case 'm':
421
				$val *= 1024;
422
			case 'k':
423
				$val *= 1024;
424
		}
425
		return $this->fix_integer_overflow($val);
426
	}
2115 lars 427
 
2116 lars 428
	protected function validate($uploaded_file, $file, $error, $index)
429
	{
430
		if ($error)
431
		{
432
			$file->error = $this->get_error_message($error);
433
			return false;
434
		}
435
		$content_length = $this->fix_integer_overflow((int)$this->get_server_var('CONTENT_LENGTH'));
436
		$post_max_size = $this->get_config_bytes(ini_get('post_max_size'));
437
		if ($post_max_size && ($content_length > $post_max_size))
438
		{
439
			$file->error = $this->get_error_message('post_max_size');
440
			return false;
441
		}
442
		if (!preg_match($this->options['accept_file_types'], $file->name))
443
		{
444
			$file->error = $this->get_error_message('accept_file_types');
445
			return false;
446
		}
447
		if ($uploaded_file && is_uploaded_file($uploaded_file))
448
		{
449
			$file_size = $this->get_file_size($uploaded_file);
450
		} else
451
		{
452
			$file_size = $content_length;
453
		}
454
		if ($this->options['max_file_size'] && ($file_size > $this->options['max_file_size'] ||
455
			$file->size > $this->options['max_file_size']))
456
		{
457
			$file->error = $this->get_error_message('max_file_size');
458
			return false;
459
		}
460
		if ($this->options['min_file_size'] && $file_size < $this->options['min_file_size'])
461
		{
462
			$file->error = $this->get_error_message('min_file_size');
463
			return false;
464
		}
465
		if (is_int($this->options['max_number_of_files']) && ($this->count_file_objects
466
			() >= $this->options['max_number_of_files']) &&
467
			// Ignore additional chunks of existing files:
468
			!is_file($this->get_upload_path($file->name)))
469
		{
470
			$file->error = $this->get_error_message('max_number_of_files');
471
			return false;
472
		}
473
		$max_width = @$this->options['max_width'];
474
		$max_height = @$this->options['max_height'];
475
		$min_width = @$this->options['min_width'];
476
		$min_height = @$this->options['min_height'];
477
		if (($max_width || $max_height || $min_width || $min_height) && preg_match($this->
478
			options['image_file_types'], $file->name))
479
		{
480
			list($img_width, $img_height) = $this->get_image_size($uploaded_file);
2115 lars 481
 
2116 lars 482
			// If we are auto rotating the image by default, do the checks on
483
			// the correct orientation
484
			if (@$this->options['image_versions']['']['auto_orient'] && function_exists('exif_read_data') &&
485
				($exif = @exif_read_data($uploaded_file)) && (((int)@$exif['Orientation']) >= 5))
486
			{
487
				$tmp = $img_width;
488
				$img_width = $img_height;
489
				$img_height = $tmp;
490
				unset($tmp);
491
			}
2115 lars 492
 
2116 lars 493
		}
494
		if (!empty($img_width))
495
		{
496
			if ($max_width && $img_width > $max_width)
497
			{
498
				$file->error = $this->get_error_message('max_width');
499
				return false;
500
			}
501
			if ($max_height && $img_height > $max_height)
502
			{
503
				$file->error = $this->get_error_message('max_height');
504
				return false;
505
			}
506
			if ($min_width && $img_width < $min_width)
507
			{
508
				$file->error = $this->get_error_message('min_width');
509
				return false;
510
			}
511
			if ($min_height && $img_height < $min_height)
512
			{
513
				$file->error = $this->get_error_message('min_height');
514
				return false;
515
			}
516
		}
517
		return true;
518
	}
2115 lars 519
 
2116 lars 520
	protected function upcount_name_callback($matches)
521
	{
522
		$index = isset($matches[1]) ? ((int)$matches[1]) + 1 : 1;
523
		$ext = isset($matches[2]) ? $matches[2] : '';
524
		return ' (' . $index . ')' . $ext;
525
	}
2115 lars 526
 
2116 lars 527
	protected function upcount_name($name)
528
	{
529
		return preg_replace_callback('/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/', array($this,
530
				'upcount_name_callback'), $name, 1);
531
	}
2115 lars 532
 
2116 lars 533
	protected function get_unique_filename($file_path, $name, $size, $type, $error,
534
		$index, $content_range)
535
	{
536
		while (is_dir($this->get_upload_path($name)))
537
		{
538
			$name = $this->upcount_name($name);
539
		}
540
		// Keep an existing filename if this is part of a chunked upload:
541
		$uploaded_bytes = $this->fix_integer_overflow((int)$content_range[1]);
542
		while (is_file($this->get_upload_path($name)))
543
		{
544
			if ($uploaded_bytes === $this->get_file_size($this->get_upload_path($name)))
545
			{
546
				break;
547
			}
548
			$name = $this->upcount_name($name);
549
		}
550
		return $name;
551
	}
2115 lars 552
 
2116 lars 553
	protected function fix_file_extension($file_path, $name, $size, $type, $error, $index,
554
		$content_range)
555
	{
556
		// Add missing file extension for known image types:
557
		if (strpos($name, '.') === false && preg_match('/^image\/(gif|jpe?g|png)/', $type,
558
			$matches))
559
		{
560
			$name .= '.' . $matches[1];
561
		}
562
		if ($this->options['correct_image_extensions'] && function_exists('exif_imagetype'))
563
		{
564
			switch (@exif_imagetype($file_path))
565
			{
566
				case IMAGETYPE_JPEG:
567
					$extensions = array('jpg', 'jpeg');
568
					break;
569
				case IMAGETYPE_PNG:
570
					$extensions = array('png');
571
					break;
572
				case IMAGETYPE_GIF:
573
					$extensions = array('gif');
574
					break;
575
			}
576
			// Adjust incorrect image file extensions:
577
			if (!empty($extensions))
578
			{
579
				$parts = explode('.', $name);
580
				$extIndex = count($parts) - 1;
581
				$ext = strtolower(@$parts[$extIndex]);
582
				if (!in_array($ext, $extensions))
583
				{
584
					$parts[$extIndex] = $extensions[0];
585
					$name = implode('.', $parts);
586
				}
587
			}
588
		}
589
		return $name;
590
	}
2115 lars 591
 
2116 lars 592
	protected function trim_file_name($file_path, $name, $size, $type, $error, $index,
593
		$content_range)
594
	{
595
		// Remove path information and dots around the filename, to prevent uploading
596
		// into different directories or replacing hidden system files.
597
		// Also remove control characters and spaces (\x00..\x20) around the filename:
598
		$name = trim($this->basename(stripslashes($name)), ".\x00..\x20");
599
		// Use a timestamp for empty filenames:
600
		if (!$name)
601
		{
602
			$name = str_replace('.', '-', microtime(true));
603
		}
604
		return $name;
605
	}
2115 lars 606
 
2116 lars 607
	protected function get_file_name($file_path, $name, $size, $type, $error, $index,
608
		$content_range)
609
	{
610
		$name = $this->trim_file_name($file_path, $name, $size, $type, $error, $index, $content_range);
611
		return $this->get_unique_filename($file_path, $this->fix_file_extension($file_path,
612
			$name, $size, $type, $error, $index, $content_range), $size, $type, $error, $index,
613
			$content_range);
614
	}
2115 lars 615
 
2116 lars 616
	protected function get_scaled_image_file_paths($file_name, $version)
617
	{
618
		$file_path = $this->get_upload_path($file_name);
619
		if (!empty($version))
620
		{
621
			$version_dir = $this->get_upload_path(null, $version);
622
			if (!is_dir($version_dir))
623
			{
624
				mkdir($version_dir, $this->options['mkdir_mode'], true);
625
			}
626
			$new_file_path = $version_dir . '/' . $file_name;
627
		} else
628
		{
629
			$new_file_path = $file_path;
630
		}
631
		return array($file_path, $new_file_path);
632
	}
2115 lars 633
 
2116 lars 634
	protected function gd_get_image_object($file_path, $func, $no_cache = false)
635
	{
636
		if (empty($this->image_objects[$file_path]) || $no_cache)
637
		{
638
			$this->gd_destroy_image_object($file_path);
639
			$this->image_objects[$file_path] = $func($file_path);
640
		}
641
		return $this->image_objects[$file_path];
642
	}
2115 lars 643
 
2116 lars 644
	protected function gd_set_image_object($file_path, $image)
645
	{
646
		$this->gd_destroy_image_object($file_path);
647
		$this->image_objects[$file_path] = $image;
648
	}
2115 lars 649
 
2116 lars 650
	protected function gd_destroy_image_object($file_path)
651
	{
652
		$image = (isset($this->image_objects[$file_path])) ? $this->image_objects[$file_path] : null;
653
		return $image && imagedestroy($image);
654
	}
2115 lars 655
 
2116 lars 656
	protected function gd_imageflip($image, $mode)
657
	{
658
		if (function_exists('imageflip'))
659
		{
660
			return imageflip($image, $mode);
661
		}
662
		$new_width = $src_width = imagesx($image);
663
		$new_height = $src_height = imagesy($image);
664
		$new_img = imagecreatetruecolor($new_width, $new_height);
665
		$src_x = 0;
666
		$src_y = 0;
667
		switch ($mode)
668
		{
669
			case '1': // flip on the horizontal axis
670
				$src_y = $new_height - 1;
671
				$src_height = -$new_height;
672
				break;
673
			case '2': // flip on the vertical axis
674
				$src_x = $new_width - 1;
675
				$src_width = -$new_width;
676
				break;
677
			case '3': // flip on both axes
678
				$src_y = $new_height - 1;
679
				$src_height = -$new_height;
680
				$src_x = $new_width - 1;
681
				$src_width = -$new_width;
682
				break;
683
			default:
684
				return $image;
685
		}
686
		imagecopyresampled($new_img, $image, 0, 0, $src_x, $src_y, $new_width, $new_height,
687
			$src_width, $src_height);
688
		return $new_img;
689
	}
2115 lars 690
 
2116 lars 691
	protected function gd_orient_image($file_path, $src_img)
692
	{
693
		if (!function_exists('exif_read_data'))
694
		{
695
			return false;
696
		}
697
		$exif = @exif_read_data($file_path);
698
		if ($exif === false)
699
		{
700
			return false;
701
		}
702
		$orientation = (int)@$exif['Orientation'];
703
		if ($orientation < 2 || $orientation > 8)
704
		{
705
			return false;
706
		}
707
		switch ($orientation)
708
		{
709
			case 2:
710
				$new_img = $this->gd_imageflip($src_img, defined('IMG_FLIP_VERTICAL') ?
711
					IMG_FLIP_VERTICAL : 2);
712
				break;
713
			case 3:
714
				$new_img = imagerotate($src_img, 180, 0);
715
				break;
716
			case 4:
717
				$new_img = $this->gd_imageflip($src_img, defined('IMG_FLIP_HORIZONTAL') ?
718
					IMG_FLIP_HORIZONTAL : 1);
719
				break;
720
			case 5:
721
				$tmp_img = $this->gd_imageflip($src_img, defined('IMG_FLIP_HORIZONTAL') ?
722
					IMG_FLIP_HORIZONTAL : 1);
723
				$new_img = imagerotate($tmp_img, 270, 0);
724
				imagedestroy($tmp_img);
725
				break;
726
			case 6:
727
				$new_img = imagerotate($src_img, 270, 0);
728
				break;
729
			case 7:
730
				$tmp_img = $this->gd_imageflip($src_img, defined('IMG_FLIP_VERTICAL') ?
731
					IMG_FLIP_VERTICAL : 2);
732
				$new_img = imagerotate($tmp_img, 270, 0);
733
				imagedestroy($tmp_img);
734
				break;
735
			case 8:
736
				$new_img = imagerotate($src_img, 90, 0);
737
				break;
738
			default:
739
				return false;
740
		}
741
		$this->gd_set_image_object($file_path, $new_img);
742
		return true;
743
	}
2115 lars 744
 
2116 lars 745
	protected function gd_create_scaled_image($file_name, $version, $options)
746
	{
747
		if (!function_exists('imagecreatetruecolor'))
748
		{
749
			error_log('Function not found: imagecreatetruecolor');
750
			return false;
751
		}
752
		list($file_path, $new_file_path) = $this->get_scaled_image_file_paths($file_name,
753
			$version);
754
		$type = strtolower(substr(strrchr($file_name, '.'), 1));
755
		switch ($type)
756
		{
757
			case 'jpg':
758
			case 'jpeg':
759
				$src_func = 'imagecreatefromjpeg';
760
				$write_func = 'imagejpeg';
761
				$image_quality = isset($options['jpeg_quality']) ? $options['jpeg_quality'] : 75;
762
				break;
763
			case 'gif':
764
				$src_func = 'imagecreatefromgif';
765
				$write_func = 'imagegif';
766
				$image_quality = null;
767
				break;
768
			case 'png':
769
				$src_func = 'imagecreatefrompng';
770
				$write_func = 'imagepng';
771
				$image_quality = isset($options['png_quality']) ? $options['png_quality'] : 9;
772
				break;
773
			default:
774
				return false;
775
		}
776
		$src_img = $this->gd_get_image_object($file_path, $src_func, !empty($options['no_cache']));
777
		$image_oriented = false;
778
		if (!empty($options['auto_orient']) && $this->gd_orient_image($file_path, $src_img))
779
		{
780
			$image_oriented = true;
781
			$src_img = $this->gd_get_image_object($file_path, $src_func);
782
		}
783
		$max_width = $img_width = imagesx($src_img);
784
		$max_height = $img_height = imagesy($src_img);
785
		if (!empty($options['max_width']))
786
		{
787
			$max_width = $options['max_width'];
788
		}
789
		if (!empty($options['max_height']))
790
		{
791
			$max_height = $options['max_height'];
792
		}
793
		$scale = min($max_width / $img_width, $max_height / $img_height);
794
		if ($scale >= 1)
795
		{
796
			if ($image_oriented)
797
			{
798
				return $write_func($src_img, $new_file_path, $image_quality);
799
			}
800
			if ($file_path !== $new_file_path)
801
			{
802
				return copy($file_path, $new_file_path);
803
			}
804
			return true;
805
		}
806
		if (empty($options['crop']))
807
		{
808
			$new_width = $img_width * $scale;
809
			$new_height = $img_height * $scale;
810
			$dst_x = 0;
811
			$dst_y = 0;
812
			$new_img = imagecreatetruecolor($new_width, $new_height);
813
		} else
814
		{
815
			if (($img_width / $img_height) >= ($max_width / $max_height))
816
			{
817
				$new_width = $img_width / ($img_height / $max_height);
818
				$new_height = $max_height;
819
			} else
820
			{
821
				$new_width = $max_width;
822
				$new_height = $img_height / ($img_width / $max_width);
823
			}
824
			$dst_x = 0 - ($new_width - $max_width) / 2;
825
			$dst_y = 0 - ($new_height - $max_height) / 2;
826
			$new_img = imagecreatetruecolor($max_width, $max_height);
827
		}
828
		// Handle transparency in GIF and PNG images:
829
		switch ($type)
830
		{
831
			case 'gif':
832
			case 'png':
833
				imagecolortransparent($new_img, imagecolorallocate($new_img, 0, 0, 0));
834
			case 'png':
835
				imagealphablending($new_img, false);
836
				imagesavealpha($new_img, true);
837
				break;
838
		}
839
		$success = imagecopyresampled($new_img, $src_img, $dst_x, $dst_y, 0, 0, $new_width,
840
			$new_height, $img_width, $img_height) && $write_func($new_img, $new_file_path, $image_quality);
841
		$this->gd_set_image_object($file_path, $new_img);
842
		return $success;
843
	}
2115 lars 844
 
2116 lars 845
	protected function imagick_get_image_object($file_path, $no_cache = false)
846
	{
847
		if (empty($this->image_objects[$file_path]) || $no_cache)
848
		{
849
			$this->imagick_destroy_image_object($file_path);
850
			$image = new \Imagick();
851
			if (!empty($this->options['imagick_resource_limits']))
852
			{
853
				foreach ($this->options['imagick_resource_limits'] as $type => $limit)
854
				{
855
					$image->setResourceLimit($type, $limit);
856
				}
857
			}
858
			$image->readImage($file_path);
859
			$this->image_objects[$file_path] = $image;
860
		}
861
		return $this->image_objects[$file_path];
862
	}
2115 lars 863
 
2116 lars 864
	protected function imagick_set_image_object($file_path, $image)
865
	{
866
		$this->imagick_destroy_image_object($file_path);
867
		$this->image_objects[$file_path] = $image;
868
	}
2115 lars 869
 
2116 lars 870
	protected function imagick_destroy_image_object($file_path)
871
	{
872
		$image = (isset($this->image_objects[$file_path])) ? $this->image_objects[$file_path] : null;
873
		return $image && $image->destroy();
874
	}
2115 lars 875
 
2116 lars 876
	protected function imagick_orient_image($image)
877
	{
878
		$orientation = $image->getImageOrientation();
879
		$background = new \ImagickPixel('none');
880
		switch ($orientation)
881
		{
882
			case \imagick::ORIENTATION_TOPRIGHT: // 2
883
				$image->flopImage(); // horizontal flop around y-axis
884
				break;
885
			case \imagick::ORIENTATION_BOTTOMRIGHT: // 3
886
				$image->rotateImage($background, 180);
887
				break;
888
			case \imagick::ORIENTATION_BOTTOMLEFT: // 4
889
				$image->flipImage(); // vertical flip around x-axis
890
				break;
891
			case \imagick::ORIENTATION_LEFTTOP: // 5
892
				$image->flopImage(); // horizontal flop around y-axis
893
				$image->rotateImage($background, 270);
894
				break;
895
			case \imagick::ORIENTATION_RIGHTTOP: // 6
896
				$image->rotateImage($background, 90);
897
				break;
898
			case \imagick::ORIENTATION_RIGHTBOTTOM: // 7
899
				$image->flipImage(); // vertical flip around x-axis
900
				$image->rotateImage($background, 270);
901
				break;
902
			case \imagick::ORIENTATION_LEFTBOTTOM: // 8
903
				$image->rotateImage($background, 270);
904
				break;
905
			default:
906
				return false;
907
		}
908
		$image->setImageOrientation(\imagick::ORIENTATION_TOPLEFT); // 1
909
		return true;
910
	}
2115 lars 911
 
2116 lars 912
	protected function imagick_create_scaled_image($file_name, $version, $options)
913
	{
914
		list($file_path, $new_file_path) = $this->get_scaled_image_file_paths($file_name,
915
			$version);
916
		$image = $this->imagick_get_image_object($file_path, !empty($options['crop']) ||
917
			!empty($options['no_cache']));
918
		if ($image->getImageFormat() === 'GIF')
919
		{
920
			// Handle animated GIFs:
921
			$images = $image->coalesceImages();
922
			foreach ($images as $frame)
923
			{
924
				$image = $frame;
925
				$this->imagick_set_image_object($file_name, $image);
926
				break;
927
			}
928
		}
929
		$image_oriented = false;
930
		if (!empty($options['auto_orient']))
931
		{
932
			$image_oriented = $this->imagick_orient_image($image);
933
		}
934
		$new_width = $max_width = $img_width = $image->getImageWidth();
935
		$new_height = $max_height = $img_height = $image->getImageHeight();
936
		if (!empty($options['max_width']))
937
		{
938
			$new_width = $max_width = $options['max_width'];
939
		}
940
		if (!empty($options['max_height']))
941
		{
942
			$new_height = $max_height = $options['max_height'];
943
		}
944
		$image_strip = false;
945
		if (!empty($options["strip"]))
946
		{
947
			$image_strip = $options["strip"];
948
		}
949
		if (!$image_oriented && ($max_width >= $img_width) && ($max_height >= $img_height) &&
950
			!$image_strip && empty($options["jpeg_quality"]))
951
		{
952
			if ($file_path !== $new_file_path)
953
			{
954
				return copy($file_path, $new_file_path);
955
			}
956
			return true;
957
		}
958
		$crop = !empty($options['crop']);
959
		if ($crop)
960
		{
961
			$x = 0;
962
			$y = 0;
963
			if (($img_width / $img_height) >= ($max_width / $max_height))
964
			{
965
				$new_width = 0; // Enables proportional scaling based on max_height
966
				$x = ($img_width / ($img_height / $max_height) - $max_width) / 2;
967
			} else
968
			{
969
				$new_height = 0; // Enables proportional scaling based on max_width
970
				$y = ($img_height / ($img_width / $max_width) - $max_height) / 2;
971
			}
972
		}
973
		$success = $image->resizeImage($new_width, $new_height, isset($options['filter']) ?
974
			$options['filter'] : \imagick::FILTER_LANCZOS, isset($options['blur']) ? $options['blur'] :
975
			1, $new_width && $new_height // fit image into constraints if not to be cropped
976
			);
977
		if ($success && $crop)
978
		{
979
			$success = $image->cropImage($max_width, $max_height, $x, $y);
980
			if ($success)
981
			{
982
				$success = $image->setImagePage($max_width, $max_height, 0, 0);
983
			}
984
		}
985
		$type = strtolower(substr(strrchr($file_name, '.'), 1));
986
		switch ($type)
987
		{
988
			case 'jpg':
989
			case 'jpeg':
990
				if (!empty($options['jpeg_quality']))
991
				{
992
					$image->setImageCompression(\imagick::COMPRESSION_JPEG);
993
					$image->setImageCompressionQuality($options['jpeg_quality']);
994
				}
995
				break;
996
		}
997
		if ($image_strip)
998
		{
999
			$image->stripImage();
1000
		}
1001
		return $success && $image->writeImage($new_file_path);
1002
	}
2115 lars 1003
 
2116 lars 1004
	protected function imagemagick_create_scaled_image($file_name, $version, $options)
1005
	{
1006
		list($file_path, $new_file_path) = $this->get_scaled_image_file_paths($file_name,
1007
			$version);
1008
		$resize = @$options['max_width'] . (empty($options['max_height']) ? '' : 'X' . $options['max_height']);
1009
		if (!$resize && empty($options['auto_orient']))
1010
		{
1011
			if ($file_path !== $new_file_path)
1012
			{
1013
				return copy($file_path, $new_file_path);
1014
			}
1015
			return true;
1016
		}
1017
		$cmd = $this->options['convert_bin'];
1018
		if (!empty($this->options['convert_params']))
1019
		{
1020
			$cmd .= ' ' . $this->options['convert_params'];
1021
		}
1022
		$cmd .= ' ' . escapeshellarg($file_path);
1023
		if (!empty($options['auto_orient']))
1024
		{
1025
			$cmd .= ' -auto-orient';
1026
		}
1027
		if ($resize)
1028
		{
1029
			// Handle animated GIFs:
1030
			$cmd .= ' -coalesce';
1031
			if (empty($options['crop']))
1032
			{
1033
				$cmd .= ' -resize ' . escapeshellarg($resize . '>');
1034
			} else
1035
			{
1036
				$cmd .= ' -resize ' . escapeshellarg($resize . '^');
1037
				$cmd .= ' -gravity center';
1038
				$cmd .= ' -crop ' . escapeshellarg($resize . '+0+0');
1039
			}
1040
			// Make sure the page dimensions are correct (fixes offsets of animated GIFs):
1041
			$cmd .= ' +repage';
1042
		}
1043
		if (!empty($options['convert_params']))
1044
		{
1045
			$cmd .= ' ' . $options['convert_params'];
1046
		}
1047
		$cmd .= ' ' . escapeshellarg($new_file_path);
1048
		exec($cmd, $output, $error);
1049
		if ($error)
1050
		{
1051
			error_log(implode('\n', $output));
1052
			return false;
1053
		}
1054
		return true;
1055
	}
2115 lars 1056
 
2116 lars 1057
	protected function get_image_size($file_path)
1058
	{
1059
		if ($this->options['image_library'])
1060
		{
1061
			if (extension_loaded('imagick'))
1062
			{
1063
				$image = new \Imagick();
1064
				try
1065
				{
1066
					if (@$image->pingImage($file_path))
1067
					{
1068
						$dimensions = array($image->getImageWidth(), $image->getImageHeight());
1069
						$image->destroy();
1070
						return $dimensions;
1071
					}
1072
					return false;
1073
				}
1074
				catch (\Exception $e)
1075
				{
1076
					error_log($e->getMessage());
1077
				}
1078
			}
1079
			if ($this->options['image_library'] === 2)
1080
			{
1081
				$cmd = $this->options['identify_bin'];
1082
				$cmd .= ' -ping ' . escapeshellarg($file_path);
1083
				exec($cmd, $output, $error);
1084
				if (!$error && !empty($output))
1085
				{
1086
					// image.jpg JPEG 1920x1080 1920x1080+0+0 8-bit sRGB 465KB 0.000u 0:00.000
1087
					$infos = preg_split('/\s+/', substr($output[0], strlen($file_path)));
1088
					$dimensions = preg_split('/x/', $infos[2]);
1089
					return $dimensions;
1090
				}
1091
				return false;
1092
			}
1093
		}
1094
		if (!function_exists('getimagesize'))
1095
		{
1096
			error_log('Function not found: getimagesize');
1097
			return false;
1098
		}
1099
		return @getimagesize($file_path);
1100
	}
2115 lars 1101
 
2116 lars 1102
	protected function create_scaled_image($file_name, $version, $options)
1103
	{
1104
		if ($this->options['image_library'] === 2)
1105
		{
1106
			return $this->imagemagick_create_scaled_image($file_name, $version, $options);
1107
		}
1108
		if ($this->options['image_library'] && extension_loaded('imagick'))
1109
		{
1110
			return $this->imagick_create_scaled_image($file_name, $version, $options);
1111
		}
1112
		return $this->gd_create_scaled_image($file_name, $version, $options);
1113
	}
2115 lars 1114
 
2116 lars 1115
	protected function destroy_image_object($file_path)
1116
	{
1117
		if ($this->options['image_library'] && extension_loaded('imagick'))
1118
		{
1119
			return $this->imagick_destroy_image_object($file_path);
1120
		}
1121
	}
2115 lars 1122
 
2116 lars 1123
	protected function is_valid_image_file($file_path)
1124
	{
1125
		if (!preg_match($this->options['image_file_types'], $file_path))
1126
		{
1127
			return false;
1128
		}
1129
		if (function_exists('exif_imagetype'))
1130
		{
1131
			return @exif_imagetype($file_path);
1132
		}
1133
		$image_info = $this->get_image_size($file_path);
1134
		return $image_info && $image_info[0] && $image_info[1];
1135
	}
2115 lars 1136
 
2116 lars 1137
	protected function handle_image_file($file_path, $file)
1138
	{
1139
		$failed_versions = array();
1140
		foreach ($this->options['image_versions'] as $version => $options)
1141
		{
1142
			if ($this->create_scaled_image($file->name, $version, $options))
1143
			{
1144
				if (!empty($version))
1145
				{
1146
					$file->{$version . 'Url'} = $this->get_download_url($file->name, $version);
1147
				} else
1148
				{
1149
					$file->size = $this->get_file_size($file_path, true);
1150
				}
1151
			} else
1152
			{
1153
				$failed_versions[] = $version ? $version : 'original';
1154
			}
1155
		}
1156
		if (count($failed_versions))
1157
		{
1158
			$file->error = $this->get_error_message('image_resize') . ' (' . implode($failed_versions,
1159
				', ') . ')';
1160
		}
1161
		// Free memory:
1162
		$this->destroy_image_object($file_path);
1163
	}
2115 lars 1164
 
2116 lars 1165
	protected function handle_file_upload($uploaded_file, $name, $size, $type, $error,
1166
		$index = null, $content_range = null)
1167
	{
1168
		$file = new \stdClass();
1169
		$file->name = $this->get_file_name($uploaded_file, $name, $size, $type, $error,
1170
			$index, $content_range);
1171
		$file->size = $this->fix_integer_overflow((int)$size);
1172
		$file->type = $type;
1173
		if ($this->validate($uploaded_file, $file, $error, $index))
1174
		{
1175
			$this->handle_form_data($file, $index);
1176
			$upload_dir = $this->get_upload_path();
1177
			if (!is_dir($upload_dir))
1178
			{
1179
				mkdir($upload_dir, $this->options['mkdir_mode'], true);
1180
			}
1181
			$file_path = $this->get_upload_path($file->name);
1182
			$append_file = $content_range && is_file($file_path) && $file->size > $this->
1183
				get_file_size($file_path);
1184
			if ($uploaded_file && is_uploaded_file($uploaded_file))
1185
			{
1186
				// multipart/formdata uploads (POST method uploads)
1187
				if ($append_file)
1188
				{
1189
					file_put_contents($file_path, fopen($uploaded_file, 'r'), FILE_APPEND);
1190
				} else
1191
				{
1192
					move_uploaded_file($uploaded_file, $file_path);
1193
				}
1194
			} else
1195
			{
1196
				// Non-multipart uploads (PUT method support)
1197
				file_put_contents($file_path, fopen($this->options['input_stream'], 'r'), $append_file ?
1198
					FILE_APPEND : 0);
1199
			}
1200
			$file_size = $this->get_file_size($file_path, $append_file);
1201
			if ($file_size === $file->size)
1202
			{
1203
				$file->url = $this->get_download_url($file->name);
1204
				if ($this->is_valid_image_file($file_path))
1205
				{
1206
					$this->handle_image_file($file_path, $file);
1207
				}
1208
			} else
1209
			{
1210
				$file->size = $file_size;
1211
				if (!$content_range && $this->options['discard_aborted_uploads'])
1212
				{
1213
					unlink($file_path);
1214
					$file->error = $this->get_error_message('abort');
1215
				}
1216
			}
1217
			$this->set_additional_file_properties($file);
1218
		}
1219
		return $file;
1220
	}
2115 lars 1221
 
2116 lars 1222
	protected function readfile($file_path)
1223
	{
1224
		$file_size = $this->get_file_size($file_path);
1225
		$chunk_size = $this->options['readfile_chunk_size'];
1226
		if ($chunk_size && $file_size > $chunk_size)
1227
		{
1228
			$handle = fopen($file_path, 'rb');
1229
			while (!feof($handle))
1230
			{
1231
				echo fread($handle, $chunk_size);
1232
				@ob_flush();
1233
				@flush();
1234
			}
1235
			fclose($handle);
1236
			return $file_size;
1237
		}
1238
		return readfile($file_path);
1239
	}
2115 lars 1240
 
2116 lars 1241
	protected function body($str)
1242
	{
1243
		echo $str;
1244
	}
2115 lars 1245
 
2116 lars 1246
	protected function header($str)
1247
	{
1248
		header($str);
1249
	}
2115 lars 1250
 
2116 lars 1251
	protected function get_upload_data($id)
1252
	{
1253
		return @$_FILES[$id];
1254
	}
2115 lars 1255
 
2116 lars 1256
	protected function get_post_param($id)
1257
	{
1258
		return @$_POST[$id];
1259
	}
2115 lars 1260
 
2116 lars 1261
	protected function get_query_param($id)
1262
	{
1263
		return @$_GET[$id];
1264
	}
2115 lars 1265
 
2116 lars 1266
	protected function get_server_var($id)
1267
	{
1268
		return @$_SERVER[$id];
1269
	}
2115 lars 1270
 
2116 lars 1271
	protected function handle_form_data($file, $index)
1272
	{
1273
		// Handle form data, e.g. $_POST['description'][$index]
1274
	}
2115 lars 1275
 
2116 lars 1276
	protected function get_version_param()
1277
	{
1278
		return $this->basename(stripslashes($this->get_query_param('version')));
1279
	}
2115 lars 1280
 
2116 lars 1281
	protected function get_singular_param_name()
1282
	{
1283
		return substr($this->options['param_name'], 0, -1);
1284
	}
2115 lars 1285
 
2116 lars 1286
	protected function get_file_name_param()
1287
	{
1288
		$name = $this->get_singular_param_name();
1289
		return $this->basename(stripslashes($this->get_query_param($name)));
1290
	}
2115 lars 1291
 
2116 lars 1292
	protected function get_file_names_params()
1293
	{
1294
		$params = $this->get_query_param($this->options['param_name']);
1295
		if (!$params)
1296
		{
1297
			return null;
1298
		}
1299
		foreach ($params as $key => $value)
1300
		{
1301
			$params[$key] = $this->basename(stripslashes($value));
1302
		}
1303
		return $params;
1304
	}
2115 lars 1305
 
2116 lars 1306
	protected function get_file_type($file_path)
1307
	{
1308
		switch (strtolower(pathinfo($file_path, PATHINFO_EXTENSION)))
1309
		{
1310
			case 'jpeg':
1311
			case 'jpg':
1312
				return 'image/jpeg';
1313
			case 'png':
1314
				return 'image/png';
1315
			case 'gif':
1316
				return 'image/gif';
1317
			default:
1318
				return '';
1319
		}
1320
	}
2115 lars 1321
 
2116 lars 1322
	protected function download()
1323
	{
1324
		switch ($this->options['download_via_php'])
1325
		{
1326
			case 1:
1327
				$redirect_header = null;
1328
				break;
1329
			case 2:
1330
				$redirect_header = 'X-Sendfile';
1331
				break;
1332
			case 3:
1333
				$redirect_header = 'X-Accel-Redirect';
1334
				break;
1335
			default:
1336
				return $this->header('HTTP/1.1 403 Forbidden');
1337
		}
1338
		$file_name = $this->get_file_name_param();
1339
		if (!$this->is_valid_file_object($file_name))
1340
		{
1341
			return $this->header('HTTP/1.1 404 Not Found');
1342
		}
1343
		if ($redirect_header)
1344
		{
1345
			return $this->header($redirect_header . ': ' . $this->get_download_url($file_name,
1346
				$this->get_version_param(), true));
1347
		}
1348
		$file_path = $this->get_upload_path($file_name, $this->get_version_param());
1349
		// Prevent browsers from MIME-sniffing the content-type:
1350
		$this->header('X-Content-Type-Options: nosniff');
1351
		if (!preg_match($this->options['inline_file_types'], $file_name))
1352
		{
1353
			$this->header('Content-Type: application/octet-stream');
1354
			$this->header('Content-Disposition: attachment; filename="' . $file_name . '"');
1355
		} else
1356
		{
1357
			$this->header('Content-Type: ' . $this->get_file_type($file_path));
1358
			$this->header('Content-Disposition: inline; filename="' . $file_name . '"');
1359
		}
1360
		$this->header('Content-Length: ' . $this->get_file_size($file_path));
1361
		$this->header('Last-Modified: ' . gmdate('D, d M Y H:i:s T', filemtime($file_path)));
1362
		$this->readfile($file_path);
1363
	}
2115 lars 1364
 
2116 lars 1365
	protected function send_content_type_header()
1366
	{
1367
		$this->header('Vary: Accept');
1368
		if (strpos($this->get_server_var('HTTP_ACCEPT'), 'application/json') !== false)
1369
		{
1370
			$this->header('Content-type: application/json');
1371
		} else
1372
		{
1373
			$this->header('Content-type: text/plain');
1374
		}
1375
	}
2115 lars 1376
 
2116 lars 1377
	protected function send_access_control_headers()
1378
	{
1379
		$this->header('Access-Control-Allow-Origin: ' . $this->options['access_control_allow_origin']);
1380
		$this->header('Access-Control-Allow-Credentials: ' . ($this->options['access_control_allow_credentials'] ?
1381
			'true' : 'false'));
1382
		$this->header('Access-Control-Allow-Methods: ' . implode(', ', $this->options['access_control_allow_methods']));
1383
		$this->header('Access-Control-Allow-Headers: ' . implode(', ', $this->options['access_control_allow_headers']));
1384
	}
2115 lars 1385
 
2116 lars 1386
	public function generate_response($content, $print_response = true)
1387
	{
1388
		$this->response = $content;
1389
		if ($print_response)
1390
		{
1391
			$json = json_encode($content);
1392
			$redirect = stripslashes($this->get_post_param('redirect'));
1393
			if ($redirect && preg_match($this->options['redirect_allow_target'], $redirect))
1394
			{
1395
				$this->header('Location: ' . sprintf($redirect, rawurlencode($json)));
1396
				return;
1397
			}
1398
			$this->head();
1399
			if ($this->get_server_var('HTTP_CONTENT_RANGE'))
1400
			{
1401
				$files = isset($content[$this->options['param_name']]) ? $content[$this->
1402
					options['param_name']] : null;
1403
				if ($files && is_array($files) && is_object($files[0]) && $files[0]->size)
1404
				{
1405
					$this->header('Range: 0-' . ($this->fix_integer_overflow((int)$files[0]->size) -
1406
						1));
1407
				}
1408
			}
1409
			$this->body($json);
1410
		}
1411
		return $content;
1412
	}
2115 lars 1413
 
2116 lars 1414
	public function get_response()
1415
	{
1416
		return $this->response;
1417
	}
2115 lars 1418
 
2116 lars 1419
	public function head()
1420
	{
1421
		$this->header('Pragma: no-cache');
1422
		$this->header('Cache-Control: no-store, no-cache, must-revalidate');
1423
		$this->header('Content-Disposition: inline; filename="files.json"');
1424
		// Prevent Internet Explorer from MIME-sniffing the content-type:
1425
		$this->header('X-Content-Type-Options: nosniff');
1426
		if ($this->options['access_control_allow_origin'])
1427
		{
1428
			$this->send_access_control_headers();
1429
		}
1430
		$this->send_content_type_header();
1431
	}
2115 lars 1432
 
2116 lars 1433
	public function get($print_response = true)
1434
	{
1435
		if ($print_response && $this->get_query_param('download'))
1436
		{
1437
			return $this->download();
1438
		}
1439
		$file_name = $this->get_file_name_param();
1440
		if ($file_name)
1441
		{
1442
			$response = array($this->get_singular_param_name() => $this->get_file_object($file_name));
1443
		} else
1444
		{
1445
			$response = array($this->options['param_name'] => $this->get_file_objects());
1446
		}
1447
		return $this->generate_response($response, $print_response);
1448
	}
2115 lars 1449
 
2116 lars 1450
	public function post($print_response = true)
1451
	{
1452
		if ($this->get_query_param('_method') === 'DELETE')
1453
		{
1454
			return $this->delete($print_response);
1455
		}
1456
		$upload = $this->get_upload_data($this->options['param_name']);
1457
		// Parse the Content-Disposition header, if available:
1458
		$content_disposition_header = $this->get_server_var('HTTP_CONTENT_DISPOSITION');
1459
		$file_name = $content_disposition_header ? rawurldecode(preg_replace('/(^[^"]+")|("$)/',
1460
			'', $content_disposition_header)) : null;
1461
		// Parse the Content-Range header, which has the following form:
1462
		// Content-Range: bytes 0-524287/2000000
1463
		$content_range_header = $this->get_server_var('HTTP_CONTENT_RANGE');
1464
		$content_range = $content_range_header ? preg_split('/[^0-9]+/', $content_range_header) : null;
1465
		$size = $content_range ? $content_range[3] : null;
1466
		$files = array();
1467
		if ($upload)
1468
		{
1469
			if (is_array($upload['tmp_name']))
1470
			{
1471
				// param_name is an array identifier like "files[]",
1472
				// $upload is a multi-dimensional array:
1473
				foreach ($upload['tmp_name'] as $index => $value)
1474
				{
1475
					$files[] = $this->handle_file_upload($upload['tmp_name'][$index], $file_name ? $file_name :
1476
						$upload['name'][$index], $size ? $size : $upload['size'][$index], $upload['type'][$index],
1477
						$upload['error'][$index], $index, $content_range);
1478
				}
1479
			} else
1480
			{
1481
				// param_name is a single object identifier like "file",
1482
				// $upload is a one-dimensional array:
1483
				$files[] = $this->handle_file_upload(isset($upload['tmp_name']) ? $upload['tmp_name'] : null,
1484
					$file_name ? $file_name : (isset($upload['name']) ? $upload['name'] : null), $size ?
1485
					$size : (isset($upload['size']) ? $upload['size'] : $this->get_server_var('CONTENT_LENGTH')),
1486
					isset($upload['type']) ? $upload['type'] : $this->get_server_var('CONTENT_TYPE'),
1487
					isset($upload['error']) ? $upload['error'] : null, null, $content_range);
1488
			}
1489
		}
1490
		$response = array($this->options['param_name'] => $files);
1491
		return $this->generate_response($response, $print_response);
1492
	}
2115 lars 1493
 
2116 lars 1494
	public function delete($print_response = true)
1495
	{
1496
		$file_names = $this->get_file_names_params();
1497
		if (empty($file_names))
1498
		{
1499
			$file_names = array($this->get_file_name_param());
1500
		}
1501
		$response = array();
1502
		foreach ($file_names as $file_name)
1503
		{
1504
			$file_path = $this->get_upload_path($file_name);
1505
			$success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path);
1506
			if ($success)
1507
			{
1508
				foreach ($this->options['image_versions'] as $version => $options)
1509
				{
1510
					if (!empty($version))
1511
					{
1512
						$file = $this->get_upload_path($file_name, $version);
1513
						if (is_file($file))
1514
						{
1515
							unlink($file);
1516
						}
1517
					}
1518
				}
1519
			}
1520
			$response[$file_name] = $success;
1521
		}
1522
		return $this->generate_response($response, $print_response);
1523
	}
2115 lars 1524
 
2116 lars 1525
	protected function basename($filepath, $suffix = null)
1526
	{
1527
		$splited = preg_split('/\//', rtrim($filepath, '/ '));
1528
		return substr(basename('X' . $splited[count($splited) - 1], $suffix), 1);
1529
	}
2115 lars 1530
}