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