Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
/filter/tex/ -> lib.php (source)

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * TeX filter library functions.
  19   *
  20   * @package    filter
  21   * @subpackage tex
  22   * @copyright  2004 Zbigniew Fiedorowicz fiedorow@math.ohio-state.edu
  23   *             Originally based on code provided by Bruno Vernier bruno@vsbeducation.ca
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  function filter_tex_get_executable($debug=false) {
  30      global $CFG;
  31  
  32      if ((PHP_OS == "WINNT") || (PHP_OS == "WIN32") || (PHP_OS == "Windows")) {
  33          return "$CFG->dirroot/filter/tex/mimetex.exe";
  34      }
  35  
  36      if ($pathmimetex = get_config('filter_tex', 'pathmimetex')) {
  37          if (is_executable($pathmimetex)) {
  38              return $pathmimetex;
  39          } else {
  40              throw new \moodle_exception('mimetexnotexecutable', 'error');
  41          }
  42      }
  43  
  44      $custom_commandpath = "$CFG->dirroot/filter/tex/mimetex";
  45      if (file_exists($custom_commandpath)) {
  46          if (is_executable($custom_commandpath)) {
  47              return $custom_commandpath;
  48          } else {
  49              throw new \moodle_exception('mimetexnotexecutable', 'error');
  50          }
  51      }
  52  
  53      switch (PHP_OS) {
  54          case "Darwin":  return "$CFG->dirroot/filter/tex/mimetex.darwin";
  55          case "FreeBSD": return "$CFG->dirroot/filter/tex/mimetex.freebsd";
  56          case "Linux":
  57              if (php_uname('m') == 'aarch64') {
  58                  return "$CFG->dirroot/filter/tex/mimetex.linux.aarch64";
  59              }
  60  
  61              return "$CFG->dirroot/filter/tex/mimetex.linux";
  62      }
  63  
  64      throw new \moodle_exception('mimetexisnotexist', 'error');
  65  }
  66  
  67  /**
  68   * Check the formula expression against the list of denied keywords.
  69   *
  70   * List of allowed could be more complete but also harder to maintain.
  71   *
  72   * @param string $texexp Formula expression to check.
  73   * @return string Formula expression with denied keywords replaced with 'forbiddenkeyword'.
  74   */
  75  function filter_tex_sanitize_formula(string $texexp): string {
  76  
  77      $denylist = [
  78          'include', 'command', 'loop', 'repeat', 'open', 'toks', 'output',
  79          'input', 'catcode', 'name', '^^',
  80          '\def', '\edef', '\gdef', '\xdef',
  81          '\every', '\errhelp', '\errorstopmode', '\scrollmode', '\nonstopmode',
  82          '\batchmode', '\read', '\write', 'csname', '\newhelp', '\uppercase',
  83          '\lowercase', '\relax', '\aftergroup',
  84          '\afterassignment', '\expandafter', '\noexpand', '\special',
  85          '\let', '\futurelet', '\else', '\fi', '\chardef', '\makeatletter', '\afterground',
  86          '\noexpand', '\line', '\mathcode', '\item', '\section', '\mbox', '\declarerobustcommand',
  87          '\ExplSyntaxOn',
  88      ];
  89  
  90      $allowlist = ['inputenc'];
  91  
  92      // Prepare the denylist for regular expression.
  93      $denylist = array_map(function($value){
  94          return '/' . preg_quote($value, '/') . '/i';
  95      }, $denylist);
  96  
  97      // Prepare the allowlist for regular expression.
  98      $allowlist = array_map(function($value){
  99          return '/\bforbiddenkeyword_(' . preg_quote($value, '/') . ')\b/i';
 100      }, $allowlist);
 101  
 102      // First, mangle all denied words.
 103      $texexp = preg_replace_callback($denylist,
 104          function($matches) {
 105              return 'forbiddenkeyword_' . $matches[0];
 106          },
 107          $texexp
 108      );
 109  
 110      // Then, change back the allowed words.
 111      $texexp = preg_replace_callback($allowlist,
 112          function($matches) {
 113              return $matches[1];
 114          },
 115          $texexp
 116      );
 117  
 118      return $texexp;
 119  }
 120  
 121  function filter_tex_get_cmd($pathname, $texexp) {
 122      $texexp = filter_tex_sanitize_formula($texexp);
 123      $texexp = escapeshellarg($texexp);
 124      $executable = filter_tex_get_executable(false);
 125  
 126      if ((PHP_OS == "WINNT") || (PHP_OS == "WIN32") || (PHP_OS == "Windows")) {
 127          $executable = str_replace(' ', '^ ', $executable);
 128          return "$executable ++ -e  \"$pathname\" -- $texexp";
 129  
 130      } else {
 131          return "\"$executable\" -e \"$pathname\" -- $texexp";
 132      }
 133  }
 134  
 135  /**
 136   * Purge all caches when settings changed.
 137   */
 138  function filter_tex_updatedcallback($name) {
 139      global $CFG, $DB;
 140      reset_text_filters_cache();
 141  
 142      if (file_exists("$CFG->dataroot/filter/tex")) {
 143          remove_dir("$CFG->dataroot/filter/tex");
 144      }
 145      if (file_exists("$CFG->dataroot/filter/algebra")) {
 146          remove_dir("$CFG->dataroot/filter/algebra");
 147      }
 148      if (file_exists("$CFG->tempdir/latex")) {
 149          remove_dir("$CFG->tempdir/latex");
 150      }
 151  
 152      $DB->delete_records('cache_filters', array('filter'=>'tex'));
 153      $DB->delete_records('cache_filters', array('filter'=>'algebra'));
 154  
 155      $pathlatex = get_config('filter_tex', 'pathlatex');
 156      if ($pathlatex === false) {
 157          // detailed settings not present yet
 158          return;
 159      }
 160  
 161      $pathlatex = trim($pathlatex, " '\"");
 162      $pathdvips = trim(get_config('filter_tex', 'pathdvips'), " '\"");
 163      $pathconvert = trim(get_config('filter_tex', 'pathconvert'), " '\"");
 164      $pathdvisvgm = trim(get_config('filter_tex', 'pathdvisvgm'), " '\"");
 165  
 166      $supportedformats = array('gif');
 167      if ((is_file($pathlatex) && is_executable($pathlatex)) &&
 168              (is_file($pathdvips) && is_executable($pathdvips))) {
 169          if (is_file($pathconvert) && is_executable($pathconvert)) {
 170               $supportedformats[] = 'png';
 171          }
 172          if (is_file($pathdvisvgm) && is_executable($pathdvisvgm)) {
 173               $supportedformats[] = 'svg';
 174          }
 175      }
 176      if (!in_array(get_config('filter_tex', 'convertformat'), $supportedformats)) {
 177          set_config('convertformat', array_pop($supportedformats), 'filter_tex');
 178      }
 179  
 180  }
 181