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