Completed
Push — master ( 79e63b...a5181c )
by Daniel
05:26 queued 02:19
created

code/MimeUploadValidator.php (2 issues)

class opening brace is on a new line.

Coding Style Informational

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Adds an additional validation rule to Upload_Validator that attempts to detect
4
 * the file extension of an uploaded file matches it's contents, which is done
5
 * by detecting the MIME type and doing a fuzzy match.
6
 */
7
class MimeUploadValidator extends Upload_Validator {
0 ignored issues
show
As per PSR2, the opening brace for this class should be on a new line.
Loading history...
8
9
	/**
10
	 * The preg_replace() pattern to use against MIME types. Used to strip out
11
	 * useless characters so matching of MIME types can be fuzzy.
12
	 *
13
	 * @var string Regexp pattern
14
	 */
15
	protected $filterPattern = '/.*[\/\.\-\+]/i';
16
17
	public function setFilterPattern($pattern) {
18
		$this->filterPattern = $pattern;
19
	}
20
21
	public function getFilterPattern() {
22
		return $this->filterPattern;
23
	}
24
25
	/**
26
	 * Check if the temporary file has a valid MIME type for it's extension.
27
	 *
28
	 * @uses finfo php extension
29
	 * @return boolean|null
30
	 */
31
	public function isValidMime() {
32
                $extension = strtolower(pathinfo($this->tmpFile['name'], PATHINFO_EXTENSION));
33
34
                // we can't check filenames without an extension or no temp file path, let them pass validation.
35
                if(!$extension || !$this->tmpFile['tmp_name']) return true;
36
37
		$expectedMimes = $this->getExpectedMimeTypes($this->tmpFile);
38
		if(empty($expectedMimes)) {
39
			throw new MimeUploadValidator_Exception(
40
				sprintf('Could not find a MIME type for extension %s', $extension)
41
			);
42
		}
43
44
		$finfo = new finfo(FILEINFO_MIME_TYPE);
45
		$foundMime = $finfo->file($this->tmpFile['tmp_name']);
46
		if(!$foundMime) {
47
			throw new MimeUploadValidator_Exception(
48
				sprintf('Could not find a MIME type for file %s', $this->tmpFile['tmp_name'])
49
			);
50
		}
51
52
		foreach($expectedMimes as $expected) {
53
			if($this->compareMime($foundMime, $expected)) return true;
54
		}
55
		return false;
56
	}
57
58
	/**
59
	 * Fetches an array of valid mimetypes.
60
	 *
61
	 * @return array
62
	 */
63
	public function getExpectedMimeTypes($tmpFile) {
64
		$extension = strtolower(pathinfo($tmpFile['name'], PATHINFO_EXTENSION));
65
66
		// if the finfo php extension isn't loaded, we can't complete this check.
67
		if(!class_exists('finfo')) {
68
			throw new MimeUploadValidator_Exception('PHP extension finfo is not loaded');
69
		}
70
71
		// Attempt to figure out which mime types are expected/acceptable here.
72
		$expectedMimes = array();
73
74
		// Get the mime types set in framework core
75
		$knownMimes = Config::inst()->get('HTTP', 'MimeTypes');
76
		if(isset($knownMimes[$extension])) {
77
			$expectedMimes[] = $knownMimes[$extension];
78
		}
79
80
		// Get the mime types and their variations from mimevalidator
81
		$knownMimes = Config::inst()->get(get_class($this), 'MimeTypes');
82
		if(isset($knownMimes[$extension])) {
83
			if(is_array($knownMimes[$extension])) {
84
				$expectedMimes += $knownMimes[$extension];
85
			} else {
86
				$expectedMimes[] = $knownMimes[$extension];
87
			}
88
		}
89
		return $expectedMimes;
90
	}
91
92
	/**
93
	 * Check two MIME types roughly match eachother.
94
	 *
95
	 * Before we check MIME types, remove known prefixes "vnd.", "x-" etc.
96
	 * If there is a suffix, we'll use that to compare. Examples:
97
	 *
98
	 * application/x-json = json
99
	 * application/json = json
100
	 * application/xhtml+xml = xml
101
	 * application/xml = xml
102
	 *
103
	 * @param string $first The first MIME type to compare to the second
104
	 * @param string $second The second MIME type to compare to the first
105
	 * @return boolean
106
	 */
107
	public function compareMime($first, $second) {
108
		return preg_replace($this->filterPattern, '', $first) === preg_replace($this->filterPattern, '', $second);
109
	}
110
111
	public function validate() {
112
		if(parent::validate() === false) return false;
113
114
		try {
115
			$result = $this->isValidMime();
116
			if($result === false) {
117
				$this->errors[] = _t(
118
					'File.INVALIDMIME',
119
					'File extension does not match known MIME type'
120
				);
121
				return false;
122
			}
123
		} catch(MimeUploadValidator_Exception $e) {
124
			$this->errors[] = _t(
125
				'File.FAILEDMIMECHECK',
126
				'MIME validation failed: {message}',
127
				'Argument 1: Message about why MIME type detection failed',
128
				array('message' => $e->getMessage())
129
			);
130
			return false;
131
		}
132
133
		return true;
134
	}
135
136
}
137
138
class MimeUploadValidator_Exception extends Exception {
0 ignored issues
show
As per PSR2, the opening brace for this class should be on a new line.
Loading history...
139
140
}
141
142