1 | <?php |
---|
2 | |
---|
3 | /** |
---|
4 | * @file |
---|
5 | * API for handling file uploads and server file management. |
---|
6 | */ |
---|
7 | |
---|
8 | /** |
---|
9 | * @defgroup file File interface |
---|
10 | * @{ |
---|
11 | * Common file handling functions. |
---|
12 | */ |
---|
13 | |
---|
14 | define('FILE_DOWNLOADS_PUBLIC', 1); |
---|
15 | define('FILE_DOWNLOADS_PRIVATE', 2); |
---|
16 | define('FILE_CREATE_DIRECTORY', 1); |
---|
17 | define('FILE_MODIFY_PERMISSIONS', 2); |
---|
18 | define('FILE_EXISTS_RENAME', 0); |
---|
19 | define('FILE_EXISTS_REPLACE', 1); |
---|
20 | define('FILE_EXISTS_ERROR', 2); |
---|
21 | |
---|
22 | /** |
---|
23 | * A files status can be one of two values: temporary or permanent. The status |
---|
24 | * for each file Drupal manages is stored in the {files} tables. If the status |
---|
25 | * is temporary Drupal's file garbage collection will delete the file and |
---|
26 | * remove it from the files table after a set period of time. |
---|
27 | * |
---|
28 | * If you wish to add custom statuses for use by contrib modules please expand as |
---|
29 | * binary flags and consider the first 8 bits reserved. (0,1,2,4,8,16,32,64,128) |
---|
30 | */ |
---|
31 | define('FILE_STATUS_TEMPORARY', 0); |
---|
32 | define('FILE_STATUS_PERMANENT', 1); |
---|
33 | |
---|
34 | /** |
---|
35 | * Create the download path to a file. |
---|
36 | * |
---|
37 | * @param $path A string containing the path of the file to generate URL for. |
---|
38 | * @return A string containing a URL that can be used to download the file. |
---|
39 | */ |
---|
40 | function file_create_url($path) { |
---|
41 | // Strip file_directory_path from $path. We only include relative paths in URLs. |
---|
42 | if (strpos($path, file_directory_path() .'/') === 0) { |
---|
43 | $path = trim(substr($path, strlen(file_directory_path())), '\\/'); |
---|
44 | } |
---|
45 | switch (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC)) { |
---|
46 | case FILE_DOWNLOADS_PUBLIC: |
---|
47 | return $GLOBALS['base_url'] .'/'. file_directory_path() .'/'. str_replace('\\', '/', $path); |
---|
48 | case FILE_DOWNLOADS_PRIVATE: |
---|
49 | return url('system/files/'. $path, array('absolute' => TRUE)); |
---|
50 | } |
---|
51 | } |
---|
52 | |
---|
53 | /** |
---|
54 | * Make sure the destination is a complete path and resides in the file system |
---|
55 | * directory, if it is not prepend the file system directory. |
---|
56 | * |
---|
57 | * @param $dest A string containing the path to verify. If this value is |
---|
58 | * omitted, Drupal's 'files' directory will be used. |
---|
59 | * @return A string containing the path to file, with file system directory |
---|
60 | * appended if necessary, or FALSE if the path is invalid (i.e. outside the |
---|
61 | * configured 'files' or temp directories). |
---|
62 | */ |
---|
63 | function file_create_path($dest = 0) { |
---|
64 | $file_path = file_directory_path(); |
---|
65 | if (!$dest) { |
---|
66 | return $file_path; |
---|
67 | } |
---|
68 | // file_check_location() checks whether the destination is inside the Drupal files directory. |
---|
69 | if (file_check_location($dest, $file_path)) { |
---|
70 | return $dest; |
---|
71 | } |
---|
72 | // check if the destination is instead inside the Drupal temporary files directory. |
---|
73 | else if (file_check_location($dest, file_directory_temp())) { |
---|
74 | return $dest; |
---|
75 | } |
---|
76 | // Not found, try again with prefixed directory path. |
---|
77 | else if (file_check_location($file_path .'/'. $dest, $file_path)) { |
---|
78 | return $file_path .'/'. $dest; |
---|
79 | } |
---|
80 | // File not found. |
---|
81 | return FALSE; |
---|
82 | } |
---|
83 | |
---|
84 | /** |
---|
85 | * Checks whether a directory exists and is writable. |
---|
86 | * |
---|
87 | * Furthermore, the directory can optionally be created if it does not exist, |
---|
88 | * and/or be set to writable if it is currently not. Directories need to have |
---|
89 | * execute permission to be considered a directory by FTP servers. |
---|
90 | * |
---|
91 | * @param $directory |
---|
92 | * A string representing the directory path. |
---|
93 | * @param $mode |
---|
94 | * An optional bitmask containing the actions, if any, to be carried out on |
---|
95 | * the directory. Any combination of the actions FILE_CREATE_DIRECTORY and |
---|
96 | * FILE_MODIFY_PERMISSIONS is allowed. |
---|
97 | * @param $form_item |
---|
98 | * An optional string containing the name of a form item that any errors |
---|
99 | * will be attached to. Useful when the function validates a directory path |
---|
100 | * entered as a form value. An error will consequently prevent form submit |
---|
101 | * handlers from running, and instead display the form along with the |
---|
102 | * error messages. |
---|
103 | * |
---|
104 | * @return |
---|
105 | * FALSE if the directory does not exist or is not writable, even after |
---|
106 | * any optional actions have been carried out. Otherwise, TRUE is returned. |
---|
107 | */ |
---|
108 | function file_check_directory(&$directory, $mode = 0, $form_item = NULL) { |
---|
109 | $directory = rtrim($directory, '/\\'); |
---|
110 | |
---|
111 | // Check if directory exists. |
---|
112 | if (!is_dir($directory)) { |
---|
113 | if (($mode & FILE_CREATE_DIRECTORY) && @mkdir($directory)) { |
---|
114 | drupal_set_message(t('The directory %directory has been created.', array('%directory' => $directory))); |
---|
115 | @chmod($directory, 0775); // Necessary for non-webserver users. |
---|
116 | } |
---|
117 | else { |
---|
118 | if ($form_item) { |
---|
119 | form_set_error($form_item, t('The directory %directory does not exist.', array('%directory' => $directory))); |
---|
120 | } |
---|
121 | return FALSE; |
---|
122 | } |
---|
123 | } |
---|
124 | |
---|
125 | // Check to see if the directory is writable. |
---|
126 | if (!is_writable($directory)) { |
---|
127 | if (($mode & FILE_MODIFY_PERMISSIONS) && @chmod($directory, 0775)) { |
---|
128 | drupal_set_message(t('The permissions of directory %directory have been changed to make it writable.', array('%directory' => $directory))); |
---|
129 | } |
---|
130 | else { |
---|
131 | form_set_error($form_item, t('The directory %directory is not writable', array('%directory' => $directory))); |
---|
132 | watchdog('file system', 'The directory %directory is not writable, because it does not have the correct permissions set.', array('%directory' => $directory), WATCHDOG_ERROR); |
---|
133 | return FALSE; |
---|
134 | } |
---|
135 | } |
---|
136 | |
---|
137 | if (file_directory_path() == $directory || file_directory_temp() == $directory) { |
---|
138 | file_create_htaccess($directory, $form_item); |
---|
139 | } |
---|
140 | |
---|
141 | return TRUE; |
---|
142 | } |
---|
143 | |
---|
144 | /** |
---|
145 | * Creates a .htaccess file in the given directory. |
---|
146 | * |
---|
147 | * @param $directory |
---|
148 | * The directory. |
---|
149 | * @param $form_item |
---|
150 | * An optional string containing the name of a form item that any errors |
---|
151 | * will be attached to. Useful when called from file_check_directory() to |
---|
152 | * validate a directory path entered as a form value. An error will |
---|
153 | * consequently prevent form submit handlers from running, and instead |
---|
154 | * display the form along with the error messages. |
---|
155 | * @param $force_overwrite |
---|
156 | * Set to TRUE to attempt to overwrite the existing .htaccess file if one is |
---|
157 | * already present. Defaults to FALSE. |
---|
158 | */ |
---|
159 | function file_create_htaccess($directory, $form_item = NULL, $force_overwrite = FALSE) { |
---|
160 | if (!is_file("$directory/.htaccess") || $force_overwrite) { |
---|
161 | $htaccess_lines = file_htaccess_lines(); |
---|
162 | if (($fp = fopen("$directory/.htaccess", 'w')) && fputs($fp, $htaccess_lines)) { |
---|
163 | fclose($fp); |
---|
164 | chmod($directory .'/.htaccess', 0664); |
---|
165 | } |
---|
166 | else { |
---|
167 | $variables = array('%directory' => $directory, '!htaccess' => '<br />'. nl2br(check_plain($htaccess_lines))); |
---|
168 | if ($form_item) { |
---|
169 | form_set_error($form_item, t("Security warning: Couldn't write .htaccess file. Please create a .htaccess file in your %directory directory which contains the following lines: <code>!htaccess</code>", $variables)); |
---|
170 | } |
---|
171 | watchdog('security', "Security warning: Couldn't write .htaccess file. Please create a .htaccess file in your %directory directory which contains the following lines: <code>!htaccess</code>", $variables, WATCHDOG_ERROR); |
---|
172 | } |
---|
173 | } |
---|
174 | } |
---|
175 | |
---|
176 | /** |
---|
177 | * Returns the standard .htaccess lines that Drupal writes to file directories. |
---|
178 | * |
---|
179 | * @return |
---|
180 | * A string representing the desired contents of the .htaccess file. |
---|
181 | * |
---|
182 | * @see file_create_htaccess() |
---|
183 | */ |
---|
184 | function file_htaccess_lines() { |
---|
185 | $lines = <<<EOF |
---|
186 | # Turn off all options we don't need. |
---|
187 | Options None |
---|
188 | Options +FollowSymLinks |
---|
189 | |
---|
190 | # Set the catch-all handler to prevent scripts from being executed. |
---|
191 | SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006 |
---|
192 | <Files *> |
---|
193 | # Override the handler again if we're run later in the evaluation list. |
---|
194 | SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003 |
---|
195 | </Files> |
---|
196 | |
---|
197 | # If we know how to do it safely, disable the PHP engine entirely. |
---|
198 | <IfModule mod_php5.c> |
---|
199 | php_flag engine off |
---|
200 | </IfModule> |
---|
201 | # PHP 4, Apache 1. |
---|
202 | <IfModule mod_php4.c> |
---|
203 | php_flag engine off |
---|
204 | </IfModule> |
---|
205 | # PHP 4, Apache 2. |
---|
206 | <IfModule sapi_apache2.c> |
---|
207 | php_flag engine off |
---|
208 | </IfModule> |
---|
209 | EOF; |
---|
210 | |
---|
211 | return $lines; |
---|
212 | } |
---|
213 | |
---|
214 | /** |
---|
215 | * Checks path to see if it is a directory, or a dir/file. |
---|
216 | * |
---|
217 | * @param $path A string containing a file path. This will be set to the |
---|
218 | * directory's path. |
---|
219 | * @return If the directory is not in a Drupal writable directory, FALSE is |
---|
220 | * returned. Otherwise, the base name of the path is returned. |
---|
221 | */ |
---|
222 | function file_check_path(&$path) { |
---|
223 | // Check if path is a directory. |
---|
224 | if (file_check_directory($path)) { |
---|
225 | return ''; |
---|
226 | } |
---|
227 | |
---|
228 | // Check if path is a possible dir/file. |
---|
229 | $filename = basename($path); |
---|
230 | $path = dirname($path); |
---|
231 | if (file_check_directory($path)) { |
---|
232 | return $filename; |
---|
233 | } |
---|
234 | |
---|
235 | return FALSE; |
---|
236 | } |
---|
237 | |
---|
238 | /** |
---|
239 | * Check if a file is really located inside $directory. Should be used to make |
---|
240 | * sure a file specified is really located within the directory to prevent |
---|
241 | * exploits. |
---|
242 | * |
---|
243 | * @code |
---|
244 | * // Returns FALSE: |
---|
245 | * file_check_location('/www/example.com/files/../../../etc/passwd', '/www/example.com/files'); |
---|
246 | * @endcode |
---|
247 | * |
---|
248 | * @param $source A string set to the file to check. |
---|
249 | * @param $directory A string where the file should be located. |
---|
250 | * @return 0 for invalid path or the real path of the source. |
---|
251 | */ |
---|
252 | function file_check_location($source, $directory = '') { |
---|
253 | $check = realpath($source); |
---|
254 | if ($check) { |
---|
255 | $source = $check; |
---|
256 | } |
---|
257 | else { |
---|
258 | // This file does not yet exist |
---|
259 | $source = realpath(dirname($source)) .'/'. basename($source); |
---|
260 | } |
---|
261 | $directory = realpath($directory); |
---|
262 | if ($directory && strpos($source, $directory) !== 0) { |
---|
263 | return 0; |
---|
264 | } |
---|
265 | return $source; |
---|
266 | } |
---|
267 | |
---|
268 | /** |
---|
269 | * Copies a file to a new location. |
---|
270 | * |
---|
271 | * This is a powerful function that in many ways performs like an advanced |
---|
272 | * version of copy(). |
---|
273 | * - Checks if $source and $dest are valid and readable/writable. |
---|
274 | * - Performs a file copy if $source is not equal to $dest. |
---|
275 | * - If file already exists in $dest either the call will error out, replace the |
---|
276 | * file or rename the file based on the $replace parameter. |
---|
277 | * |
---|
278 | * @param $source |
---|
279 | * Either a string specifying the file location of the original file or an |
---|
280 | * object containing a 'filepath' property. This parameter is passed by |
---|
281 | * reference and will contain the resulting destination filename in case of |
---|
282 | * success. |
---|
283 | * @param $dest |
---|
284 | * A string containing the directory $source should be copied to. If this |
---|
285 | * value is omitted, Drupal's 'files' directory will be used. |
---|
286 | * @param $replace |
---|
287 | * Replace behavior when the destination file already exists. |
---|
288 | * - FILE_EXISTS_REPLACE: Replace the existing file. |
---|
289 | * - FILE_EXISTS_RENAME: Append _{incrementing number} until the filename is |
---|
290 | * unique. |
---|
291 | * - FILE_EXISTS_ERROR: Do nothing and return FALSE. |
---|
292 | * @return |
---|
293 | * TRUE for success, FALSE for failure. |
---|
294 | */ |
---|
295 | function file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) { |
---|
296 | $dest = file_create_path($dest); |
---|
297 | |
---|
298 | $directory = $dest; |
---|
299 | $basename = file_check_path($directory); |
---|
300 | |
---|
301 | // Make sure we at least have a valid directory. |
---|
302 | if ($basename === FALSE) { |
---|
303 | $source = is_object($source) ? $source->filepath : $source; |
---|
304 | drupal_set_message(t('The selected file %file could not be uploaded, because the destination %directory is not properly configured.', array('%file' => $source, '%directory' => $dest)), 'error'); |
---|
305 | watchdog('file system', 'The selected file %file could not be uploaded, because the destination %directory could not be found, or because its permissions do not allow the file to be written.', array('%file' => $source, '%directory' => $dest), WATCHDOG_ERROR); |
---|
306 | return 0; |
---|
307 | } |
---|
308 | |
---|
309 | // Process a file upload object. |
---|
310 | if (is_object($source)) { |
---|
311 | $file = $source; |
---|
312 | $source = $file->filepath; |
---|
313 | if (!$basename) { |
---|
314 | $basename = $file->filename; |
---|
315 | } |
---|
316 | } |
---|
317 | |
---|
318 | $source = realpath($source); |
---|
319 | if (!file_exists($source)) { |
---|
320 | drupal_set_message(t('The selected file %file could not be copied, because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $source)), 'error'); |
---|
321 | return 0; |
---|
322 | } |
---|
323 | |
---|
324 | // If the destination file is not specified then use the filename of the source file. |
---|
325 | $basename = $basename ? $basename : basename($source); |
---|
326 | $dest = $directory .'/'. $basename; |
---|
327 | |
---|
328 | // Make sure source and destination filenames are not the same, makes no sense |
---|
329 | // to copy it if they are. In fact copying the file will most likely result in |
---|
330 | // a 0 byte file. Which is bad. Real bad. |
---|
331 | if ($source != realpath($dest)) { |
---|
332 | if (!$dest = file_destination($dest, $replace)) { |
---|
333 | drupal_set_message(t('The selected file %file could not be copied, because a file by that name already exists in the destination.', array('%file' => $source)), 'error'); |
---|
334 | return FALSE; |
---|
335 | } |
---|
336 | |
---|
337 | if (!@copy($source, $dest)) { |
---|
338 | drupal_set_message(t('The selected file %file could not be copied.', array('%file' => $source)), 'error'); |
---|
339 | return 0; |
---|
340 | } |
---|
341 | |
---|
342 | // Give everyone read access so that FTP'd users or |
---|
343 | // non-webserver users can see/read these files, |
---|
344 | // and give group write permissions so group members |
---|
345 | // can alter files uploaded by the webserver. |
---|
346 | @chmod($dest, 0664); |
---|
347 | } |
---|
348 | |
---|
349 | if (isset($file) && is_object($file)) { |
---|
350 | $file->filename = $basename; |
---|
351 | $file->filepath = $dest; |
---|
352 | $source = $file; |
---|
353 | } |
---|
354 | else { |
---|
355 | $source = $dest; |
---|
356 | } |
---|
357 | |
---|
358 | return 1; // Everything went ok. |
---|
359 | } |
---|
360 | |
---|
361 | /** |
---|
362 | * Determines the destination path for a file depending on how replacement of |
---|
363 | * existing files should be handled. |
---|
364 | * |
---|
365 | * @param $destination A string specifying the desired path. |
---|
366 | * @param $replace Replace behavior when the destination file already exists. |
---|
367 | * - FILE_EXISTS_REPLACE - Replace the existing file |
---|
368 | * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is |
---|
369 | * unique |
---|
370 | * - FILE_EXISTS_ERROR - Do nothing and return FALSE. |
---|
371 | * @return The destination file path or FALSE if the file already exists and |
---|
372 | * FILE_EXISTS_ERROR was specified. |
---|
373 | */ |
---|
374 | function file_destination($destination, $replace) { |
---|
375 | if (file_exists($destination)) { |
---|
376 | switch ($replace) { |
---|
377 | case FILE_EXISTS_RENAME: |
---|
378 | $basename = basename($destination); |
---|
379 | $directory = dirname($destination); |
---|
380 | $destination = file_create_filename($basename, $directory); |
---|
381 | break; |
---|
382 | |
---|
383 | case FILE_EXISTS_ERROR: |
---|
384 | drupal_set_message(t('The selected file %file could not be copied, because a file by that name already exists in the destination.', array('%file' => $destination)), 'error'); |
---|
385 | return FALSE; |
---|
386 | } |
---|
387 | } |
---|
388 | return $destination; |
---|
389 | } |
---|
390 | |
---|
391 | /** |
---|
392 | * Moves a file to a new location. |
---|
393 | * |
---|
394 | * - Checks if $source and $dest are valid and readable/writable. |
---|
395 | * - Performs a file move if $source is not equal to $dest. |
---|
396 | * - If file already exists in $dest either the call will error out, replace the |
---|
397 | * file or rename the file based on the $replace parameter. |
---|
398 | * |
---|
399 | * @param $source |
---|
400 | * Either a string specifying the file location of the original file or an |
---|
401 | * object containing a 'filepath' property. This parameter is passed by |
---|
402 | * reference and will contain the resulting destination filename in case of |
---|
403 | * success. |
---|
404 | * @param $dest |
---|
405 | * A string containing the directory $source should be copied to. If this |
---|
406 | * value is omitted, Drupal's 'files' directory will be used. |
---|
407 | * @param $replace |
---|
408 | * Replace behavior when the destination file already exists. |
---|
409 | * - FILE_EXISTS_REPLACE: Replace the existing file. |
---|
410 | * - FILE_EXISTS_RENAME: Append _{incrementing number} until the filename is |
---|
411 | * unique. |
---|
412 | * - FILE_EXISTS_ERROR: Do nothing and return FALSE. |
---|
413 | * @return |
---|
414 | * TRUE for success, FALSE for failure. |
---|
415 | */ |
---|
416 | function file_move(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) { |
---|
417 | $path_original = is_object($source) ? $source->filepath : $source; |
---|
418 | |
---|
419 | if (file_copy($source, $dest, $replace)) { |
---|
420 | $path_current = is_object($source) ? $source->filepath : $source; |
---|
421 | |
---|
422 | if ($path_original == $path_current || file_delete($path_original)) { |
---|
423 | return 1; |
---|
424 | } |
---|
425 | drupal_set_message(t('The removal of the original file %file has failed.', array('%file' => $path_original)), 'error'); |
---|
426 | } |
---|
427 | return 0; |
---|
428 | } |
---|
429 | |
---|
430 | /** |
---|
431 | * Modify a filename as needed for security purposes. |
---|
432 | * |
---|
433 | * Munging a file name prevents unknown file extensions from masking exploit |
---|
434 | * files. When web servers such as Apache decide how to process a URL request, |
---|
435 | * they use the file extension. If the extension is not recognized, Apache |
---|
436 | * skips that extension and uses the previous file extension. For example, if |
---|
437 | * the file being requested is exploit.php.pps, and Apache does not recognize |
---|
438 | * the '.pps' extension, it treats the file as PHP and executes it. To make |
---|
439 | * this file name safe for Apache and prevent it from executing as PHP, the |
---|
440 | * .php extension is "munged" into .php_, making the safe file name |
---|
441 | * exploit.php_.pps. |
---|
442 | * |
---|
443 | * Specifically, this function adds an underscore to all extensions that are |
---|
444 | * between 2 and 5 characters in length, internal to the file name, and not |
---|
445 | * included in $extensions. |
---|
446 | * |
---|
447 | * Function behavior is also controlled by the Drupal variable |
---|
448 | * 'allow_insecure_uploads'. If 'allow_insecure_uploads' evaluates to TRUE, no |
---|
449 | * alterations will be made, if it evaluates to FALSE, the filename is 'munged'. |
---|
450 | * |
---|
451 | * @param $filename |
---|
452 | * File name to modify. |
---|
453 | * @param $extensions |
---|
454 | * A space-separated list of extensions that should not be altered. |
---|
455 | * @param $alerts |
---|
456 | * If TRUE, drupal_set_message() will be called to display a message if the |
---|
457 | * file name was changed. |
---|
458 | * |
---|
459 | * @return |
---|
460 | * The potentially modified $filename. |
---|
461 | */ |
---|
462 | function file_munge_filename($filename, $extensions, $alerts = TRUE) { |
---|
463 | $original = $filename; |
---|
464 | |
---|
465 | // Allow potentially insecure uploads for very savvy users and admin |
---|
466 | if (!variable_get('allow_insecure_uploads', 0)) { |
---|
467 | // Remove any null bytes. See http://php.net/manual/en/security.filesystem.nullbytes.php |
---|
468 | $filename = str_replace(chr(0), '', $filename); |
---|
469 | |
---|
470 | $whitelist = array_unique(explode(' ', trim($extensions))); |
---|
471 | |
---|
472 | // Split the filename up by periods. The first part becomes the basename |
---|
473 | // the last part the final extension. |
---|
474 | $filename_parts = explode('.', $filename); |
---|
475 | $new_filename = array_shift($filename_parts); // Remove file basename. |
---|
476 | $final_extension = array_pop($filename_parts); // Remove final extension. |
---|
477 | |
---|
478 | // Loop through the middle parts of the name and add an underscore to the |
---|
479 | // end of each section that could be a file extension but isn't in the list |
---|
480 | // of allowed extensions. |
---|
481 | foreach ($filename_parts as $filename_part) { |
---|
482 | $new_filename .= '.'. $filename_part; |
---|
483 | if (!in_array($filename_part, $whitelist) && preg_match("/^[a-zA-Z]{2,5}\d?$/", $filename_part)) { |
---|
484 | $new_filename .= '_'; |
---|
485 | } |
---|
486 | } |
---|
487 | $filename = $new_filename .'.'. $final_extension; |
---|
488 | |
---|
489 | if ($alerts && $original != $filename) { |
---|
490 | drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $filename))); |
---|
491 | } |
---|
492 | } |
---|
493 | |
---|
494 | return $filename; |
---|
495 | } |
---|
496 | |
---|
497 | /** |
---|
498 | * Undo the effect of upload_munge_filename(). |
---|
499 | * |
---|
500 | * @param $filename string filename |
---|
501 | * @return string |
---|
502 | */ |
---|
503 | function file_unmunge_filename($filename) { |
---|
504 | return str_replace('_.', '.', $filename); |
---|
505 | } |
---|
506 | |
---|
507 | /** |
---|
508 | * Create a full file path from a directory and filename. If a file with the |
---|
509 | * specified name already exists, an alternative will be used. |
---|
510 | * |
---|
511 | * @param $basename string filename |
---|
512 | * @param $directory string directory |
---|
513 | * @return |
---|
514 | */ |
---|
515 | function file_create_filename($basename, $directory) { |
---|
516 | $dest = $directory .'/'. $basename; |
---|
517 | |
---|
518 | if (file_exists($dest)) { |
---|
519 | // Destination file already exists, generate an alternative. |
---|
520 | if ($pos = strrpos($basename, '.')) { |
---|
521 | $name = substr($basename, 0, $pos); |
---|
522 | $ext = substr($basename, $pos); |
---|
523 | } |
---|
524 | else { |
---|
525 | $name = $basename; |
---|
526 | $ext = ''; |
---|
527 | } |
---|
528 | |
---|
529 | $counter = 0; |
---|
530 | do { |
---|
531 | $dest = $directory .'/'. $name .'_'. $counter++ . $ext; |
---|
532 | } while (file_exists($dest)); |
---|
533 | } |
---|
534 | |
---|
535 | return $dest; |
---|
536 | } |
---|
537 | |
---|
538 | /** |
---|
539 | * Delete a file. |
---|
540 | * |
---|
541 | * @param $path A string containing a file path. |
---|
542 | * @return TRUE for success, FALSE for failure. |
---|
543 | */ |
---|
544 | function file_delete($path) { |
---|
545 | if (is_file($path)) { |
---|
546 | return unlink($path); |
---|
547 | } |
---|
548 | } |
---|
549 | |
---|
550 | /** |
---|
551 | * Determine total disk space used by a single user or the whole filesystem. |
---|
552 | * |
---|
553 | * @param $uid |
---|
554 | * An optional user id. A NULL value returns the total space used |
---|
555 | * by all files. |
---|
556 | */ |
---|
557 | function file_space_used($uid = NULL) { |
---|
558 | if (isset($uid)) { |
---|
559 | return (int) db_result(db_query('SELECT SUM(filesize) FROM {files} WHERE uid = %d', $uid)); |
---|
560 | } |
---|
561 | return (int) db_result(db_query('SELECT SUM(filesize) FROM {files}')); |
---|
562 | } |
---|
563 | |
---|
564 | /** |
---|
565 | * Saves a file upload to a new location. |
---|
566 | * |
---|
567 | * The source file is validated as a proper upload and handled as such. |
---|
568 | * The file will be added to the files table as a temporary file. Temporary |
---|
569 | * files are periodically cleaned. To make the file permanent file call |
---|
570 | * file_set_status() to change its status. |
---|
571 | * |
---|
572 | * @param $source |
---|
573 | * A string specifying the name of the upload field to save. |
---|
574 | * @param $validators |
---|
575 | * (optional) An associative array of callback functions used to validate the |
---|
576 | * file. The keys are function names and the values arrays of callback |
---|
577 | * parameters which will be passed in after the file object. The |
---|
578 | * functions should return an array of error messages; an empty array |
---|
579 | * indicates that the file passed validation. The functions will be called in |
---|
580 | * the order specified. |
---|
581 | * @param $dest |
---|
582 | * A string containing the directory $source should be copied to. If this is |
---|
583 | * not provided or is not writable, the temporary directory will be used. |
---|
584 | * @param $replace |
---|
585 | * Replace behavior when the destination file already exists: |
---|
586 | * - FILE_EXISTS_REPLACE: Replace the existing file. |
---|
587 | * - FILE_EXISTS_RENAME: Append _{incrementing number} until the filename |
---|
588 | * is unique. |
---|
589 | * - FILE_EXISTS_ERROR: Do nothing and return FALSE. |
---|
590 | * |
---|
591 | * @return |
---|
592 | * An object containing the file information, or 0 in the event of an error. |
---|
593 | */ |
---|
594 | function file_save_upload($source, $validators = array(), $dest = FALSE, $replace = FILE_EXISTS_RENAME) { |
---|
595 | global $user; |
---|
596 | static $upload_cache; |
---|
597 | |
---|
598 | // Add our check of the file name length. |
---|
599 | $validators['file_validate_name_length'] = array(); |
---|
600 | |
---|
601 | // Return cached objects without processing since the file will have |
---|
602 | // already been processed and the paths in _FILES will be invalid. |
---|
603 | if (isset($upload_cache[$source])) { |
---|
604 | return $upload_cache[$source]; |
---|
605 | } |
---|
606 | |
---|
607 | // If a file was uploaded, process it. |
---|
608 | if (isset($_FILES['files']) && $_FILES['files']['name'][$source] && is_uploaded_file($_FILES['files']['tmp_name'][$source])) { |
---|
609 | // Check for file upload errors and return FALSE if a |
---|
610 | // lower level system error occurred. |
---|
611 | switch ($_FILES['files']['error'][$source]) { |
---|
612 | // @see http://php.net/manual/en/features.file-upload.errors.php |
---|
613 | case UPLOAD_ERR_OK: |
---|
614 | break; |
---|
615 | |
---|
616 | case UPLOAD_ERR_INI_SIZE: |
---|
617 | case UPLOAD_ERR_FORM_SIZE: |
---|
618 | drupal_set_message(t('The file %file could not be saved, because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $source, '%maxsize' => format_size(file_upload_max_size()))), 'error'); |
---|
619 | return 0; |
---|
620 | |
---|
621 | case UPLOAD_ERR_PARTIAL: |
---|
622 | case UPLOAD_ERR_NO_FILE: |
---|
623 | drupal_set_message(t('The file %file could not be saved, because the upload did not complete.', array('%file' => $source)), 'error'); |
---|
624 | return 0; |
---|
625 | |
---|
626 | // Unknown error |
---|
627 | default: |
---|
628 | drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $source)), 'error'); |
---|
629 | return 0; |
---|
630 | } |
---|
631 | |
---|
632 | // Build the list of non-munged extensions. |
---|
633 | // @todo: this should not be here. we need to figure out the right place. |
---|
634 | $extensions = ''; |
---|
635 | foreach ($user->roles as $rid => $name) { |
---|
636 | $extensions .= ' '. variable_get("upload_extensions_$rid", |
---|
637 | variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp')); |
---|
638 | } |
---|
639 | |
---|
640 | // Begin building file object. |
---|
641 | $file = new stdClass(); |
---|
642 | $file->filename = file_munge_filename(trim(basename($_FILES['files']['name'][$source]), '.'), $extensions); |
---|
643 | $file->filepath = $_FILES['files']['tmp_name'][$source]; |
---|
644 | $file->filemime = file_get_mimetype($file->filename); |
---|
645 | |
---|
646 | // If the destination is not provided, or is not writable, then use the |
---|
647 | // temporary directory. |
---|
648 | if (empty($dest) || file_check_path($dest) === FALSE) { |
---|
649 | $dest = file_directory_temp(); |
---|
650 | } |
---|
651 | |
---|
652 | $file->source = $source; |
---|
653 | $file->destination = file_destination(file_create_path($dest .'/'. $file->filename), $replace); |
---|
654 | $file->filesize = $_FILES['files']['size'][$source]; |
---|
655 | |
---|
656 | // Call the validation functions. |
---|
657 | $errors = array(); |
---|
658 | foreach ($validators as $function => $args) { |
---|
659 | array_unshift($args, $file); |
---|
660 | // Make sure $file is passed around by reference. |
---|
661 | $args[0] = &$file; |
---|
662 | $errors = array_merge($errors, call_user_func_array($function, $args)); |
---|
663 | } |
---|
664 | |
---|
665 | // Rename potentially executable files, to help prevent exploits. |
---|
666 | if (preg_match('/\.(php|pl|py|cgi|asp|js)$/i', $file->filename) && (substr($file->filename, -4) != '.txt')) { |
---|
667 | $file->filemime = 'text/plain'; |
---|
668 | $file->filepath .= '.txt'; |
---|
669 | $file->filename .= '.txt'; |
---|
670 | // As the file may be named example.php.txt, we need to munge again to |
---|
671 | // convert to example.php_.txt, then create the correct destination. |
---|
672 | $file->filename = file_munge_filename($file->filename, $extensions); |
---|
673 | $file->destination = file_destination(file_create_path($dest .'/'. $file->filename), $replace); |
---|
674 | } |
---|
675 | |
---|
676 | |
---|
677 | // Check for validation errors. |
---|
678 | if (!empty($errors)) { |
---|
679 | $message = t('The selected file %name could not be uploaded.', array('%name' => $file->filename)); |
---|
680 | if (count($errors) > 1) { |
---|
681 | $message .= '<ul><li>'. implode('</li><li>', $errors) .'</li></ul>'; |
---|
682 | } |
---|
683 | else { |
---|
684 | $message .= ' '. array_pop($errors); |
---|
685 | } |
---|
686 | form_set_error($source, $message); |
---|
687 | return 0; |
---|
688 | } |
---|
689 | |
---|
690 | // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary directory. |
---|
691 | // This overcomes open_basedir restrictions for future file operations. |
---|
692 | $file->filepath = $file->destination; |
---|
693 | if (!move_uploaded_file($_FILES['files']['tmp_name'][$source], $file->filepath)) { |
---|
694 | form_set_error($source, t('File upload error. Could not move uploaded file.')); |
---|
695 | watchdog('file', 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination' => $file->filepath)); |
---|
696 | return 0; |
---|
697 | } |
---|
698 | |
---|
699 | // If we made it this far it's safe to record this file in the database. |
---|
700 | $file->uid = $user->uid; |
---|
701 | $file->status = FILE_STATUS_TEMPORARY; |
---|
702 | $file->timestamp = time(); |
---|
703 | drupal_write_record('files', $file); |
---|
704 | |
---|
705 | // Add file to the cache. |
---|
706 | $upload_cache[$source] = $file; |
---|
707 | return $file; |
---|
708 | } |
---|
709 | return 0; |
---|
710 | } |
---|
711 | |
---|
712 | /** |
---|
713 | * Check for files with names longer than we can store in the database. |
---|
714 | * |
---|
715 | * @param $file |
---|
716 | * A Drupal file object. |
---|
717 | * @return |
---|
718 | * An array. If the file name is too long, it will contain an error message. |
---|
719 | */ |
---|
720 | function file_validate_name_length($file) { |
---|
721 | $errors = array(); |
---|
722 | |
---|
723 | if (strlen($file->filename) > 255) { |
---|
724 | $errors[] = t('Its name exceeds the 255 characters limit. Please rename the file and try again.'); |
---|
725 | } |
---|
726 | return $errors; |
---|
727 | } |
---|
728 | |
---|
729 | /** |
---|
730 | * Check that the filename ends with an allowed extension. This check is not |
---|
731 | * enforced for the user #1. |
---|
732 | * |
---|
733 | * @param $file |
---|
734 | * A Drupal file object. |
---|
735 | * @param $extensions |
---|
736 | * A string with a space separated list of allowed file extensions, not |
---|
737 | * including the period. For example, 'bmp jpg gif png'. |
---|
738 | * |
---|
739 | * @return |
---|
740 | * An array. If the file extension is not allowed, it will contain an error |
---|
741 | * message. |
---|
742 | */ |
---|
743 | function file_validate_extensions($file, $extensions) { |
---|
744 | global $user; |
---|
745 | |
---|
746 | $errors = array(); |
---|
747 | |
---|
748 | // Bypass validation for uid = 1. |
---|
749 | if ($user->uid != 1) { |
---|
750 | $regex = '/\.('. @ereg_replace(' +', '|', preg_quote($extensions)) .')$/i'; |
---|
751 | if (!preg_match($regex, $file->filename)) { |
---|
752 | $errors[] = t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => $extensions)); |
---|
753 | } |
---|
754 | } |
---|
755 | return $errors; |
---|
756 | } |
---|
757 | |
---|
758 | /** |
---|
759 | * Check that the file's size is below certain limits. This check is not |
---|
760 | * enforced for the user #1. |
---|
761 | * |
---|
762 | * @param $file |
---|
763 | * A Drupal file object. |
---|
764 | * @param $file_limit |
---|
765 | * An integer specifying the maximum file size in bytes. Zero indicates that |
---|
766 | * no limit should be enforced. |
---|
767 | * @param $user_limit |
---|
768 | * An integer specifying the maximum number of bytes the user is allowed. Zero |
---|
769 | * indicates that no limit should be enforced. |
---|
770 | * @return |
---|
771 | * An array. If the file size exceeds limits, it will contain an error message. |
---|
772 | */ |
---|
773 | function file_validate_size($file, $file_limit = 0, $user_limit = 0) { |
---|
774 | global $user; |
---|
775 | |
---|
776 | $errors = array(); |
---|
777 | |
---|
778 | // Bypass validation for uid = 1. |
---|
779 | if ($user->uid != 1) { |
---|
780 | if ($file_limit && $file->filesize > $file_limit) { |
---|
781 | $errors[] = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size($file->filesize), '%maxsize' => format_size($file_limit))); |
---|
782 | } |
---|
783 | |
---|
784 | // Save a query by only calling file_space_used() when a limit is provided. |
---|
785 | if ($user_limit && (file_space_used($user->uid) + $file->filesize) > $user_limit) { |
---|
786 | $errors[] = t('The file is %filesize which would exceed your disk quota of %quota.', array('%filesize' => format_size($file->filesize), '%quota' => format_size($user_limit))); |
---|
787 | } |
---|
788 | } |
---|
789 | return $errors; |
---|
790 | } |
---|
791 | |
---|
792 | /** |
---|
793 | * Check that the file is recognized by image_get_info() as an image. |
---|
794 | * |
---|
795 | * @param $file |
---|
796 | * A Drupal file object. |
---|
797 | * @return |
---|
798 | * An array. If the file is not an image, it will contain an error message. |
---|
799 | */ |
---|
800 | function file_validate_is_image(&$file) { |
---|
801 | $errors = array(); |
---|
802 | |
---|
803 | $info = image_get_info($file->filepath); |
---|
804 | if (!$info || empty($info['extension'])) { |
---|
805 | $errors[] = t('Only JPEG, PNG and GIF images are allowed.'); |
---|
806 | } |
---|
807 | |
---|
808 | return $errors; |
---|
809 | } |
---|
810 | |
---|
811 | /** |
---|
812 | * If the file is an image verify that its dimensions are within the specified |
---|
813 | * maximum and minimum dimensions. Non-image files will be ignored. |
---|
814 | * |
---|
815 | * @param $file |
---|
816 | * A Drupal file object. This function may resize the file affecting its size. |
---|
817 | * @param $maximum_dimensions |
---|
818 | * An optional string in the form WIDTHxHEIGHT e.g. '640x480' or '85x85'. If |
---|
819 | * an image toolkit is installed the image will be resized down to these |
---|
820 | * dimensions. A value of 0 indicates no restriction on size, so resizing |
---|
821 | * will be attempted. |
---|
822 | * @param $minimum_dimensions |
---|
823 | * An optional string in the form WIDTHxHEIGHT. This will check that the image |
---|
824 | * meets a minimum size. A value of 0 indicates no restriction. |
---|
825 | * @return |
---|
826 | * An array. If the file is an image and did not meet the requirements, it |
---|
827 | * will contain an error message. |
---|
828 | */ |
---|
829 | function file_validate_image_resolution(&$file, $maximum_dimensions = 0, $minimum_dimensions = 0) { |
---|
830 | $errors = array(); |
---|
831 | |
---|
832 | // Check first that the file is an image. |
---|
833 | if ($info = image_get_info($file->filepath)) { |
---|
834 | if ($maximum_dimensions) { |
---|
835 | // Check that it is smaller than the given dimensions. |
---|
836 | list($width, $height) = explode('x', $maximum_dimensions); |
---|
837 | if ($info['width'] > $width || $info['height'] > $height) { |
---|
838 | // Try to resize the image to fit the dimensions. |
---|
839 | if (image_get_toolkit() && image_scale($file->filepath, $file->filepath, $width, $height)) { |
---|
840 | drupal_set_message(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', array('%dimensions' => $maximum_dimensions))); |
---|
841 | |
---|
842 | // Clear the cached filesize and refresh the image information. |
---|
843 | clearstatcache(); |
---|
844 | $info = image_get_info($file->filepath); |
---|
845 | $file->filesize = $info['file_size']; |
---|
846 | } |
---|
847 | else { |
---|
848 | $errors[] = t('The image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => $maximum_dimensions)); |
---|
849 | } |
---|
850 | } |
---|
851 | } |
---|
852 | |
---|
853 | if ($minimum_dimensions) { |
---|
854 | // Check that it is larger than the given dimensions. |
---|
855 | list($width, $height) = explode('x', $minimum_dimensions); |
---|
856 | if ($info['width'] < $width || $info['height'] < $height) { |
---|
857 | $errors[] = t('The image is too small; the minimum dimensions are %dimensions pixels.', array('%dimensions' => $minimum_dimensions)); |
---|
858 | } |
---|
859 | } |
---|
860 | } |
---|
861 | |
---|
862 | return $errors; |
---|
863 | } |
---|
864 | |
---|
865 | /** |
---|
866 | * Save a string to the specified destination. |
---|
867 | * |
---|
868 | * @param $data A string containing the contents of the file. |
---|
869 | * @param $dest A string containing the destination location. |
---|
870 | * @param $replace Replace behavior when the destination file already exists. |
---|
871 | * - FILE_EXISTS_REPLACE - Replace the existing file |
---|
872 | * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique |
---|
873 | * - FILE_EXISTS_ERROR - Do nothing and return FALSE. |
---|
874 | * |
---|
875 | * @return A string containing the resulting filename or 0 on error |
---|
876 | */ |
---|
877 | function file_save_data($data, $dest, $replace = FILE_EXISTS_RENAME) { |
---|
878 | $temp = file_directory_temp(); |
---|
879 | // On Windows, tempnam() requires an absolute path, so we use realpath(). |
---|
880 | $file = tempnam(realpath($temp), 'file'); |
---|
881 | if (!$fp = fopen($file, 'wb')) { |
---|
882 | drupal_set_message(t('The file could not be created.'), 'error'); |
---|
883 | return 0; |
---|
884 | } |
---|
885 | fwrite($fp, $data); |
---|
886 | fclose($fp); |
---|
887 | |
---|
888 | if (!file_move($file, $dest, $replace)) { |
---|
889 | return 0; |
---|
890 | } |
---|
891 | |
---|
892 | return $file; |
---|
893 | } |
---|
894 | |
---|
895 | /** |
---|
896 | * Set the status of a file. |
---|
897 | * |
---|
898 | * @param $file |
---|
899 | * A Drupal file object. |
---|
900 | * @param $status |
---|
901 | * A status value to set the file to. One of: |
---|
902 | * - FILE_STATUS_PERMANENT |
---|
903 | * - FILE_STATUS_TEMPORARY |
---|
904 | * |
---|
905 | * @return FALSE on failure, TRUE on success and $file->status will contain the |
---|
906 | * status. |
---|
907 | */ |
---|
908 | function file_set_status(&$file, $status) { |
---|
909 | if (db_query('UPDATE {files} SET status = %d WHERE fid = %d', $status, $file->fid)) { |
---|
910 | $file->status = $status; |
---|
911 | return TRUE; |
---|
912 | } |
---|
913 | return FALSE; |
---|
914 | } |
---|
915 | |
---|
916 | /** |
---|
917 | * Transfer file using http to client. Pipes a file through Drupal to the |
---|
918 | * client. |
---|
919 | * |
---|
920 | * @param $source File to transfer. |
---|
921 | * @param $headers An array of http headers to send along with file. |
---|
922 | */ |
---|
923 | function file_transfer($source, $headers) { |
---|
924 | if (ob_get_level()) { |
---|
925 | ob_end_clean(); |
---|
926 | } |
---|
927 | |
---|
928 | // IE cannot download private files because it cannot store files downloaded |
---|
929 | // over HTTPS in the browser cache. The problem can be solved by sending |
---|
930 | // custom headers to IE. See http://support.microsoft.com/kb/323308/en-us |
---|
931 | if (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on')) { |
---|
932 | drupal_set_header('Cache-Control: private'); |
---|
933 | drupal_set_header('Pragma: private'); |
---|
934 | } |
---|
935 | |
---|
936 | foreach ($headers as $header) { |
---|
937 | // To prevent HTTP header injection, we delete new lines that are |
---|
938 | // not followed by a space or a tab. |
---|
939 | // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 |
---|
940 | $header = preg_replace('/\r?\n(?!\t| )/', '', $header); |
---|
941 | drupal_set_header($header); |
---|
942 | } |
---|
943 | |
---|
944 | $source = file_create_path($source); |
---|
945 | |
---|
946 | // Transfer file in 1024 byte chunks to save memory usage. |
---|
947 | if ($fd = fopen($source, 'rb')) { |
---|
948 | while (!feof($fd)) { |
---|
949 | print fread($fd, 1024); |
---|
950 | } |
---|
951 | fclose($fd); |
---|
952 | } |
---|
953 | else { |
---|
954 | drupal_not_found(); |
---|
955 | } |
---|
956 | exit(); |
---|
957 | } |
---|
958 | |
---|
959 | /** |
---|
960 | * Call modules that implement hook_file_download() to find out if a file is |
---|
961 | * accessible and what headers it should be transferred with. If a module |
---|
962 | * returns -1 drupal_access_denied() will be returned. If one or more modules |
---|
963 | * returned headers the download will start with the returned headers. If no |
---|
964 | * modules respond drupal_not_found() will be returned. |
---|
965 | */ |
---|
966 | function file_download() { |
---|
967 | // Merge remainder of arguments from GET['q'], into relative file path. |
---|
968 | $args = func_get_args(); |
---|
969 | $filepath = implode('/', $args); |
---|
970 | |
---|
971 | // Maintain compatibility with old ?file=paths saved in node bodies. |
---|
972 | if (isset($_GET['file'])) { |
---|
973 | $filepath = $_GET['file']; |
---|
974 | } |
---|
975 | |
---|
976 | if (file_exists(file_create_path($filepath))) { |
---|
977 | $headers = file_download_headers($filepath); |
---|
978 | if (count($headers)) { |
---|
979 | file_transfer($filepath, $headers); |
---|
980 | } |
---|
981 | else { |
---|
982 | return drupal_access_denied(); |
---|
983 | } |
---|
984 | } |
---|
985 | return drupal_not_found(); |
---|
986 | } |
---|
987 | |
---|
988 | /** |
---|
989 | * Retrieves headers for a private file download. |
---|
990 | * |
---|
991 | * Calls all module implementations of hook_file_download() to retrieve headers |
---|
992 | * for files by the module that originally provided the file. The presence of |
---|
993 | * returned headers indicates the current user has access to the file. |
---|
994 | * |
---|
995 | * @param $filepath |
---|
996 | * The path for the file whose headers should be retrieved. |
---|
997 | * |
---|
998 | * @return |
---|
999 | * If access is allowed, headers for the file, suitable for passing to |
---|
1000 | * file_transfer(). If access is not allowed, an empty array will be returned. |
---|
1001 | * |
---|
1002 | * @see file_transfer() |
---|
1003 | * @see file_download_access() |
---|
1004 | * @see hook_file_downlaod() |
---|
1005 | */ |
---|
1006 | function file_download_headers($filepath) { |
---|
1007 | $headers = module_invoke_all('file_download', $filepath); |
---|
1008 | if (in_array(-1, $headers)) { |
---|
1009 | // Throw away the headers received so far. |
---|
1010 | $headers = array(); |
---|
1011 | } |
---|
1012 | return $headers; |
---|
1013 | } |
---|
1014 | |
---|
1015 | /** |
---|
1016 | * Checks that the current user has access to a particular file. |
---|
1017 | * |
---|
1018 | * The return value of this function hinges on the return value from |
---|
1019 | * file_download_headers(), which is the function responsible for collecting |
---|
1020 | * access information through hook_file_download(). |
---|
1021 | * |
---|
1022 | * If immediately transferring the file to the browser and the headers will |
---|
1023 | * need to be retrieved, the return value of file_download_headers() should be |
---|
1024 | * used to determine access directly, so that access checks will not be run |
---|
1025 | * twice. |
---|
1026 | * |
---|
1027 | * @param $filepath |
---|
1028 | * The path for the file whose headers should be retrieved. |
---|
1029 | * |
---|
1030 | * @return |
---|
1031 | * Boolean TRUE if access is allowed. FALSE if access is not allowed. |
---|
1032 | * |
---|
1033 | * @see file_download_headers() |
---|
1034 | * @see hook_file_download() |
---|
1035 | */ |
---|
1036 | function file_download_access($filepath) { |
---|
1037 | return count(file_download_headers($filepath)) > 0; |
---|
1038 | } |
---|
1039 | |
---|
1040 | /** |
---|
1041 | * Finds all files that match a given mask in a given directory. |
---|
1042 | * |
---|
1043 | * Directories and files beginning with a period are excluded; this |
---|
1044 | * prevents hidden files and directories (such as SVN working directories) |
---|
1045 | * from being scanned. |
---|
1046 | * |
---|
1047 | * @param $dir |
---|
1048 | * The base directory for the scan, without trailing slash. |
---|
1049 | * @param $mask |
---|
1050 | * The regular expression of the files to find. |
---|
1051 | * @param $nomask |
---|
1052 | * An array of files/directories to ignore. |
---|
1053 | * @param $callback |
---|
1054 | * The callback function to call for each match. |
---|
1055 | * @param $recurse |
---|
1056 | * When TRUE, the directory scan will recurse the entire tree |
---|
1057 | * starting at the provided directory. |
---|
1058 | * @param $key |
---|
1059 | * The key to be used for the returned associative array of files. Possible |
---|
1060 | * values are "filename", for the path starting with $dir; "basename", for |
---|
1061 | * the basename of the file; and "name" for the name of the file without the |
---|
1062 | * extension. |
---|
1063 | * @param $min_depth |
---|
1064 | * Minimum depth of directories to return files from. |
---|
1065 | * @param $depth |
---|
1066 | * Current depth of recursion. This parameter is only used internally and |
---|
1067 | * should not be passed in. |
---|
1068 | * |
---|
1069 | * @return |
---|
1070 | * An associative array (keyed on the provided key) of objects with |
---|
1071 | * "filename", "basename", and "name" members corresponding to the |
---|
1072 | * matching files. |
---|
1073 | */ |
---|
1074 | function file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0) { |
---|
1075 | $key = (in_array($key, array('filename', 'basename', 'name')) ? $key : 'filename'); |
---|
1076 | $files = array(); |
---|
1077 | |
---|
1078 | if (is_dir($dir) && $handle = opendir($dir)) { |
---|
1079 | while (FALSE !== ($file = readdir($handle))) { |
---|
1080 | if (!in_array($file, $nomask) && $file[0] != '.') { |
---|
1081 | if (is_dir("$dir/$file") && $recurse) { |
---|
1082 | // Give priority to files in this folder by merging them in after any subdirectory files. |
---|
1083 | $files = array_merge(file_scan_directory("$dir/$file", $mask, $nomask, $callback, $recurse, $key, $min_depth, $depth + 1), $files); |
---|
1084 | } |
---|
1085 | elseif ($depth >= $min_depth && @ereg($mask, $file)) { |
---|
1086 | // Always use this match over anything already set in $files with the same $$key. |
---|
1087 | $filename = "$dir/$file"; |
---|
1088 | $basename = basename($file); |
---|
1089 | $name = substr($basename, 0, strrpos($basename, '.')); |
---|
1090 | $files[$$key] = new stdClass(); |
---|
1091 | $files[$$key]->filename = $filename; |
---|
1092 | $files[$$key]->basename = $basename; |
---|
1093 | $files[$$key]->name = $name; |
---|
1094 | if ($callback) { |
---|
1095 | $callback($filename); |
---|
1096 | } |
---|
1097 | } |
---|
1098 | } |
---|
1099 | } |
---|
1100 | |
---|
1101 | closedir($handle); |
---|
1102 | } |
---|
1103 | |
---|
1104 | return $files; |
---|
1105 | } |
---|
1106 | |
---|
1107 | /** |
---|
1108 | * Determine the default temporary directory. |
---|
1109 | * |
---|
1110 | * @return A string containing a temp directory. |
---|
1111 | */ |
---|
1112 | function file_directory_temp() { |
---|
1113 | $temporary_directory = variable_get('file_directory_temp', NULL); |
---|
1114 | |
---|
1115 | if (is_null($temporary_directory)) { |
---|
1116 | $directories = array(); |
---|
1117 | |
---|
1118 | // Has PHP been set with an upload_tmp_dir? |
---|
1119 | if (ini_get('upload_tmp_dir')) { |
---|
1120 | $directories[] = ini_get('upload_tmp_dir'); |
---|
1121 | } |
---|
1122 | |
---|
1123 | // Operating system specific dirs. |
---|
1124 | if (substr(PHP_OS, 0, 3) == 'WIN') { |
---|
1125 | $directories[] = 'c:\\windows\\temp'; |
---|
1126 | $directories[] = 'c:\\winnt\\temp'; |
---|
1127 | $path_delimiter = '\\'; |
---|
1128 | } |
---|
1129 | else { |
---|
1130 | $directories[] = '/tmp'; |
---|
1131 | $path_delimiter = '/'; |
---|
1132 | } |
---|
1133 | |
---|
1134 | foreach ($directories as $directory) { |
---|
1135 | if (!$temporary_directory && is_dir($directory)) { |
---|
1136 | $temporary_directory = $directory; |
---|
1137 | } |
---|
1138 | } |
---|
1139 | |
---|
1140 | // if a directory has been found, use it, otherwise default to 'files/tmp' or 'files\\tmp'; |
---|
1141 | $temporary_directory = $temporary_directory ? $temporary_directory : file_directory_path() . $path_delimiter .'tmp'; |
---|
1142 | variable_set('file_directory_temp', $temporary_directory); |
---|
1143 | } |
---|
1144 | |
---|
1145 | return $temporary_directory; |
---|
1146 | } |
---|
1147 | |
---|
1148 | /** |
---|
1149 | * Determine the default 'files' directory. |
---|
1150 | * |
---|
1151 | * @return A string containing the path to Drupal's 'files' directory. |
---|
1152 | */ |
---|
1153 | function file_directory_path() { |
---|
1154 | return variable_get('file_directory_path', conf_path() .'/files'); |
---|
1155 | } |
---|
1156 | |
---|
1157 | /** |
---|
1158 | * Determine the maximum file upload size by querying the PHP settings. |
---|
1159 | * |
---|
1160 | * @return |
---|
1161 | * A file size limit in bytes based on the PHP upload_max_filesize and post_max_size |
---|
1162 | */ |
---|
1163 | function file_upload_max_size() { |
---|
1164 | static $max_size = -1; |
---|
1165 | |
---|
1166 | if ($max_size < 0) { |
---|
1167 | $upload_max = parse_size(ini_get('upload_max_filesize')); |
---|
1168 | $post_max = parse_size(ini_get('post_max_size')); |
---|
1169 | $max_size = ($upload_max < $post_max) ? $upload_max : $post_max; |
---|
1170 | } |
---|
1171 | return $max_size; |
---|
1172 | } |
---|
1173 | |
---|
1174 | /** |
---|
1175 | * Determine an Internet Media Type, or MIME type from a filename. |
---|
1176 | * |
---|
1177 | * @param $filename |
---|
1178 | * Name of the file, including extension. |
---|
1179 | * @param $mapping |
---|
1180 | * An optional array of extension to media type mappings in the form |
---|
1181 | * 'extension1|extension2|...' => 'type'. |
---|
1182 | * |
---|
1183 | * @return |
---|
1184 | * The internet media type registered for the extension or application/octet-stream for unknown extensions. |
---|
1185 | */ |
---|
1186 | function file_get_mimetype($filename, $mapping = NULL) { |
---|
1187 | if (!is_array($mapping)) { |
---|
1188 | $mapping = variable_get('mime_extension_mapping', array( |
---|
1189 | 'ez' => 'application/andrew-inset', |
---|
1190 | 'atom' => 'application/atom', |
---|
1191 | 'atomcat' => 'application/atomcat+xml', |
---|
1192 | 'atomsrv' => 'application/atomserv+xml', |
---|
1193 | 'cap|pcap' => 'application/cap', |
---|
1194 | 'cu' => 'application/cu-seeme', |
---|
1195 | 'tsp' => 'application/dsptype', |
---|
1196 | 'spl' => 'application/x-futuresplash', |
---|
1197 | 'hta' => 'application/hta', |
---|
1198 | 'jar' => 'application/java-archive', |
---|
1199 | 'ser' => 'application/java-serialized-object', |
---|
1200 | 'class' => 'application/java-vm', |
---|
1201 | 'hqx' => 'application/mac-binhex40', |
---|
1202 | 'cpt' => 'image/x-corelphotopaint', |
---|
1203 | 'nb' => 'application/mathematica', |
---|
1204 | 'mdb' => 'application/msaccess', |
---|
1205 | 'doc|dot' => 'application/msword', |
---|
1206 | 'bin' => 'application/octet-stream', |
---|
1207 | 'oda' => 'application/oda', |
---|
1208 | 'ogg|ogx' => 'application/ogg', |
---|
1209 | 'pdf' => 'application/pdf', |
---|
1210 | 'key' => 'application/pgp-keys', |
---|
1211 | 'pgp' => 'application/pgp-signature', |
---|
1212 | 'prf' => 'application/pics-rules', |
---|
1213 | 'ps|ai|eps' => 'application/postscript', |
---|
1214 | 'rar' => 'application/rar', |
---|
1215 | 'rdf' => 'application/rdf+xml', |
---|
1216 | 'rss' => 'application/rss+xml', |
---|
1217 | 'rtf' => 'application/rtf', |
---|
1218 | 'smi|smil' => 'application/smil', |
---|
1219 | 'wpd' => 'application/wordperfect', |
---|
1220 | 'wp5' => 'application/wordperfect5.1', |
---|
1221 | 'xhtml|xht' => 'application/xhtml+xml', |
---|
1222 | 'xml|xsl' => 'application/xml', |
---|
1223 | 'zip' => 'application/zip', |
---|
1224 | 'cdy' => 'application/vnd.cinderella', |
---|
1225 | 'kml' => 'application/vnd.google-earth.kml+xml', |
---|
1226 | 'kmz' => 'application/vnd.google-earth.kmz', |
---|
1227 | 'xul' => 'application/vnd.mozilla.xul+xml', |
---|
1228 | 'xls|xlb|xlt' => 'application/vnd.ms-excel', |
---|
1229 | 'cat' => 'application/vnd.ms-pki.seccat', |
---|
1230 | 'stl' => 'application/vnd.ms-pki.stl', |
---|
1231 | 'ppt|pps' => 'application/vnd.ms-powerpoint', |
---|
1232 | 'odc' => 'application/vnd.oasis.opendocument.chart', |
---|
1233 | 'odb' => 'application/vnd.oasis.opendocument.database', |
---|
1234 | 'odf' => 'application/vnd.oasis.opendocument.formula', |
---|
1235 | 'odg' => 'application/vnd.oasis.opendocument.graphics', |
---|
1236 | 'otg' => 'application/vnd.oasis.opendocument.graphics-template', |
---|
1237 | 'odi' => 'application/vnd.oasis.opendocument.image', |
---|
1238 | 'odp' => 'application/vnd.oasis.opendocument.presentation', |
---|
1239 | 'otp' => 'application/vnd.oasis.opendocument.presentation-template', |
---|
1240 | 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', |
---|
1241 | 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', |
---|
1242 | 'odt' => 'application/vnd.oasis.opendocument.text', |
---|
1243 | 'odm' => 'application/vnd.oasis.opendocument.text-master', |
---|
1244 | 'ott' => 'application/vnd.oasis.opendocument.text-template', |
---|
1245 | 'oth' => 'application/vnd.oasis.opendocument.text-web', |
---|
1246 | 'docm' => 'application/vnd.ms-word.document.macroEnabled.12', |
---|
1247 | 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', |
---|
1248 | 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', |
---|
1249 | 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', |
---|
1250 | 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', |
---|
1251 | 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', |
---|
1252 | 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', |
---|
1253 | 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', |
---|
1254 | 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', |
---|
1255 | 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', |
---|
1256 | 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', |
---|
1257 | 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', |
---|
1258 | 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', |
---|
1259 | 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', |
---|
1260 | 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', |
---|
1261 | 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', |
---|
1262 | 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', |
---|
1263 | 'cod' => 'application/vnd.rim.cod', |
---|
1264 | 'mmf' => 'application/vnd.smaf', |
---|
1265 | 'sdc' => 'application/vnd.stardivision.calc', |
---|
1266 | 'sds' => 'application/vnd.stardivision.chart', |
---|
1267 | 'sda' => 'application/vnd.stardivision.draw', |
---|
1268 | 'sdd' => 'application/vnd.stardivision.impress', |
---|
1269 | 'sdf' => 'application/vnd.stardivision.math', |
---|
1270 | 'sdw' => 'application/vnd.stardivision.writer', |
---|
1271 | 'sgl' => 'application/vnd.stardivision.writer-global', |
---|
1272 | 'sxc' => 'application/vnd.sun.xml.calc', |
---|
1273 | 'stc' => 'application/vnd.sun.xml.calc.template', |
---|
1274 | 'sxd' => 'application/vnd.sun.xml.draw', |
---|
1275 | 'std' => 'application/vnd.sun.xml.draw.template', |
---|
1276 | 'sxi' => 'application/vnd.sun.xml.impress', |
---|
1277 | 'sti' => 'application/vnd.sun.xml.impress.template', |
---|
1278 | 'sxm' => 'application/vnd.sun.xml.math', |
---|
1279 | 'sxw' => 'application/vnd.sun.xml.writer', |
---|
1280 | 'sxg' => 'application/vnd.sun.xml.writer.global', |
---|
1281 | 'stw' => 'application/vnd.sun.xml.writer.template', |
---|
1282 | 'sis' => 'application/vnd.symbian.install', |
---|
1283 | 'vsd' => 'application/vnd.visio', |
---|
1284 | 'wbxml' => 'application/vnd.wap.wbxml', |
---|
1285 | 'wmlc' => 'application/vnd.wap.wmlc', |
---|
1286 | 'wmlsc' => 'application/vnd.wap.wmlscriptc', |
---|
1287 | 'wk' => 'application/x-123', |
---|
1288 | '7z' => 'application/x-7z-compressed', |
---|
1289 | 'abw' => 'application/x-abiword', |
---|
1290 | 'dmg' => 'application/x-apple-diskimage', |
---|
1291 | 'bcpio' => 'application/x-bcpio', |
---|
1292 | 'torrent' => 'application/x-bittorrent', |
---|
1293 | 'cab' => 'application/x-cab', |
---|
1294 | 'cbr' => 'application/x-cbr', |
---|
1295 | 'cbz' => 'application/x-cbz', |
---|
1296 | 'cdf' => 'application/x-cdf', |
---|
1297 | 'vcd' => 'application/x-cdlink', |
---|
1298 | 'pgn' => 'application/x-chess-pgn', |
---|
1299 | 'cpio' => 'application/x-cpio', |
---|
1300 | 'csh' => 'text/x-csh', |
---|
1301 | 'deb|udeb' => 'application/x-debian-package', |
---|
1302 | 'dcr|dir|dxr' => 'application/x-director', |
---|
1303 | 'dms' => 'application/x-dms', |
---|
1304 | 'wad' => 'application/x-doom', |
---|
1305 | 'dvi' => 'application/x-dvi', |
---|
1306 | 'rhtml' => 'application/x-httpd-eruby', |
---|
1307 | 'flac' => 'application/x-flac', |
---|
1308 | 'pfa|pfb|gsf|pcf|pcf.Z' => 'application/x-font', |
---|
1309 | 'mm' => 'application/x-freemind', |
---|
1310 | 'gnumeric' => 'application/x-gnumeric', |
---|
1311 | 'sgf' => 'application/x-go-sgf', |
---|
1312 | 'gcf' => 'application/x-graphing-calculator', |
---|
1313 | 'gtar|tgz|taz' => 'application/x-gtar', |
---|
1314 | 'hdf' => 'application/x-hdf', |
---|
1315 | 'phtml|pht|php' => 'application/x-httpd-php', |
---|
1316 | 'phps' => 'application/x-httpd-php-source', |
---|
1317 | 'php3' => 'application/x-httpd-php3', |
---|
1318 | 'php3p' => 'application/x-httpd-php3-preprocessed', |
---|
1319 | 'php4' => 'application/x-httpd-php4', |
---|
1320 | 'ica' => 'application/x-ica', |
---|
1321 | 'ins|isp' => 'application/x-internet-signup', |
---|
1322 | 'iii' => 'application/x-iphone', |
---|
1323 | 'iso' => 'application/x-iso9660-image', |
---|
1324 | 'jnlp' => 'application/x-java-jnlp-file', |
---|
1325 | 'js' => 'application/x-javascript', |
---|
1326 | 'jmz' => 'application/x-jmol', |
---|
1327 | 'chrt' => 'application/x-kchart', |
---|
1328 | 'kil' => 'application/x-killustrator', |
---|
1329 | 'skp|skd|skt|skm' => 'application/x-koan', |
---|
1330 | 'kpr|kpt' => 'application/x-kpresenter', |
---|
1331 | 'ksp' => 'application/x-kspread', |
---|
1332 | 'kwd|kwt' => 'application/x-kword', |
---|
1333 | 'latex' => 'application/x-latex', |
---|
1334 | 'lha' => 'application/x-lha', |
---|
1335 | 'lyx' => 'application/x-lyx', |
---|
1336 | 'lzh' => 'application/x-lzh', |
---|
1337 | 'lzx' => 'application/x-lzx', |
---|
1338 | 'frm|maker|frame|fm|fb|book|fbdoc' => 'application/x-maker', |
---|
1339 | 'mif' => 'application/x-mif', |
---|
1340 | 'wmd' => 'application/x-ms-wmd', |
---|
1341 | 'wmz' => 'application/x-ms-wmz', |
---|
1342 | 'com|exe|bat|dll' => 'application/x-msdos-program', |
---|
1343 | 'msi' => 'application/x-msi', |
---|
1344 | 'nc' => 'application/x-netcdf', |
---|
1345 | 'pac' => 'application/x-ns-proxy-autoconfig', |
---|
1346 | 'nwc' => 'application/x-nwc', |
---|
1347 | 'o' => 'application/x-object', |
---|
1348 | 'oza' => 'application/x-oz-application', |
---|
1349 | 'p7r' => 'application/x-pkcs7-certreqresp', |
---|
1350 | 'crl' => 'application/x-pkcs7-crl', |
---|
1351 | 'pyc|pyo' => 'application/x-python-code', |
---|
1352 | 'qtl' => 'application/x-quicktimeplayer', |
---|
1353 | 'rpm' => 'application/x-redhat-package-manager', |
---|
1354 | 'sh' => 'text/x-sh', |
---|
1355 | 'shar' => 'application/x-shar', |
---|
1356 | 'swf|swfl' => 'application/x-shockwave-flash', |
---|
1357 | 'sit|sitx' => 'application/x-stuffit', |
---|
1358 | 'sv4cpio' => 'application/x-sv4cpio', |
---|
1359 | 'sv4crc' => 'application/x-sv4crc', |
---|
1360 | 'tar' => 'application/x-tar', |
---|
1361 | 'tcl' => 'application/x-tcl', |
---|
1362 | 'gf' => 'application/x-tex-gf', |
---|
1363 | 'pk' => 'application/x-tex-pk', |
---|
1364 | 'texinfo|texi' => 'application/x-texinfo', |
---|
1365 | '~|%|bak|old|sik' => 'application/x-trash', |
---|
1366 | 't|tr|roff' => 'application/x-troff', |
---|
1367 | 'man' => 'application/x-troff-man', |
---|
1368 | 'me' => 'application/x-troff-me', |
---|
1369 | 'ms' => 'application/x-troff-ms', |
---|
1370 | 'ustar' => 'application/x-ustar', |
---|
1371 | 'src' => 'application/x-wais-source', |
---|
1372 | 'wz' => 'application/x-wingz', |
---|
1373 | 'crt' => 'application/x-x509-ca-cert', |
---|
1374 | 'xcf' => 'application/x-xcf', |
---|
1375 | 'fig' => 'application/x-xfig', |
---|
1376 | 'xpi' => 'application/x-xpinstall', |
---|
1377 | 'au|snd' => 'audio/basic', |
---|
1378 | 'mid|midi|kar' => 'audio/midi', |
---|
1379 | 'mpga|mpega|mp2|mp3|m4a' => 'audio/mpeg', |
---|
1380 | 'f4a|f4b' => 'audio/mp4', |
---|
1381 | 'm3u' => 'audio/x-mpegurl', |
---|
1382 | 'oga|spx' => 'audio/ogg', |
---|
1383 | 'sid' => 'audio/prs.sid', |
---|
1384 | 'aif|aiff|aifc' => 'audio/x-aiff', |
---|
1385 | 'gsm' => 'audio/x-gsm', |
---|
1386 | 'wma' => 'audio/x-ms-wma', |
---|
1387 | 'wax' => 'audio/x-ms-wax', |
---|
1388 | 'ra|rm|ram' => 'audio/x-pn-realaudio', |
---|
1389 | 'ra' => 'audio/x-realaudio', |
---|
1390 | 'pls' => 'audio/x-scpls', |
---|
1391 | 'sd2' => 'audio/x-sd2', |
---|
1392 | 'wav' => 'audio/x-wav', |
---|
1393 | 'alc' => 'chemical/x-alchemy', |
---|
1394 | 'cac|cache' => 'chemical/x-cache', |
---|
1395 | 'csf' => 'chemical/x-cache-csf', |
---|
1396 | 'cbin|cascii|ctab' => 'chemical/x-cactvs-binary', |
---|
1397 | 'cdx' => 'chemical/x-cdx', |
---|
1398 | 'cer' => 'chemical/x-cerius', |
---|
1399 | 'c3d' => 'chemical/x-chem3d', |
---|
1400 | 'chm' => 'chemical/x-chemdraw', |
---|
1401 | 'cif' => 'chemical/x-cif', |
---|
1402 | 'cmdf' => 'chemical/x-cmdf', |
---|
1403 | 'cml' => 'chemical/x-cml', |
---|
1404 | 'cpa' => 'chemical/x-compass', |
---|
1405 | 'bsd' => 'chemical/x-crossfire', |
---|
1406 | 'csml|csm' => 'chemical/x-csml', |
---|
1407 | 'ctx' => 'chemical/x-ctx', |
---|
1408 | 'cxf|cef' => 'chemical/x-cxf', |
---|
1409 | 'emb|embl' => 'chemical/x-embl-dl-nucleotide', |
---|
1410 | 'spc' => 'chemical/x-galactic-spc', |
---|
1411 | 'inp|gam|gamin' => 'chemical/x-gamess-input', |
---|
1412 | 'fch|fchk' => 'chemical/x-gaussian-checkpoint', |
---|
1413 | 'cub' => 'chemical/x-gaussian-cube', |
---|
1414 | 'gau|gjc|gjf' => 'chemical/x-gaussian-input', |
---|
1415 | 'gal' => 'chemical/x-gaussian-log', |
---|
1416 | 'gcg' => 'chemical/x-gcg8-sequence', |
---|
1417 | 'gen' => 'chemical/x-genbank', |
---|
1418 | 'hin' => 'chemical/x-hin', |
---|
1419 | 'istr|ist' => 'chemical/x-isostar', |
---|
1420 | 'jdx|dx' => 'chemical/x-jcamp-dx', |
---|
1421 | 'kin' => 'chemical/x-kinemage', |
---|
1422 | 'mcm' => 'chemical/x-macmolecule', |
---|
1423 | 'mmd|mmod' => 'chemical/x-macromodel-input', |
---|
1424 | 'mol' => 'chemical/x-mdl-molfile', |
---|
1425 | 'rd' => 'chemical/x-mdl-rdfile', |
---|
1426 | 'rxn' => 'chemical/x-mdl-rxnfile', |
---|
1427 | 'sd|sdf' => 'chemical/x-mdl-sdfile', |
---|
1428 | 'tgf' => 'chemical/x-mdl-tgf', |
---|
1429 | 'mcif' => 'chemical/x-mmcif', |
---|
1430 | 'mol2' => 'chemical/x-mol2', |
---|
1431 | 'b' => 'chemical/x-molconn-Z', |
---|
1432 | 'gpt' => 'chemical/x-mopac-graph', |
---|
1433 | 'mop|mopcrt|mpc|dat|zmt' => 'chemical/x-mopac-input', |
---|
1434 | 'moo' => 'chemical/x-mopac-out', |
---|
1435 | 'mvb' => 'chemical/x-mopac-vib', |
---|
1436 | 'asn' => 'chemical/x-ncbi-asn1-spec', |
---|
1437 | 'prt|ent' => 'chemical/x-ncbi-asn1-ascii', |
---|
1438 | 'val|aso' => 'chemical/x-ncbi-asn1-binary', |
---|
1439 | 'pdb|ent' => 'chemical/x-pdb', |
---|
1440 | 'ros' => 'chemical/x-rosdal', |
---|
1441 | 'sw' => 'chemical/x-swissprot', |
---|
1442 | 'vms' => 'chemical/x-vamas-iso14976', |
---|
1443 | 'vmd' => 'chemical/x-vmd', |
---|
1444 | 'xtel' => 'chemical/x-xtel', |
---|
1445 | 'xyz' => 'chemical/x-xyz', |
---|
1446 | 'gif' => 'image/gif', |
---|
1447 | 'ief' => 'image/ief', |
---|
1448 | 'jpeg|jpg|jpe' => 'image/jpeg', |
---|
1449 | 'pcx' => 'image/pcx', |
---|
1450 | 'png' => 'image/png', |
---|
1451 | 'svg|svgz' => 'image/svg+xml', |
---|
1452 | 'tiff|tif' => 'image/tiff', |
---|
1453 | 'djvu|djv' => 'image/vnd.djvu', |
---|
1454 | 'wbmp' => 'image/vnd.wap.wbmp', |
---|
1455 | 'ras' => 'image/x-cmu-raster', |
---|
1456 | 'cdr' => 'image/x-coreldraw', |
---|
1457 | 'pat' => 'image/x-coreldrawpattern', |
---|
1458 | 'cdt' => 'image/x-coreldrawtemplate', |
---|
1459 | 'ico' => 'image/x-icon', |
---|
1460 | 'art' => 'image/x-jg', |
---|
1461 | 'jng' => 'image/x-jng', |
---|
1462 | 'bmp' => 'image/x-ms-bmp', |
---|
1463 | 'psd' => 'image/x-photoshop', |
---|
1464 | 'pnm' => 'image/x-portable-anymap', |
---|
1465 | 'pbm' => 'image/x-portable-bitmap', |
---|
1466 | 'pgm' => 'image/x-portable-graymap', |
---|
1467 | 'ppm' => 'image/x-portable-pixmap', |
---|
1468 | 'rgb' => 'image/x-rgb', |
---|
1469 | 'xbm' => 'image/x-xbitmap', |
---|
1470 | 'xpm' => 'image/x-xpixmap', |
---|
1471 | 'xwd' => 'image/x-xwindowdump', |
---|
1472 | 'eml' => 'message/rfc822', |
---|
1473 | 'igs|iges' => 'model/iges', |
---|
1474 | 'msh|mesh|silo' => 'model/mesh', |
---|
1475 | 'wrl|vrml' => 'model/vrml', |
---|
1476 | 'ics|icz' => 'text/calendar', |
---|
1477 | 'css' => 'text/css', |
---|
1478 | 'csv' => 'text/csv', |
---|
1479 | '323' => 'text/h323', |
---|
1480 | 'html|htm|shtml' => 'text/html', |
---|
1481 | 'uls' => 'text/iuls', |
---|
1482 | 'mml' => 'text/mathml', |
---|
1483 | 'asc|txt|text|pot' => 'text/plain', |
---|
1484 | 'rtx' => 'text/richtext', |
---|
1485 | 'sct|wsc' => 'text/scriptlet', |
---|
1486 | 'tm|ts' => 'text/texmacs', |
---|
1487 | 'tsv' => 'text/tab-separated-values', |
---|
1488 | 'jad' => 'text/vnd.sun.j2me.app-descriptor', |
---|
1489 | 'wml' => 'text/vnd.wap.wml', |
---|
1490 | 'wmls' => 'text/vnd.wap.wmlscript', |
---|
1491 | 'bib' => 'text/x-bibtex', |
---|
1492 | 'boo' => 'text/x-boo', |
---|
1493 | 'h++|hpp|hxx|hh' => 'text/x-c++hdr', |
---|
1494 | 'c++|cpp|cxx|cc' => 'text/x-c++src', |
---|
1495 | 'h' => 'text/x-chdr', |
---|
1496 | 'htc' => 'text/x-component', |
---|
1497 | 'c' => 'text/x-csrc', |
---|
1498 | 'd' => 'text/x-dsrc', |
---|
1499 | 'diff|patch' => 'text/x-diff', |
---|
1500 | 'hs' => 'text/x-haskell', |
---|
1501 | 'java' => 'text/x-java', |
---|
1502 | 'lhs' => 'text/x-literate-haskell', |
---|
1503 | 'moc' => 'text/x-moc', |
---|
1504 | 'p|pas' => 'text/x-pascal', |
---|
1505 | 'gcd' => 'text/x-pcs-gcd', |
---|
1506 | 'pl|pm' => 'text/x-perl', |
---|
1507 | 'py' => 'text/x-python', |
---|
1508 | 'etx' => 'text/x-setext', |
---|
1509 | 'tcl|tk' => 'text/x-tcl', |
---|
1510 | 'tex|ltx|sty|cls' => 'text/x-tex', |
---|
1511 | 'vcs' => 'text/x-vcalendar', |
---|
1512 | 'vcf' => 'text/x-vcard', |
---|
1513 | '3gp' => 'video/3gpp', |
---|
1514 | 'dl' => 'video/dl', |
---|
1515 | 'dif|dv' => 'video/dv', |
---|
1516 | 'fli' => 'video/fli', |
---|
1517 | 'gl' => 'video/gl', |
---|
1518 | 'mpeg|mpg|mpe' => 'video/mpeg', |
---|
1519 | 'mp4|f4v|f4p' => 'video/mp4', |
---|
1520 | 'flv' => 'video/x-flv', |
---|
1521 | 'ogv' => 'video/ogg', |
---|
1522 | 'qt|mov' => 'video/quicktime', |
---|
1523 | 'mxu' => 'video/vnd.mpegurl', |
---|
1524 | 'lsf|lsx' => 'video/x-la-asf', |
---|
1525 | 'mng' => 'video/x-mng', |
---|
1526 | 'asf|asx' => 'video/x-ms-asf', |
---|
1527 | 'wm' => 'video/x-ms-wm', |
---|
1528 | 'wmv' => 'video/x-ms-wmv', |
---|
1529 | 'wmx' => 'video/x-ms-wmx', |
---|
1530 | 'wvx' => 'video/x-ms-wvx', |
---|
1531 | 'avi' => 'video/x-msvideo', |
---|
1532 | 'movie' => 'video/x-sgi-movie', |
---|
1533 | 'ice' => 'x-conference/x-cooltalk', |
---|
1534 | 'sisx' => 'x-epoc/x-sisx-app', |
---|
1535 | 'vrm|vrml|wrl' => 'x-world/x-vrml', |
---|
1536 | 'xps' => 'application/vnd.ms-xpsdocument', |
---|
1537 | )); |
---|
1538 | } |
---|
1539 | foreach ($mapping as $ext_preg => $mime_match) { |
---|
1540 | if (preg_match('!\.('. $ext_preg .')$!i', $filename)) { |
---|
1541 | return $mime_match; |
---|
1542 | } |
---|
1543 | } |
---|
1544 | |
---|
1545 | return 'application/octet-stream'; |
---|
1546 | } |
---|
1547 | |
---|
1548 | /** |
---|
1549 | * @} End of "defgroup file". |
---|
1550 | */ |
---|