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.
/filter/tex/ -> lib.php (source)

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  // 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              print_error('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              print_error('mimetexnotexecutable', 'error');
  50          }
  51      }
  52  
  53      switch (PHP_OS) {
  54          case "Linux":   return "$CFG->dirroot/filter/tex/mimetex.linux";
  55          case "Darwin":  return "$CFG->dirroot/filter/tex/mimetex.darwin";
  56          case "FreeBSD": return "$CFG->dirroot/filter/tex/mimetex.freebsd";
  57      }
  58  
  59      print_error('mimetexisnotexist', 'error');
  60  }
  61  
  62  function filter_tex_sanitize_formula($texexp) {
  63      /// Check $texexp against blacklist (whitelisting could be more complete but also harder to maintain)
  64      $denylist = [
  65          'include','command','loop','repeat','open','toks','output',
  66          'input','catcode','name','^^',
  67          '\def','\edef','\gdef','\xdef',
  68          '\every','\errhelp','\errorstopmode','\scrollmode','\nonstopmode',
  69          '\batchmode','\read','\write','csname','\newhelp','\uppercase',
  70          '\lowercase','\relax','\aftergroup',
  71          '\afterassignment','\expandafter','\noexpand','\special',
  72          '\let', '\futurelet','\else','\fi','\chardef','\makeatletter','\afterground',
  73          '\noexpand','\line','\mathcode','\item','\section','\mbox','\declarerobustcommand',
  74          '\ExplSyntaxOn',
  75      ];
  76  
  77      $allowlist = ['inputenc'];
  78  
  79      // Prepare the denylist for regular expression.
  80      $denylist = array_map(function($value){
  81          return '/' . preg_quote($value, '/') . '/i';
  82      }, $denylist);
  83  
  84      // Prepare the allowlist for regular expression.
  85      $allowlist = array_map(function($value){
  86          return '/\bforbiddenkeyword_(' . preg_quote($value, '/') . ')\b/i';
  87      }, $allowlist);
  88  
  89      // First, mangle all denied words.
  90      $texexp = preg_replace_callback($denylist,
  91          function($matches) {
  92              return 'forbiddenkeyword_' . $matches[0];
  93          },
  94          $texexp
  95      );
  96  
  97      // Then, change back the allowed words.
  98      $texexp = preg_replace_callback($allowlist,
  99          function($matches) {
 100              return $matches[1];
 101          },
 102          $texexp
 103      );
 104  
 105      return $texexp;
 106  }
 107  
 108  function filter_tex_get_cmd($pathname, $texexp) {
 109      $texexp = filter_tex_sanitize_formula($texexp);
 110      $texexp = escapeshellarg($texexp);
 111      $executable = filter_tex_get_executable(false);
 112  
 113      if ((PHP_OS == "WINNT") || (PHP_OS == "WIN32") || (PHP_OS == "Windows")) {
 114          $executable = str_replace(' ', '^ ', $executable);
 115          return "$executable ++ -e  \"$pathname\" -- $texexp";
 116  
 117      } else {
 118          return "\"$executable\" -e \"$pathname\" -- $texexp";
 119      }
 120  }
 121  
 122  /**
 123   * Purge all caches when settings changed.
 124   */
 125  function filter_tex_updatedcallback($name) {
 126      global $CFG, $DB;
 127      reset_text_filters_cache();
 128  
 129      if (file_exists("$CFG->dataroot/filter/tex")) {
 130          remove_dir("$CFG->dataroot/filter/tex");
 131      }
 132      if (file_exists("$CFG->dataroot/filter/algebra")) {
 133          remove_dir("$CFG->dataroot/filter/algebra");
 134      }
 135      if (file_exists("$CFG->tempdir/latex")) {
 136          remove_dir("$CFG->tempdir/latex");
 137      }
 138  
 139      $DB->delete_records('cache_filters', array('filter'=>'tex'));
 140      $DB->delete_records('cache_filters', array('filter'=>'algebra'));
 141  
 142      $pathlatex = get_config('filter_tex', 'pathlatex');
 143      if ($pathlatex === false) {
 144          // detailed settings not present yet
 145          return;
 146      }
 147  
 148      $pathlatex = trim($pathlatex, " '\"");
 149      $pathdvips = trim(get_config('filter_tex', 'pathdvips'), " '\"");
 150      $pathconvert = trim(get_config('filter_tex', 'pathconvert'), " '\"");
 151      $pathdvisvgm = trim(get_config('filter_tex', 'pathdvisvgm'), " '\"");
 152  
 153      $supportedformats = array('gif');
 154      if ((is_file($pathlatex) && is_executable($pathlatex)) &&
 155              (is_file($pathdvips) && is_executable($pathdvips))) {
 156          if (is_file($pathconvert) && is_executable($pathconvert)) {
 157               $supportedformats[] = 'png';
 158          }
 159          if (is_file($pathdvisvgm) && is_executable($pathdvisvgm)) {
 160               $supportedformats[] = 'svg';
 161          }
 162      }
 163      if (!in_array(get_config('filter_tex', 'convertformat'), $supportedformats)) {
 164          set_config('convertformat', array_pop($supportedformats), 'filter_tex');
 165      }
 166  
 167  }
 168