1: | <?php
|
2: | |
3: | |
4: | |
5: | |
6: | |
7: | |
8: | |
9: | |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | |
16: | |
17: | |
18: |
|
19: |
|
20: | defined('XOOPS_ROOT_PATH') || exit('Restricted access');
|
21: |
|
22: | |
23: | |
24: | |
25: | |
26: | |
27: | |
28: | |
29: | |
30: | |
31: | |
32: | |
33: | |
34: | |
35: | |
36: | |
37: | |
38: | |
39: | |
40: | |
41: | |
42: | |
43: | |
44: | |
45: | |
46: | |
47: | |
48: | |
49: | |
50: | |
51: | |
52: | |
53: | |
54: | |
55: | |
56: | |
57: | |
58: | |
59: | |
60: | |
61: | |
62: | |
63: | |
64: | |
65: | |
66: | |
67: | |
68: | |
69: | |
70: | |
71: |
|
72: | class XoopsMediaUploader
|
73: | {
|
74: | |
75: | |
76: |
|
77: |
|
78: | public $allowUnknownTypes = false;
|
79: | public $mediaName;
|
80: | public $mediaType;
|
81: | public $mediaSize;
|
82: | public $mediaTmpName;
|
83: | public $mediaError;
|
84: | public $mediaRealType = '';
|
85: | public $uploadDir = '';
|
86: | public $allowedMimeTypes = array();
|
87: | public $deniedMimeTypes = array(
|
88: | 'application/x-httpd-php');
|
89: | public $maxFileSize = 0;
|
90: | public $maxWidth;
|
91: | public $maxHeight;
|
92: | public $targetFileName;
|
93: | public $prefix;
|
94: | public $errors = array();
|
95: | public $savedDestination;
|
96: | public $savedFileName;
|
97: | public $extensionToMime = array();
|
98: | public $checkImageType = true;
|
99: | public $extensionsToBeSanitized = array(
|
100: | 'php',
|
101: | 'phtml',
|
102: | 'phtm',
|
103: | 'php3',
|
104: | 'php4',
|
105: | 'cgi',
|
106: | 'pl',
|
107: | 'asp',
|
108: | 'php5',
|
109: | 'php7',
|
110: | );
|
111: |
|
112: | public $imageExtensions = array(
|
113: | 1 => 'gif',
|
114: | 2 => 'jpg',
|
115: | 3 => 'png',
|
116: | 4 => 'swf',
|
117: | 5 => 'psd',
|
118: | 6 => 'bmp',
|
119: | 7 => 'tif',
|
120: | 8 => 'tif',
|
121: | 9 => 'jpc',
|
122: | 10 => 'jp2',
|
123: | 11 => 'jpx',
|
124: | 12 => 'jb2',
|
125: | 13 => 'swc',
|
126: | 14 => 'iff',
|
127: | 15 => 'wbmp',
|
128: | 16 => 'xbm',
|
129: | 17 => 'webp');
|
130: | public $randomFilename = false;
|
131: |
|
132: | |
133: | |
134: | |
135: | |
136: | |
137: | |
138: | |
139: | |
140: | |
141: |
|
142: |
|
143: | public function __construct($uploadDir, $allowedMimeTypes, $maxFileSize = 0, $maxWidth = null, $maxHeight = null, $randomFilename = false)
|
144: | {
|
145: | $this->extensionToMime = include $GLOBALS['xoops']->path('include/mimetypes.inc.php');
|
146: | if (!is_array($this->extensionToMime)) {
|
147: | $this->extensionToMime = array();
|
148: |
|
149: | return false;
|
150: | }
|
151: | if (is_array($allowedMimeTypes)) {
|
152: | $this->allowedMimeTypes =& $allowedMimeTypes;
|
153: | }
|
154: | $this->uploadDir = $uploadDir;
|
155: |
|
156: | $limits = array();
|
157: | $limits = $this->arrayPushIfPositive($limits, $maxFileSize);
|
158: | $limits = $this->arrayPushIfPositive($limits, $this->return_bytes(ini_get('upload_max_filesize')));
|
159: | $limits = $this->arrayPushIfPositive($limits, $this->return_bytes(ini_get('post_max_size')));
|
160: | $limits = $this->arrayPushIfPositive($limits, $this->return_bytes(ini_get('memory_limit')));
|
161: | $this->maxFileSize = min($limits);
|
162: |
|
163: | if (isset($maxWidth)) {
|
164: | $this->maxWidth = (int)$maxWidth;
|
165: | }
|
166: | if (isset($maxHeight)) {
|
167: | $this->maxHeight = (int)$maxHeight;
|
168: | }
|
169: | if (isset($randomFilename)) {
|
170: | $this->randomFilename = $randomFilename;
|
171: | }
|
172: | if (!include_once $GLOBALS['xoops']->path('language/' . $GLOBALS['xoopsConfig']['language'] . '/uploader.php')) {
|
173: | include_once $GLOBALS['xoops']->path('language/english/uploader.php');
|
174: | }
|
175: | }
|
176: |
|
177: | |
178: | |
179: | |
180: | |
181: | |
182: | |
183: |
|
184: | public function return_bytes($size_str)
|
185: | {
|
186: | switch (substr($size_str, -1)) {
|
187: | case 'K':
|
188: | case 'k':
|
189: | return (int)$size_str * 1024;
|
190: | case 'M':
|
191: | case 'm':
|
192: | return (int)$size_str * 1048576;
|
193: | case 'G':
|
194: | case 'g':
|
195: | return (int)$size_str * 1073741824;
|
196: | default:
|
197: | return $size_str;
|
198: | }
|
199: | }
|
200: |
|
201: | |
202: | |
203: | |
204: | |
205: | |
206: |
|
207: | public function countMedia($media_name) {
|
208: | if (!isset($_FILES[$media_name])) {
|
209: | $this->setErrors(_ER_UP_FILENOTFOUND);
|
210: | return false;
|
211: | }
|
212: | return count($_FILES[$media_name]['name']);
|
213: | }
|
214: |
|
215: | |
216: | |
217: | |
218: | |
219: | |
220: | |
221: |
|
222: | public function fetchMedia($media_name, $index = null)
|
223: | {
|
224: | if (empty($this->extensionToMime)) {
|
225: | $this->setErrors(_ER_UP_MIMETYPELOAD);
|
226: |
|
227: | return false;
|
228: | }
|
229: | if (!isset($_FILES[$media_name])) {
|
230: | $this->setErrors(_ER_UP_FILENOTFOUND);
|
231: |
|
232: | return false;
|
233: | } elseif (is_array($_FILES[$media_name]['name']) && isset($index)) {
|
234: | $index = (int)$index;
|
235: | $this->mediaName = @get_magic_quotes_gpc() ? stripslashes($_FILES[$media_name]['name'][$index]) : $_FILES[$media_name]['name'][$index];
|
236: | if ($this->randomFilename) {
|
237: | $unique = uniqid('', true);
|
238: | $this->targetFileName = '' . $unique . '--' . $this->mediaName;
|
239: | }
|
240: | $this->mediaType = $_FILES[$media_name]['type'][$index];
|
241: | $this->mediaSize = $_FILES[$media_name]['size'][$index];
|
242: | $this->mediaTmpName = $_FILES[$media_name]['tmp_name'][$index];
|
243: | $this->mediaError = !empty($_FILES[$media_name]['error'][$index]) ? $_FILES[$media_name]['error'][$index] : 0;
|
244: | } elseif (is_array($_FILES[$media_name]['name']) && !isset($index)) {
|
245: | $this->setErrors(_ER_UP_INDEXNOTSET);
|
246: |
|
247: | return false;
|
248: | } else {
|
249: | $media_name =& $_FILES[$media_name];
|
250: | $this->mediaName = @get_magic_quotes_gpc() ? stripslashes($media_name['name']) : $media_name['name'];
|
251: | if ($this->randomFilename) {
|
252: | $unique = uniqid('', true);
|
253: | $this->targetFileName = '' . $unique . '--' . $this->mediaName;
|
254: | }
|
255: | $this->mediaType = $media_name['type'];
|
256: | $this->mediaSize = $media_name['size'];
|
257: | $this->mediaTmpName = $media_name['tmp_name'];
|
258: | $this->mediaError = !empty($media_name['error']) ? $media_name['error'] : 0;
|
259: | }
|
260: |
|
261: | if (($ext = strrpos($this->mediaName, '.')) !== false) {
|
262: | $ext = strtolower(substr($this->mediaName, $ext + 1));
|
263: | if (isset($this->extensionToMime[$ext])) {
|
264: | $this->mediaRealType = $this->extensionToMime[$ext];
|
265: | }
|
266: | }
|
267: | $this->errors = array();
|
268: | if ($this->mediaError > 0) {
|
269: | switch($this->mediaError){
|
270: | case UPLOAD_ERR_INI_SIZE:
|
271: | $this->setErrors(_ER_UP_INISIZE);
|
272: | return false;
|
273: | break;
|
274: | case UPLOAD_ERR_FORM_SIZE:
|
275: | $this->setErrors(_ER_UP_FORMSIZE);
|
276: | return false;
|
277: | break;
|
278: | case UPLOAD_ERR_PARTIAL:
|
279: | $this->setErrors(_ER_UP_PARTIAL);
|
280: | return false;
|
281: | break;
|
282: | case UPLOAD_ERR_NO_FILE:
|
283: | $this->setErrors(_ER_UP_NOFILE);
|
284: | return false;
|
285: | break;
|
286: | case UPLOAD_ERR_NO_TMP_DIR:
|
287: | $this->setErrors(_ER_UP_NOTMPDIR);
|
288: | return false;
|
289: | break;
|
290: | case UPLOAD_ERR_CANT_WRITE:
|
291: | $this->setErrors(_ER_UP_CANTWRITE);
|
292: | return false;
|
293: | break;
|
294: | case UPLOAD_ERR_EXTENSION:
|
295: | $this->setErrors(_ER_UP_EXTENSION);
|
296: | return false;
|
297: | break;
|
298: | default:
|
299: | $this->setErrors(_ER_UP_UNKNOWN);
|
300: | return false;
|
301: | break;
|
302: | }
|
303: | }
|
304: |
|
305: | if ((int)$this->mediaSize < 0) {
|
306: | $this->setErrors(_ER_UP_INVALIDFILESIZE);
|
307: |
|
308: | return false;
|
309: | }
|
310: | if ($this->mediaName == '') {
|
311: | $this->setErrors(_ER_UP_FILENAMEEMPTY);
|
312: |
|
313: | return false;
|
314: | }
|
315: | if ($this->mediaTmpName === 'none' || !is_uploaded_file($this->mediaTmpName)) {
|
316: | $this->setErrors(_ER_UP_NOFILEUPLOADED);
|
317: |
|
318: | return false;
|
319: | }
|
320: |
|
321: | return true;
|
322: | }
|
323: |
|
324: | |
325: | |
326: | |
327: | |
328: |
|
329: | public function setTargetFileName($value)
|
330: | {
|
331: | $this->targetFileName = (string)trim($value);
|
332: | }
|
333: |
|
334: | |
335: | |
336: | |
337: | |
338: |
|
339: | public function setPrefix($value)
|
340: | {
|
341: | $this->prefix = (string)trim($value);
|
342: | }
|
343: |
|
344: | |
345: | |
346: | |
347: | |
348: |
|
349: | public function getMediaName()
|
350: | {
|
351: | return $this->mediaName;
|
352: | }
|
353: |
|
354: | |
355: | |
356: | |
357: | |
358: |
|
359: | public function getMediaType()
|
360: | {
|
361: | return $this->mediaType;
|
362: | }
|
363: |
|
364: | |
365: | |
366: | |
367: | |
368: |
|
369: | public function getMediaSize()
|
370: | {
|
371: | return $this->mediaSize;
|
372: | }
|
373: |
|
374: | |
375: | |
376: | |
377: | |
378: |
|
379: | public function getMediaTmpName()
|
380: | {
|
381: | return $this->mediaTmpName;
|
382: | }
|
383: |
|
384: | |
385: | |
386: | |
387: | |
388: |
|
389: | public function getSavedFileName()
|
390: | {
|
391: | return $this->savedFileName;
|
392: | }
|
393: |
|
394: | |
395: | |
396: | |
397: | |
398: |
|
399: | public function getSavedDestination()
|
400: | {
|
401: | return $this->savedDestination;
|
402: | }
|
403: |
|
404: | |
405: | |
406: | |
407: | |
408: | |
409: |
|
410: | public function upload($chmod = 0644)
|
411: | {
|
412: | if ($this->uploadDir == '') {
|
413: | $this->setErrors(_ER_UP_UPLOADDIRNOTSET);
|
414: |
|
415: | return false;
|
416: | }
|
417: | if (!is_dir($this->uploadDir)) {
|
418: | $this->setErrors(sprintf(_ER_UP_FAILEDOPENDIR, $this->uploadDir));
|
419: |
|
420: | return false;
|
421: | }
|
422: | if (!is_writable($this->uploadDir)) {
|
423: | $this->setErrors(sprintf(_ER_UP_FAILEDOPENDIRWRITE, $this->uploadDir));
|
424: |
|
425: | return false;
|
426: | }
|
427: | $this->sanitizeMultipleExtensions();
|
428: |
|
429: | if (!$this->checkMaxFileSize()) {
|
430: | return false;
|
431: | }
|
432: | if (!$this->checkMaxWidth()) {
|
433: | return false;
|
434: | }
|
435: | if (!$this->checkMaxHeight()) {
|
436: | return false;
|
437: | }
|
438: | if (!$this->checkMimeType()) {
|
439: | return false;
|
440: | }
|
441: | if (!$this->checkImageType()) {
|
442: | return false;
|
443: | }
|
444: | if (count($this->errors) > 0) {
|
445: | return false;
|
446: | }
|
447: |
|
448: | return $this->_copyFile($chmod);
|
449: | }
|
450: |
|
451: | |
452: | |
453: | |
454: | |
455: | |
456: |
|
457: | public function _copyFile($chmod)
|
458: | {
|
459: | $matched = array();
|
460: | if (!preg_match("/\.([a-zA-Z0-9]+)$/", $this->mediaName, $matched)) {
|
461: | $this->setErrors(_ER_UP_INVALIDFILENAME);
|
462: |
|
463: | return false;
|
464: | }
|
465: | if (isset($this->targetFileName)) {
|
466: | $this->savedFileName = $this->targetFileName;
|
467: | } elseif (isset($this->prefix)) {
|
468: | $this->savedFileName = uniqid($this->prefix, true) . '.' . strtolower($matched[1]);
|
469: | } else {
|
470: | $this->savedFileName = strtolower($this->mediaName);
|
471: | }
|
472: |
|
473: | $this->savedFileName = iconv('UTF-8', 'ASCII//TRANSLIT', $this->savedFileName);
|
474: | $this->savedFileName = preg_replace('!\s+!', '_', $this->savedFileName);
|
475: | $this->savedFileName = preg_replace("/[^a-zA-Z0-9\._-]/", '', $this->savedFileName);
|
476: |
|
477: | $this->savedDestination = $this->uploadDir . '/' . $this->savedFileName;
|
478: | if (!move_uploaded_file($this->mediaTmpName, $this->savedDestination)) {
|
479: | $this->setErrors(sprintf(_ER_UP_FAILEDSAVEFILE, $this->savedDestination));
|
480: |
|
481: | return false;
|
482: | }
|
483: |
|
484: | $ext = strtolower(substr(strrchr($this->savedDestination, '.'), 1));
|
485: | if (in_array($ext, $this->imageExtensions)) {
|
486: | $info = @getimagesize($this->savedDestination);
|
487: | if ($info === false || $this->imageExtensions[(int)$info[2]] != $ext) {
|
488: | $this->setErrors(_ER_UP_SUSPICIOUSREFUSED);
|
489: | @unlink($this->savedDestination);
|
490: |
|
491: | return false;
|
492: | }
|
493: | }
|
494: | @chmod($this->savedDestination, $chmod);
|
495: |
|
496: | return true;
|
497: | }
|
498: |
|
499: | |
500: | |
501: | |
502: | |
503: |
|
504: | public function checkMaxFileSize()
|
505: | {
|
506: | if (!isset($this->maxFileSize)) {
|
507: | return true;
|
508: | }
|
509: | if ($this->mediaSize > $this->maxFileSize) {
|
510: | $this->setErrors(sprintf(_ER_UP_FILESIZETOOLARGE, $this->maxFileSize, $this->mediaSize));
|
511: |
|
512: | return false;
|
513: | }
|
514: |
|
515: | return true;
|
516: | }
|
517: |
|
518: | |
519: | |
520: | |
521: | |
522: |
|
523: | public function checkMaxWidth()
|
524: | {
|
525: | if (!isset($this->maxWidth)) {
|
526: | return true;
|
527: | }
|
528: | if (false !== $dimension = getimagesize($this->mediaTmpName)) {
|
529: | if ($dimension[0] > $this->maxWidth) {
|
530: | $this->setErrors(sprintf(_ER_UP_FILEWIDTHTOOLARGE, $this->maxWidth, $dimension[0]));
|
531: |
|
532: | return false;
|
533: | }
|
534: | } else {
|
535: | trigger_error(sprintf(_ER_UP_FAILEDFETCHIMAGESIZE, $this->mediaTmpName), E_USER_WARNING);
|
536: | }
|
537: |
|
538: | return true;
|
539: | }
|
540: |
|
541: | |
542: | |
543: | |
544: | |
545: |
|
546: | public function checkMaxHeight()
|
547: | {
|
548: | if (!isset($this->maxHeight)) {
|
549: | return true;
|
550: | }
|
551: | if (false !== $dimension = getimagesize($this->mediaTmpName)) {
|
552: | if ($dimension[1] > $this->maxHeight) {
|
553: | $this->setErrors(sprintf(_ER_UP_FILEHEIGHTTOOLARGE, $this->maxHeight, $dimension[1]));
|
554: |
|
555: | return false;
|
556: | }
|
557: | } else {
|
558: | trigger_error(sprintf(_ER_UP_FAILEDFETCHIMAGESIZE, $this->mediaTmpName), E_USER_WARNING);
|
559: | }
|
560: |
|
561: | return true;
|
562: | }
|
563: |
|
564: | |
565: | |
566: | |
567: | |
568: |
|
569: | public function checkMimeType()
|
570: | {
|
571: |
|
572: | $structureCheck = (bool) preg_match('/^\w+\/[-+.\w]+$/', $this->mediaType);
|
573: | if (false === $structureCheck) {
|
574: | $this->mediaType = 'invalid';
|
575: | $this->setErrors(_ER_UP_UNKNOWNFILETYPEREJECTED);
|
576: | return false;
|
577: | }
|
578: |
|
579: | if (empty($this->mediaRealType) && empty($this->allowUnknownTypes)) {
|
580: | $this->setErrors(_ER_UP_UNKNOWNFILETYPEREJECTED);
|
581: |
|
582: | return false;
|
583: | }
|
584: |
|
585: | if ((!empty($this->allowedMimeTypes) && !in_array($this->mediaRealType, $this->allowedMimeTypes)) || (!empty($this->deniedMimeTypes) && in_array($this->mediaRealType, $this->deniedMimeTypes))) {
|
586: | $this->setErrors(sprintf(_ER_UP_MIMETYPENOTALLOWED, htmlspecialchars($this->mediaRealType, ENT_QUOTES)));
|
587: |
|
588: | return false;
|
589: | }
|
590: |
|
591: | return true;
|
592: | }
|
593: |
|
594: | |
595: | |
596: | |
597: | |
598: |
|
599: | public function checkImageType()
|
600: | {
|
601: | if (empty($this->checkImageType)) {
|
602: | return true;
|
603: | }
|
604: |
|
605: | if (('image' === substr($this->mediaType, 0, strpos($this->mediaType, '/'))) || (!empty($this->mediaRealType) && 'image' === substr($this->mediaRealType, 0, strpos($this->mediaRealType, '/')))) {
|
606: | if (!($info = @getimagesize($this->mediaTmpName))) {
|
607: | $this->setErrors(_ER_UP_INVALIDIMAGEFILE);
|
608: |
|
609: | return false;
|
610: | }
|
611: | }
|
612: |
|
613: | return true;
|
614: | }
|
615: |
|
616: | |
617: | |
618: |
|
619: | public function sanitizeMultipleExtensions()
|
620: | {
|
621: | if (empty($this->extensionsToBeSanitized)) {
|
622: | return null;
|
623: | }
|
624: |
|
625: | $patterns = array();
|
626: | $replaces = array();
|
627: | foreach ($this->extensionsToBeSanitized as $ext) {
|
628: | $patterns[] = "/\." . preg_quote($ext, '/') . "\./i";
|
629: | $replaces[] = '_' . $ext . '.';
|
630: | }
|
631: | $this->mediaName = preg_replace($patterns, $replaces, $this->mediaName);
|
632: | }
|
633: |
|
634: | |
635: | |
636: | |
637: | |
638: |
|
639: | public function setErrors($error)
|
640: | {
|
641: | $this->errors[] = trim($error);
|
642: | }
|
643: |
|
644: | |
645: | |
646: | |
647: | |
648: | |
649: |
|
650: | public function &getErrors($ashtml = true)
|
651: | {
|
652: | if (!$ashtml) {
|
653: | return $this->errors;
|
654: | } else {
|
655: | $ret = '';
|
656: | if (count($this->errors) > 0) {
|
657: | $ret = '<h4>' . sprintf(_ER_UP_ERRORSRETURNED, htmlspecialchars($this->mediaName, ENT_QUOTES)) . '</h4>';
|
658: | foreach ($this->errors as $error) {
|
659: | $ret .= $error . '<br>';
|
660: | }
|
661: | }
|
662: |
|
663: | return $ret;
|
664: | }
|
665: | }
|
666: |
|
667: | |
668: | |
669: | |
670: | |
671: | |
672: | |
673: | |
674: | |
675: |
|
676: | protected function arrayPushIfPositive($set, $value) {
|
677: | if ($value > 0) {
|
678: | array_push($set, $value);
|
679: | }
|
680: | return $set;
|
681: | }
|
682: | }
|
683: | |