Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   1  <?php
   2  /**
   3   * Copyright 1999-2017 Horde LLC (http://www.horde.org/)
   4   *
   5   * See the enclosed file LICENSE for license information (LGPL). If you
   6   * did not receive this file, see http://www.horde.org/licenses/lgpl21.
   7   *
   8   * @category  Horde
   9   * @copyright 1999-2017 Horde LLC
  10   * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
  11   * @package   Mime
  12   */
  13  
  14  /**
  15   * Utilities to determine MIME content-types of unknown content.
  16   *
  17   * @author    Anil Madhavapeddy <anil@recoil.org>
  18   * @author    Michael Slusarz <slusarz@horde.org>
  19   * @category  Horde
  20   * @copyright 1999-2017 Horde LLC
  21   * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
  22   * @package   Mime
  23   */
  24  class Horde_Mime_Magic
  25  {
  26      /**
  27       * The MIME extension map.
  28       *
  29       * @var array
  30       */
  31      protected static $_map = null;
  32  
  33      /**
  34       * Returns a copy of the MIME extension map.
  35       *
  36       * @return array  The MIME extension map.
  37       */
  38      protected static function _getMimeExtensionMap()
  39      {
  40          if (is_null(self::$_map)) {
  41              require  __DIR__ . '/mime.mapping.php';
  42              self::$_map = $mime_extension_map;
  43          }
  44  
  45          return self::$_map;
  46      }
  47  
  48      /**
  49       * Attempt to convert a file extension to a MIME type, based
  50       * on the global Horde and application specific config files.
  51       *
  52       * If we cannot map the file extension to a specific type, then
  53       * we fall back to a custom MIME handler 'x-extension/$ext', which
  54       * can be used as a normal MIME type internally throughout Horde.
  55       *
  56       * @param string $ext  The file extension to be mapped to a MIME type.
  57       *
  58       * @return string  The MIME type of the file extension.
  59       */
  60      public static function extToMime($ext)
  61      {
  62          if (empty($ext)) {
  63              return 'application/octet-stream';
  64          }
  65  
  66          $ext = Horde_String::lower($ext);
  67          $map = self::_getMimeExtensionMap();
  68          $pos = 0;
  69  
  70          while (!isset($map[$ext])) {
  71              if (($pos = strpos($ext, '.')) === false) {
  72                  break;
  73              }
  74              $ext = substr($ext, $pos + 1);
  75          }
  76  
  77          return isset($map[$ext])
  78              ? $map[$ext]
  79              : 'x-extension/' . $ext;
  80      }
  81  
  82      /**
  83       * Attempt to convert a filename to a MIME type, based on the global Horde
  84       * and application specific config files.
  85       *
  86       * @param string $filename  The filename to be mapped to a MIME type.
  87       * @param boolean $unknown  How should unknown extensions be handled? If
  88       *                          true, will return 'x-extension/*' types.  If
  89       *                          false, will return 'application/octet-stream'.
  90       *
  91       * @return string  The MIME type of the filename.
  92       */
  93      public static function filenameToMime($filename, $unknown = true)
  94      {
  95          $pos = strlen($filename) + 1;
  96          $type = '';
  97  
  98          $map = self::_getMimeExtensionMap();
  99          for ($i = 0; $i <= $map['__MAXPERIOD__']; ++$i) {
 100              $npos = strrpos(substr($filename, 0, $pos - 1), '.');
 101              if ($npos === false) {
 102                  break;
 103              }
 104              $pos = $npos + 1;
 105          }
 106  
 107          $type = ($pos === false) ? '' : self::extToMime(substr($filename, $pos));
 108  
 109          return (empty($type) || (!$unknown && (strpos($type, 'x-extension') !== false)))
 110              ? 'application/octet-stream'
 111              : $type;
 112      }
 113  
 114      /**
 115       * Attempt to convert a MIME type to a file extension, based
 116       * on the global Horde and application specific config files.
 117       *
 118       * If we cannot map the type to a file extension, we return false.
 119       *
 120       * @param string $type  The MIME type to be mapped to a file extension.
 121       *
 122       * @return string  The file extension of the MIME type.
 123       */
 124      public static function mimeToExt($type)
 125      {
 126          if (empty($type)) {
 127              return false;
 128          }
 129  
 130          if (($key = array_search($type, self::_getMimeExtensionMap())) === false) {
 131              list($major, $minor) = explode('/', $type);
 132              if ($major == 'x-extension') {
 133                  return $minor;
 134              }
 135              if (strpos($minor, 'x-') === 0) {
 136                  return substr($minor, 2);
 137              }
 138              return false;
 139          }
 140  
 141          return $key;
 142      }
 143  
 144      /**
 145       * Attempt to determine the MIME type of an unknown file.
 146       *
 147       * @param string $path      The path to the file to analyze.
 148       * @param string $magic_db  Path to the mime magic database.
 149       * @param array $opts       Additional options:
 150       *   - nostrip: (boolean) Don't strip parameter information from MIME
 151       *              type string.
 152       *              DEFAULT: false
 153       *
 154       * @return mixed  The MIME type of the file. Returns false if the file
 155       *                type can not be determined.
 156       */
 157      public static function analyzeFile($path, $magic_db = null,
 158                                         $opts = array())
 159      {
 160          if (Horde_Util::extensionExists('fileinfo')) {
 161              $res = empty($magic_db)
 162                  ? finfo_open(FILEINFO_MIME)
 163                  : finfo_open(FILEINFO_MIME, $magic_db);
 164  
 165              if ($res) {
 166                  $type = trim(finfo_file($res, $path));
 167                  finfo_close($res);
 168  
 169                  /* Remove any additional information. */
 170                  if (empty($opts['nostrip'])) {
 171                      foreach (array(';', ',', '\\0') as $separator) {
 172                          if (($pos = strpos($type, $separator)) !== false) {
 173                              $type = rtrim(substr($type, 0, $pos));
 174                          }
 175                      }
 176  
 177                      if (preg_match('|^[a-z0-9]+/[.-a-z0-9]+$|i', $type)) {
 178                          return $type;
 179                      }
 180                  } else {
 181                      return $type;
 182                  }
 183              }
 184          }
 185  
 186          return false;
 187      }
 188  
 189      /**
 190       * Attempt to determine the MIME type of an unknown byte stream.
 191       *
 192       * @param string $data      The file data to analyze.
 193       * @param string $magic_db  Path to the mime magic database.
 194       * @param array $opts       Additional options:
 195       *   - nostrip: (boolean) Don't strip parameter information from MIME
 196       *              type string.
 197       *              DEFAULT: false
 198       *
 199       * @return mixed  The MIME type of the file. Returns false if the file
 200       *                type can not be determined.
 201       */
 202      public static function analyzeData($data, $magic_db = null,
 203                                         $opts = array())
 204      {
 205          /* If the PHP Mimetype extension is available, use that. */
 206          if (Horde_Util::extensionExists('fileinfo')) {
 207              $res = empty($magic_db)
 208                  ? @finfo_open(FILEINFO_MIME)
 209                  : @finfo_open(FILEINFO_MIME, $magic_db);
 210  
 211              if (!$res) {
 212                  return false;
 213              }
 214  
 215              $type = trim(finfo_buffer($res, $data));
 216              finfo_close($res);
 217  
 218              /* Remove any additional information. */
 219              if (empty($opts['nostrip'])) {
 220                  if (($pos = strpos($type, ';')) !== false) {
 221                      $type = rtrim(substr($type, 0, $pos));
 222                  }
 223  
 224                  if (($pos = strpos($type, ',')) !== false) {
 225                      $type = rtrim(substr($type, 0, $pos));
 226                  }
 227              }
 228  
 229              return $type;
 230          }
 231  
 232          return false;
 233      }
 234  
 235  }