Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * Minimalistic library, usable even when no other moodle libs are loaded.
  20   *
  21   * The only library that gets loaded if you define ABORT_AFTER_CONFIG
  22   * before including main config.php. You can resume normal script operation
  23   * if you define ABORT_AFTER_CONFIG_CANCEL and require the setup.php
  24   *
  25   * @package   core
  26   * @copyright 2009 Petr Skoda (skodak)
  27   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  28   */
  29  
  30  /**
  31   * Minimalistic parameter validation function.
  32   * Can not use optional param because moodlelib.php is not loaded yet
  33   * @param string $name
  34   * @param mixed $default
  35   * @param string $type
  36   * @return mixed
  37   */
  38  function min_optional_param($name, $default, $type) {
  39      if (isset($_GET[$name])) {
  40          $value = $_GET[$name];
  41  
  42      } else if (isset($_GET['amp;'.$name])) {
  43          // very, very, very ugly hack, unfortunately $OUTPUT->pix_url() is not used properly in javascript code :-(
  44          $value = $_GET['amp;'.$name];
  45  
  46      } else if (isset($_POST[$name])) {
  47          $value = $_POST[$name];
  48  
  49      } else {
  50          return $default;
  51      }
  52  
  53      return min_clean_param($value, $type);
  54  }
  55  
  56  /**
  57   * Minimalistic parameter cleaning function.
  58   *
  59   * Note: Can not use optional param because moodlelib.php is not loaded yet.
  60   *
  61   * @param string $value
  62   * @param string $type
  63   * @return mixed
  64   */
  65  function min_clean_param($value, $type) {
  66      switch($type) {
  67          case 'RAW':      $value = min_fix_utf8((string)$value);
  68                           break;
  69          case 'INT':      $value = (int)$value;
  70                           break;
  71          case 'SAFEDIR':  $value = preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
  72                           break;
  73          case 'SAFEPATH': $value = preg_replace('/[^a-zA-Z0-9\/\._-]/', '', $value);
  74                           $value = preg_replace('/\.+/', '.', $value);
  75                           $value = preg_replace('#/+#', '/', $value);
  76                           break;
  77          default:         die("Coding error: incorrect parameter type specified ($type).");
  78      }
  79  
  80      return $value;
  81  }
  82  
  83  /**
  84   * Minimalistic UTF-8 sanitisation.
  85   *
  86   * Note: This duplicates fix_utf8() intentionally for now.
  87   *
  88   * @param string $value
  89   * @return string
  90   */
  91  function min_fix_utf8($value) {
  92      // No null bytes expected in our data, so let's remove it.
  93      $value = str_replace("\0", '', $value);
  94  
  95      static $buggyiconv = null;
  96      if ($buggyiconv === null) {
  97          set_error_handler(function () {
  98              return true;
  99          });
 100          $buggyiconv = (!function_exists('iconv') or iconv('UTF-8', 'UTF-8//IGNORE', '100'.chr(130).'€') !== '100€');
 101          restore_error_handler();
 102      }
 103  
 104      if ($buggyiconv) {
 105          if (function_exists('mb_convert_encoding')) {
 106              $subst = mb_substitute_character();
 107              mb_substitute_character('');
 108              $result = mb_convert_encoding($value, 'utf-8', 'utf-8');
 109              mb_substitute_character($subst);
 110  
 111          } else {
 112              // Warn admins on admin/index.php page.
 113              $result = $value;
 114          }
 115  
 116      } else {
 117          $result = @iconv('UTF-8', 'UTF-8//IGNORE', $value);
 118      }
 119  
 120      return $result;
 121  }
 122  
 123  /**
 124   * This method tries to enable output compression if possible.
 125   * This function must be called before any output or headers.
 126   *
 127   * (IE6 is not supported at all.)
 128   *
 129   * @return boolean, true if compression enabled
 130   */
 131  function min_enable_zlib_compression() {
 132  
 133      if (headers_sent()) {
 134          return false;
 135      }
 136  
 137      // zlib.output_compression is preferred over ob_gzhandler()
 138      if (!empty($_SERVER['HTTP_USER_AGENT']) and strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 6') !== false) {
 139          ini_set('zlib.output_compression', 'Off');
 140          if (function_exists('apache_setenv')) {
 141              apache_setenv('no-gzip', 1);
 142          }
 143          return false;
 144      }
 145  
 146      ini_set('output_handler', '');
 147  
 148      /*
 149       * docs clearly say 'on' means enable and number means size of buffer,
 150       * but unfortunately some PHP version break when we set 'on' here.
 151       * 1 probably sets chunk size to 4096. our CSS and JS scripts are much bigger,
 152       * so let's try some bigger sizes.
 153       */
 154      ini_set('zlib.output_compression', 65536);
 155  
 156      return true;
 157  }
 158  
 159  /**
 160   * Returns the slashargument part of the URL.
 161   *
 162   * Note: ".php" is NOT allowed in slasharguments,
 163   *       it is intended for ASCII characters only.
 164   *
 165   * @param boolean $clean - Should we do cleaning on this path argument. If you set this
 166   *                         to false you MUST be very careful and do the cleaning manually.
 167   * @return string
 168   */
 169  function min_get_slash_argument($clean = true) {
 170      // Note: This code has to work in the same cases as normal get_file_argument(),
 171      //       but at the same time it may be simpler because we do not have to deal
 172      //       with encodings and other tricky stuff.
 173  
 174      $relativepath = '';
 175  
 176      if (!empty($_GET['file']) and strpos($_GET['file'], '/') === 0) {
 177          // Server is using url rewriting, most probably IIS.
 178          // Always clean the result of this function as it may be used in unsafe calls to send_file.
 179          $relativepath = $_GET['file'];
 180          if ($clean) {
 181              $relativepath = min_clean_param($relativepath, 'SAFEPATH');
 182          }
 183  
 184          return $relativepath;
 185  
 186      } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'iis') !== false) {
 187          if (isset($_SERVER['PATH_INFO']) and $_SERVER['PATH_INFO'] !== '') {
 188              $relativepath = urldecode($_SERVER['PATH_INFO']);
 189          }
 190  
 191      } else {
 192          if (isset($_SERVER['PATH_INFO'])) {
 193              $relativepath = $_SERVER['PATH_INFO'];
 194          }
 195      }
 196  
 197      $matches = null;
 198      if (preg_match('|^.+\.php(.*)$|i', $relativepath, $matches)) {
 199          $relativepath = $matches[1];
 200      }
 201  
 202      // Always clean the result of this function as it may be used in unsafe calls to send_file.
 203      if ($clean) {
 204          $relativepath = min_clean_param($relativepath, 'SAFEPATH');
 205      }
 206      return $relativepath;
 207  }
 208  
 209  /**
 210   * Get the lowest possible currently valid revision number.
 211   *
 212   * This is based on the current Moodle version.
 213   *
 214   * @return int Unix timestamp
 215   */
 216  function min_get_minimum_revision(): int {
 217      static $timestamp = null;
 218  
 219      if ($timestamp === null) {
 220          global $CFG;
 221          require("{$CFG->dirroot}/version.php");
 222          // Get YYYYMMDD from version.
 223          $datestring = floor($version / 100);
 224          // Parse the date components.
 225          $year = intval(substr($datestring, 0, 4));
 226          $month = intval(substr($datestring, 4, 2));
 227          $day = intval(substr($datestring, 6, 2));
 228          // Return converted GMT Unix timestamp.
 229          $timestamp = gmmktime(0, 0, 0, $month, $day, $year);
 230      }
 231  
 232      return $timestamp;
 233  }
 234  
 235  /**
 236   * Get the highest possible currently valid revision number.
 237   *
 238   * This is based on the current time, allowing for a small amount of clock skew between servers.
 239   *
 240   * Future values beyond the clock skew are not allowed to avoid the possibility of cache poisoning.
 241   *
 242   * @return int
 243   */
 244  function min_get_maximum_revision(): int {
 245      return time() + 60;
 246  }
 247  
 248  /**
 249   * Helper function to determine if the given revision number is valid.
 250   *
 251   * @param int $revision A numeric revision to check for validity
 252   * @return bool Whether the revision is valid
 253   */
 254  function min_is_revision_valid_and_current(int $revision): bool {
 255      // Invalid revision.
 256      if ($revision <= 0) {
 257          return false;
 258      }
 259      // Check revision is within range.
 260      return $revision >= min_get_minimum_revision() && $revision <= min_get_maximum_revision();
 261  }