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