PageRenderTime 72ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/setuplib.php

https://github.com/dongsheng/moodle
PHP | 2179 lines | 1147 code | 232 blank | 800 comment | 302 complexity | 281684e2bfe2ada63945e309135bde81 MD5 | raw file
Possible License(s): BSD-3-Clause, MIT, GPL-3.0, Apache-2.0, LGPL-2.1
  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. * These functions are required very early in the Moodle
  18. * setup process, before any of the main libraries are
  19. * loaded.
  20. *
  21. * @package core
  22. * @subpackage lib
  23. * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
  24. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25. */
  26. defined('MOODLE_INTERNAL') || die();
  27. // Debug levels - always keep the values in ascending order!
  28. /** No warnings and errors at all */
  29. define('DEBUG_NONE', 0);
  30. /** Fatal errors only */
  31. define('DEBUG_MINIMAL', E_ERROR | E_PARSE);
  32. /** Errors, warnings and notices */
  33. define('DEBUG_NORMAL', E_ERROR | E_PARSE | E_WARNING | E_NOTICE);
  34. /** All problems except strict PHP warnings */
  35. define('DEBUG_ALL', E_ALL & ~E_STRICT);
  36. /** DEBUG_ALL with all debug messages and strict warnings */
  37. define('DEBUG_DEVELOPER', E_ALL | E_STRICT);
  38. /** Remove any memory limits */
  39. define('MEMORY_UNLIMITED', -1);
  40. /** Standard memory limit for given platform */
  41. define('MEMORY_STANDARD', -2);
  42. /**
  43. * Large memory limit for given platform - used in cron, upgrade, and other places that need a lot of memory.
  44. * Can be overridden with $CFG->extramemorylimit setting.
  45. */
  46. define('MEMORY_EXTRA', -3);
  47. /** Extremely large memory limit - not recommended for standard scripts */
  48. define('MEMORY_HUGE', -4);
  49. /**
  50. * Base Moodle Exception class
  51. *
  52. * Although this class is defined here, you cannot throw a moodle_exception until
  53. * after moodlelib.php has been included (which will happen very soon).
  54. *
  55. * @package core
  56. * @subpackage lib
  57. * @copyright 2008 Petr Skoda {@link http://skodak.org}
  58. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  59. */
  60. class moodle_exception extends Exception {
  61. /**
  62. * @var string The name of the string from error.php to print
  63. */
  64. public $errorcode;
  65. /**
  66. * @var string The name of module
  67. */
  68. public $module;
  69. /**
  70. * @var mixed Extra words and phrases that might be required in the error string
  71. */
  72. public $a;
  73. /**
  74. * @var string The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
  75. */
  76. public $link;
  77. /**
  78. * @var string Optional information to aid the debugging process
  79. */
  80. public $debuginfo;
  81. /**
  82. * Constructor
  83. * @param string $errorcode The name of the string from error.php to print
  84. * @param string $module name of module
  85. * @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
  86. * @param mixed $a Extra words and phrases that might be required in the error string
  87. * @param string $debuginfo optional debugging information
  88. */
  89. function __construct($errorcode, $module='', $link='', $a=NULL, $debuginfo=null) {
  90. global $CFG;
  91. if (empty($module) || $module == 'moodle' || $module == 'core') {
  92. $module = 'error';
  93. }
  94. $this->errorcode = $errorcode;
  95. $this->module = $module;
  96. $this->link = $link;
  97. $this->a = $a;
  98. $this->debuginfo = is_null($debuginfo) ? null : (string)$debuginfo;
  99. if (get_string_manager()->string_exists($errorcode, $module)) {
  100. $message = get_string($errorcode, $module, $a);
  101. $haserrorstring = true;
  102. } else {
  103. $message = $module . '/' . $errorcode;
  104. $haserrorstring = false;
  105. }
  106. $isinphpunittest = (defined('PHPUNIT_TEST') && PHPUNIT_TEST);
  107. $hasdebugdeveloper = (
  108. isset($CFG->debugdisplay) &&
  109. isset($CFG->debug) &&
  110. $CFG->debugdisplay &&
  111. $CFG->debug === DEBUG_DEVELOPER
  112. );
  113. if ($debuginfo) {
  114. if ($isinphpunittest || $hasdebugdeveloper) {
  115. $message = "$message ($debuginfo)";
  116. }
  117. }
  118. if (!$haserrorstring and $isinphpunittest) {
  119. // Append the contents of $a to $debuginfo so helpful information isn't lost.
  120. // This emulates what {@link get_exception_info()} does. Unfortunately that
  121. // function is not used by phpunit.
  122. $message .= PHP_EOL.'$a contents: '.print_r($a, true);
  123. }
  124. parent::__construct($message, 0);
  125. }
  126. }
  127. /**
  128. * Course/activity access exception.
  129. *
  130. * This exception is thrown from require_login()
  131. *
  132. * @package core_access
  133. * @copyright 2010 Petr Skoda {@link http://skodak.org}
  134. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  135. */
  136. class require_login_exception extends moodle_exception {
  137. /**
  138. * Constructor
  139. * @param string $debuginfo Information to aid the debugging process
  140. */
  141. function __construct($debuginfo) {
  142. parent::__construct('requireloginerror', 'error', '', NULL, $debuginfo);
  143. }
  144. }
  145. /**
  146. * Session timeout exception.
  147. *
  148. * This exception is thrown from require_login()
  149. *
  150. * @package core_access
  151. * @copyright 2015 Andrew Nicols <andrew@nicols.co.uk>
  152. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  153. */
  154. class require_login_session_timeout_exception extends require_login_exception {
  155. /**
  156. * Constructor
  157. */
  158. public function __construct() {
  159. moodle_exception::__construct('sessionerroruser', 'error');
  160. }
  161. }
  162. /**
  163. * Web service parameter exception class
  164. * @deprecated since Moodle 2.2 - use moodle exception instead
  165. * This exception must be thrown to the web service client when a web service parameter is invalid
  166. * The error string is gotten from webservice.php
  167. */
  168. class webservice_parameter_exception extends moodle_exception {
  169. /**
  170. * Constructor
  171. * @param string $errorcode The name of the string from webservice.php to print
  172. * @param string $a The name of the parameter
  173. * @param string $debuginfo Optional information to aid debugging
  174. */
  175. function __construct($errorcode=null, $a = '', $debuginfo = null) {
  176. parent::__construct($errorcode, 'webservice', '', $a, $debuginfo);
  177. }
  178. }
  179. /**
  180. * Exceptions indicating user does not have permissions to do something
  181. * and the execution can not continue.
  182. *
  183. * @package core_access
  184. * @copyright 2009 Petr Skoda {@link http://skodak.org}
  185. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  186. */
  187. class required_capability_exception extends moodle_exception {
  188. /**
  189. * Constructor
  190. * @param context $context The context used for the capability check
  191. * @param string $capability The required capability
  192. * @param string $errormessage The error message to show the user
  193. * @param string $stringfile
  194. */
  195. function __construct($context, $capability, $errormessage, $stringfile) {
  196. $capabilityname = get_capability_string($capability);
  197. if ($context->contextlevel == CONTEXT_MODULE and preg_match('/:view$/', $capability)) {
  198. // we can not go to mod/xx/view.php because we most probably do not have cap to view it, let's go to course instead
  199. $parentcontext = $context->get_parent_context();
  200. $link = $parentcontext->get_url();
  201. } else {
  202. $link = $context->get_url();
  203. }
  204. parent::__construct($errormessage, $stringfile, $link, $capabilityname);
  205. }
  206. }
  207. /**
  208. * Exception indicating programming error, must be fixed by a programer. For example
  209. * a core API might throw this type of exception if a plugin calls it incorrectly.
  210. *
  211. * @package core
  212. * @subpackage lib
  213. * @copyright 2008 Petr Skoda {@link http://skodak.org}
  214. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  215. */
  216. class coding_exception extends moodle_exception {
  217. /**
  218. * Constructor
  219. * @param string $hint short description of problem
  220. * @param string $debuginfo detailed information how to fix problem
  221. */
  222. function __construct($hint, $debuginfo=null) {
  223. parent::__construct('codingerror', 'debug', '', $hint, $debuginfo);
  224. }
  225. }
  226. /**
  227. * Exception indicating malformed parameter problem.
  228. * This exception is not supposed to be thrown when processing
  229. * user submitted data in forms. It is more suitable
  230. * for WS and other low level stuff.
  231. *
  232. * @package core
  233. * @subpackage lib
  234. * @copyright 2009 Petr Skoda {@link http://skodak.org}
  235. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  236. */
  237. class invalid_parameter_exception extends moodle_exception {
  238. /**
  239. * Constructor
  240. * @param string $debuginfo some detailed information
  241. */
  242. function __construct($debuginfo=null) {
  243. parent::__construct('invalidparameter', 'debug', '', null, $debuginfo);
  244. }
  245. }
  246. /**
  247. * Exception indicating malformed response problem.
  248. * This exception is not supposed to be thrown when processing
  249. * user submitted data in forms. It is more suitable
  250. * for WS and other low level stuff.
  251. */
  252. class invalid_response_exception extends moodle_exception {
  253. /**
  254. * Constructor
  255. * @param string $debuginfo some detailed information
  256. */
  257. function __construct($debuginfo=null) {
  258. parent::__construct('invalidresponse', 'debug', '', null, $debuginfo);
  259. }
  260. }
  261. /**
  262. * An exception that indicates something really weird happened. For example,
  263. * if you do switch ($context->contextlevel), and have one case for each
  264. * CONTEXT_... constant. You might throw an invalid_state_exception in the
  265. * default case, to just in case something really weird is going on, and
  266. * $context->contextlevel is invalid - rather than ignoring this possibility.
  267. *
  268. * @package core
  269. * @subpackage lib
  270. * @copyright 2009 onwards Martin Dougiamas {@link http://moodle.com}
  271. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  272. */
  273. class invalid_state_exception extends moodle_exception {
  274. /**
  275. * Constructor
  276. * @param string $hint short description of problem
  277. * @param string $debuginfo optional more detailed information
  278. */
  279. function __construct($hint, $debuginfo=null) {
  280. parent::__construct('invalidstatedetected', 'debug', '', $hint, $debuginfo);
  281. }
  282. }
  283. /**
  284. * An exception that indicates incorrect permissions in $CFG->dataroot
  285. *
  286. * @package core
  287. * @subpackage lib
  288. * @copyright 2010 Petr Skoda {@link http://skodak.org}
  289. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  290. */
  291. class invalid_dataroot_permissions extends moodle_exception {
  292. /**
  293. * Constructor
  294. * @param string $debuginfo optional more detailed information
  295. */
  296. function __construct($debuginfo = NULL) {
  297. parent::__construct('invaliddatarootpermissions', 'error', '', NULL, $debuginfo);
  298. }
  299. }
  300. /**
  301. * An exception that indicates that file can not be served
  302. *
  303. * @package core
  304. * @subpackage lib
  305. * @copyright 2010 Petr Skoda {@link http://skodak.org}
  306. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  307. */
  308. class file_serving_exception extends moodle_exception {
  309. /**
  310. * Constructor
  311. * @param string $debuginfo optional more detailed information
  312. */
  313. function __construct($debuginfo = NULL) {
  314. parent::__construct('cannotservefile', 'error', '', NULL, $debuginfo);
  315. }
  316. }
  317. /**
  318. * Default exception handler.
  319. *
  320. * @param Exception $ex
  321. * @return void -does not return. Terminates execution!
  322. */
  323. function default_exception_handler($ex) {
  324. global $CFG, $DB, $OUTPUT, $USER, $FULLME, $SESSION, $PAGE;
  325. // detect active db transactions, rollback and log as error
  326. abort_all_db_transactions();
  327. if (($ex instanceof required_capability_exception) && !CLI_SCRIPT && !AJAX_SCRIPT && !empty($CFG->autologinguests) && !empty($USER->autologinguest)) {
  328. $SESSION->wantsurl = qualified_me();
  329. redirect(get_login_url());
  330. }
  331. $info = get_exception_info($ex);
  332. // If we already tried to send the header remove it, the content length
  333. // should be either empty or the length of the error page.
  334. @header_remove('Content-Length');
  335. if (is_early_init($info->backtrace)) {
  336. echo bootstrap_renderer::early_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo, $info->errorcode);
  337. } else {
  338. if (debugging('', DEBUG_MINIMAL)) {
  339. $logerrmsg = "Default exception handler: ".$info->message.' Debug: '.$info->debuginfo."\n".format_backtrace($info->backtrace, true);
  340. error_log($logerrmsg);
  341. }
  342. try {
  343. if ($DB) {
  344. // If you enable db debugging and exception is thrown, the print footer prints a lot of rubbish
  345. $DB->set_debug(0);
  346. }
  347. if (AJAX_SCRIPT) {
  348. // If we are in an AJAX script we don't want to use PREFERRED_RENDERER_TARGET.
  349. // Because we know we will want to use ajax format.
  350. $renderer = new core_renderer_ajax($PAGE, 'ajax');
  351. } else {
  352. $renderer = $OUTPUT;
  353. }
  354. echo $renderer->fatal_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo,
  355. $info->errorcode);
  356. } catch (Exception $e) {
  357. $out_ex = $e;
  358. } catch (Throwable $e) {
  359. // Engine errors in PHP7 throw exceptions of type Throwable (this "catch" will be ignored in PHP5).
  360. $out_ex = $e;
  361. }
  362. if (isset($out_ex)) {
  363. // default exception handler MUST not throw any exceptions!!
  364. // the problem here is we do not know if page already started or not, we only know that somebody messed up in outputlib or theme
  365. // so we just print at least something instead of "Exception thrown without a stack frame in Unknown on line 0":-(
  366. if (CLI_SCRIPT or AJAX_SCRIPT) {
  367. // just ignore the error and send something back using the safest method
  368. echo bootstrap_renderer::early_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo, $info->errorcode);
  369. } else {
  370. echo bootstrap_renderer::early_error_content($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo);
  371. $outinfo = get_exception_info($out_ex);
  372. echo bootstrap_renderer::early_error_content($outinfo->message, $outinfo->moreinfourl, $outinfo->link, $outinfo->backtrace, $outinfo->debuginfo);
  373. }
  374. }
  375. }
  376. exit(1); // General error code
  377. }
  378. /**
  379. * Default error handler, prevents some white screens.
  380. * @param int $errno
  381. * @param string $errstr
  382. * @param string $errfile
  383. * @param int $errline
  384. * @return bool false means use default error handler
  385. */
  386. function default_error_handler($errno, $errstr, $errfile, $errline) {
  387. if ($errno == 4096) {
  388. //fatal catchable error
  389. throw new coding_exception('PHP catchable fatal error', $errstr);
  390. }
  391. return false;
  392. }
  393. /**
  394. * Unconditionally abort all database transactions, this function
  395. * should be called from exception handlers only.
  396. * @return void
  397. */
  398. function abort_all_db_transactions() {
  399. global $CFG, $DB, $SCRIPT;
  400. // default exception handler MUST not throw any exceptions!!
  401. if ($DB && $DB->is_transaction_started()) {
  402. error_log('Database transaction aborted automatically in ' . $CFG->dirroot . $SCRIPT);
  403. // note: transaction blocks should never change current $_SESSION
  404. $DB->force_transaction_rollback();
  405. }
  406. }
  407. /**
  408. * This function encapsulates the tests for whether an exception was thrown in
  409. * early init -- either during setup.php or during init of $OUTPUT.
  410. *
  411. * If another exception is thrown then, and if we do not take special measures,
  412. * we would just get a very cryptic message "Exception thrown without a stack
  413. * frame in Unknown on line 0". That makes debugging very hard, so we do take
  414. * special measures in default_exception_handler, with the help of this function.
  415. *
  416. * @param array $backtrace the stack trace to analyse.
  417. * @return boolean whether the stack trace is somewhere in output initialisation.
  418. */
  419. function is_early_init($backtrace) {
  420. $dangerouscode = array(
  421. array('function' => 'header', 'type' => '->'),
  422. array('class' => 'bootstrap_renderer'),
  423. array('file' => __DIR__.'/setup.php'),
  424. );
  425. foreach ($backtrace as $stackframe) {
  426. foreach ($dangerouscode as $pattern) {
  427. $matches = true;
  428. foreach ($pattern as $property => $value) {
  429. if (!isset($stackframe[$property]) || $stackframe[$property] != $value) {
  430. $matches = false;
  431. }
  432. }
  433. if ($matches) {
  434. return true;
  435. }
  436. }
  437. }
  438. return false;
  439. }
  440. /**
  441. * Abort execution by throwing of a general exception,
  442. * default exception handler displays the error message in most cases.
  443. *
  444. * @param string $errorcode The name of the language string containing the error message.
  445. * Normally this should be in the error.php lang file.
  446. * @param string $module The language file to get the error message from.
  447. * @param string $link The url where the user will be prompted to continue.
  448. * If no url is provided the user will be directed to the site index page.
  449. * @param object $a Extra words and phrases that might be required in the error string
  450. * @param string $debuginfo optional debugging information
  451. * @return void, always throws exception!
  452. */
  453. function print_error($errorcode, $module = 'error', $link = '', $a = null, $debuginfo = null) {
  454. throw new moodle_exception($errorcode, $module, $link, $a, $debuginfo);
  455. }
  456. /**
  457. * Returns detailed information about specified exception.
  458. * @param exception $ex
  459. * @return object
  460. */
  461. function get_exception_info($ex) {
  462. global $CFG, $DB, $SESSION;
  463. if ($ex instanceof moodle_exception) {
  464. $errorcode = $ex->errorcode;
  465. $module = $ex->module;
  466. $a = $ex->a;
  467. $link = $ex->link;
  468. $debuginfo = $ex->debuginfo;
  469. } else {
  470. $errorcode = 'generalexceptionmessage';
  471. $module = 'error';
  472. $a = $ex->getMessage();
  473. $link = '';
  474. $debuginfo = '';
  475. }
  476. // Append the error code to the debug info to make grepping and googling easier
  477. $debuginfo .= PHP_EOL."Error code: $errorcode";
  478. $backtrace = $ex->getTrace();
  479. $place = array('file'=>$ex->getFile(), 'line'=>$ex->getLine(), 'exception'=>get_class($ex));
  480. array_unshift($backtrace, $place);
  481. // Be careful, no guarantee moodlelib.php is loaded.
  482. if (empty($module) || $module == 'moodle' || $module == 'core') {
  483. $module = 'error';
  484. }
  485. // Search for the $errorcode's associated string
  486. // If not found, append the contents of $a to $debuginfo so helpful information isn't lost
  487. if (function_exists('get_string_manager')) {
  488. if (get_string_manager()->string_exists($errorcode, $module)) {
  489. $message = get_string($errorcode, $module, $a);
  490. } elseif ($module == 'error' && get_string_manager()->string_exists($errorcode, 'moodle')) {
  491. // Search in moodle file if error specified - needed for backwards compatibility
  492. $message = get_string($errorcode, 'moodle', $a);
  493. } else {
  494. $message = $module . '/' . $errorcode;
  495. $debuginfo .= PHP_EOL.'$a contents: '.print_r($a, true);
  496. }
  497. } else {
  498. $message = $module . '/' . $errorcode;
  499. $debuginfo .= PHP_EOL.'$a contents: '.print_r($a, true);
  500. }
  501. // Remove some absolute paths from message and debugging info.
  502. $searches = array();
  503. $replaces = array();
  504. $cfgnames = array('backuptempdir', 'tempdir', 'cachedir', 'localcachedir', 'themedir', 'dataroot', 'dirroot');
  505. foreach ($cfgnames as $cfgname) {
  506. if (property_exists($CFG, $cfgname)) {
  507. $searches[] = $CFG->$cfgname;
  508. $replaces[] = "[$cfgname]";
  509. }
  510. }
  511. if (!empty($searches)) {
  512. $message = str_replace($searches, $replaces, $message);
  513. $debuginfo = str_replace($searches, $replaces, $debuginfo);
  514. }
  515. // Be careful, no guarantee weblib.php is loaded.
  516. if (function_exists('clean_text')) {
  517. $message = clean_text($message);
  518. } else {
  519. $message = htmlspecialchars($message);
  520. }
  521. if (!empty($CFG->errordocroot)) {
  522. $errordoclink = $CFG->errordocroot . '/en/';
  523. } else {
  524. // Only if the function is available. May be not for early errors.
  525. if (function_exists('current_language')) {
  526. $errordoclink = get_docs_url();
  527. } else {
  528. $errordoclink = 'https://docs.moodle.org/en/';
  529. }
  530. }
  531. if ($module === 'error') {
  532. $modulelink = 'moodle';
  533. } else {
  534. $modulelink = $module;
  535. }
  536. $moreinfourl = $errordoclink . 'error/' . $modulelink . '/' . $errorcode;
  537. if (empty($link)) {
  538. if (!empty($SESSION->fromurl)) {
  539. $link = $SESSION->fromurl;
  540. unset($SESSION->fromurl);
  541. } else {
  542. $link = $CFG->wwwroot .'/';
  543. }
  544. }
  545. // When printing an error the continue button should never link offsite.
  546. // We cannot use clean_param() here as it is not guaranteed that it has been loaded yet.
  547. if (stripos($link, $CFG->wwwroot) === 0) {
  548. // Internal HTTP, all good.
  549. } else {
  550. // External link spotted!
  551. $link = $CFG->wwwroot . '/';
  552. }
  553. $info = new stdClass();
  554. $info->message = $message;
  555. $info->errorcode = $errorcode;
  556. $info->backtrace = $backtrace;
  557. $info->link = $link;
  558. $info->moreinfourl = $moreinfourl;
  559. $info->a = $a;
  560. $info->debuginfo = $debuginfo;
  561. return $info;
  562. }
  563. /**
  564. * @deprecated since Moodle 3.8 MDL-61038 - please do not use this function any more.
  565. * @see \core\uuid::generate()
  566. */
  567. function generate_uuid() {
  568. throw new coding_exception('generate_uuid() cannot be used anymore. Please use ' .
  569. '\core\uuid::generate() instead.');
  570. }
  571. /**
  572. * Returns the Moodle Docs URL in the users language for a given 'More help' link.
  573. *
  574. * There are three cases:
  575. *
  576. * 1. In the normal case, $path will be a short relative path 'component/thing',
  577. * like 'mod/folder/view' 'group/import'. This gets turned into an link to
  578. * MoodleDocs in the user's language, and for the appropriate Moodle version.
  579. * E.g. 'group/import' may become 'http://docs.moodle.org/2x/en/group/import'.
  580. * The 'http://docs.moodle.org' bit comes from $CFG->docroot.
  581. *
  582. * This is the only option that should be used in standard Moodle code. The other
  583. * two options have been implemented because they are useful for third-party plugins.
  584. *
  585. * 2. $path may be an absolute URL, starting http:// or https://. In this case,
  586. * the link is used as is.
  587. *
  588. * 3. $path may start %%WWWROOT%%, in which case that is replaced by
  589. * $CFG->wwwroot to make the link.
  590. *
  591. * @param string $path the place to link to. See above for details.
  592. * @return string The MoodleDocs URL in the user's language. for example @link http://docs.moodle.org/2x/en/$path}
  593. */
  594. function get_docs_url($path = null) {
  595. global $CFG;
  596. // Absolute URLs are used unmodified.
  597. if (substr($path, 0, 7) === 'http://' || substr($path, 0, 8) === 'https://') {
  598. return $path;
  599. }
  600. // Paths starting %%WWWROOT%% have that replaced by $CFG->wwwroot.
  601. if (substr($path, 0, 11) === '%%WWWROOT%%') {
  602. return $CFG->wwwroot . substr($path, 11);
  603. }
  604. // Otherwise we do the normal case, and construct a MoodleDocs URL relative to $CFG->docroot.
  605. // Check that $CFG->branch has been set up, during installation it won't be.
  606. if (empty($CFG->branch)) {
  607. // It's not there yet so look at version.php.
  608. include($CFG->dirroot.'/version.php');
  609. } else {
  610. // We can use $CFG->branch and avoid having to include version.php.
  611. $branch = $CFG->branch;
  612. }
  613. // ensure branch is valid.
  614. if (!$branch) {
  615. // We should never get here but in case we do lets set $branch to .
  616. // the smart one's will know that this is the current directory
  617. // and the smarter ones will know that there is some smart matching
  618. // that will ensure people end up at the latest version of the docs.
  619. $branch = '.';
  620. }
  621. if (empty($CFG->doclang)) {
  622. $lang = current_language();
  623. } else {
  624. $lang = $CFG->doclang;
  625. }
  626. $end = '/' . $branch . '/' . $lang . '/' . $path;
  627. if (empty($CFG->docroot)) {
  628. return 'http://docs.moodle.org'. $end;
  629. } else {
  630. return $CFG->docroot . $end ;
  631. }
  632. }
  633. /**
  634. * Formats a backtrace ready for output.
  635. *
  636. * This function does not include function arguments because they could contain sensitive information
  637. * not suitable to be exposed in a response.
  638. *
  639. * @param array $callers backtrace array, as returned by debug_backtrace().
  640. * @param boolean $plaintext if false, generates HTML, if true generates plain text.
  641. * @return string formatted backtrace, ready for output.
  642. */
  643. function format_backtrace($callers, $plaintext = false) {
  644. // do not use $CFG->dirroot because it might not be available in destructors
  645. $dirroot = dirname(__DIR__);
  646. if (empty($callers)) {
  647. return '';
  648. }
  649. $from = $plaintext ? '' : '<ul style="text-align: left" data-rel="backtrace">';
  650. foreach ($callers as $caller) {
  651. if (!isset($caller['line'])) {
  652. $caller['line'] = '?'; // probably call_user_func()
  653. }
  654. if (!isset($caller['file'])) {
  655. $caller['file'] = 'unknownfile'; // probably call_user_func()
  656. }
  657. $from .= $plaintext ? '* ' : '<li>';
  658. $from .= 'line ' . $caller['line'] . ' of ' . str_replace($dirroot, '', $caller['file']);
  659. if (isset($caller['function'])) {
  660. $from .= ': call to ';
  661. if (isset($caller['class'])) {
  662. $from .= $caller['class'] . $caller['type'];
  663. }
  664. $from .= $caller['function'] . '()';
  665. } else if (isset($caller['exception'])) {
  666. $from .= ': '.$caller['exception'].' thrown';
  667. }
  668. $from .= $plaintext ? "\n" : '</li>';
  669. }
  670. $from .= $plaintext ? '' : '</ul>';
  671. return $from;
  672. }
  673. /**
  674. * This function makes the return value of ini_get consistent if you are
  675. * setting server directives through the .htaccess file in apache.
  676. *
  677. * Current behavior for value set from php.ini On = 1, Off = [blank]
  678. * Current behavior for value set from .htaccess On = On, Off = Off
  679. * Contributed by jdell @ unr.edu
  680. *
  681. * @param string $ini_get_arg The argument to get
  682. * @return bool True for on false for not
  683. */
  684. function ini_get_bool($ini_get_arg) {
  685. $temp = ini_get($ini_get_arg);
  686. if ($temp == '1' or strtolower($temp) == 'on') {
  687. return true;
  688. }
  689. return false;
  690. }
  691. /**
  692. * This function verifies the sanity of PHP configuration
  693. * and stops execution if anything critical found.
  694. */
  695. function setup_validate_php_configuration() {
  696. // this must be very fast - no slow checks here!!!
  697. if (ini_get_bool('session.auto_start')) {
  698. print_error('sessionautostartwarning', 'admin');
  699. }
  700. }
  701. /**
  702. * Initialise global $CFG variable.
  703. * @private to be used only from lib/setup.php
  704. */
  705. function initialise_cfg() {
  706. global $CFG, $DB;
  707. if (!$DB) {
  708. // This should not happen.
  709. return;
  710. }
  711. try {
  712. $localcfg = get_config('core');
  713. } catch (dml_exception $e) {
  714. // Most probably empty db, going to install soon.
  715. return;
  716. }
  717. foreach ($localcfg as $name => $value) {
  718. // Note that get_config() keeps forced settings
  719. // and normalises values to string if possible.
  720. $CFG->{$name} = $value;
  721. }
  722. }
  723. /**
  724. * Cache any immutable config locally to avoid constant DB lookups.
  725. *
  726. * Only to be used only from lib/setup.php
  727. */
  728. function initialise_local_config_cache() {
  729. global $CFG;
  730. $bootstrapcachefile = $CFG->localcachedir . '/bootstrap.php';
  731. if (!empty($CFG->siteidentifier) && !file_exists($bootstrapcachefile)) {
  732. $contents = "<?php
  733. // ********** This file is generated DO NOT EDIT **********
  734. \$CFG->siteidentifier = '" . addslashes($CFG->siteidentifier) . "';
  735. \$CFG->bootstraphash = '" . hash_local_config_cache() . "';
  736. // Only if the file is not stale and has not been defined.
  737. if (\$CFG->bootstraphash === hash_local_config_cache() && !defined('SYSCONTEXTID')) {
  738. define('SYSCONTEXTID', ".SYSCONTEXTID.");
  739. }
  740. ";
  741. $temp = $bootstrapcachefile . '.tmp' . uniqid();
  742. file_put_contents($temp, $contents);
  743. @chmod($temp, $CFG->filepermissions);
  744. rename($temp, $bootstrapcachefile);
  745. }
  746. }
  747. /**
  748. * Calculate a proper hash to be able to invalidate stale cached configs.
  749. *
  750. * Only to be used to verify bootstrap.php status.
  751. *
  752. * @return string md5 hash of all the sensible bits deciding if cached config is stale or no.
  753. */
  754. function hash_local_config_cache() {
  755. global $CFG;
  756. // This is pretty much {@see moodle_database::get_settings_hash()} that is used
  757. // as identifier for the database meta information MUC cache. Should be enough to
  758. // react against any of the normal changes (new prefix, change of DB type) while
  759. // *incorrectly* keeping the old dataroot directory unmodified with stale data.
  760. // This may need more stuff to be considered if it's discovered that there are
  761. // more variables making the file stale.
  762. return md5($CFG->dbtype . $CFG->dbhost . $CFG->dbuser . $CFG->dbname . $CFG->prefix);
  763. }
  764. /**
  765. * Initialises $FULLME and friends. Private function. Should only be called from
  766. * setup.php.
  767. */
  768. function initialise_fullme() {
  769. global $CFG, $FULLME, $ME, $SCRIPT, $FULLSCRIPT;
  770. // Detect common config error.
  771. if (substr($CFG->wwwroot, -1) == '/') {
  772. print_error('wwwrootslash', 'error');
  773. }
  774. if (CLI_SCRIPT) {
  775. initialise_fullme_cli();
  776. return;
  777. }
  778. if (!empty($CFG->overridetossl)) {
  779. if (strpos($CFG->wwwroot, 'http://') === 0) {
  780. $CFG->wwwroot = str_replace('http:', 'https:', $CFG->wwwroot);
  781. } else {
  782. unset_config('overridetossl');
  783. }
  784. }
  785. $rurl = setup_get_remote_url();
  786. $wwwroot = parse_url($CFG->wwwroot.'/');
  787. if (empty($rurl['host'])) {
  788. // missing host in request header, probably not a real browser, let's ignore them
  789. } else if (!empty($CFG->reverseproxy)) {
  790. // $CFG->reverseproxy specifies if reverse proxy server used
  791. // Used in load balancing scenarios.
  792. // Do not abuse this to try to solve lan/wan access problems!!!!!
  793. } else {
  794. if (($rurl['host'] !== $wwwroot['host']) or
  795. (!empty($wwwroot['port']) and $rurl['port'] != $wwwroot['port']) or
  796. (strpos($rurl['path'], $wwwroot['path']) !== 0)) {
  797. // Explain the problem and redirect them to the right URL
  798. if (!defined('NO_MOODLE_COOKIES')) {
  799. define('NO_MOODLE_COOKIES', true);
  800. }
  801. // The login/token.php script should call the correct url/port.
  802. if (defined('REQUIRE_CORRECT_ACCESS') && REQUIRE_CORRECT_ACCESS) {
  803. $wwwrootport = empty($wwwroot['port'])?'':$wwwroot['port'];
  804. $calledurl = $rurl['host'];
  805. if (!empty($rurl['port'])) {
  806. $calledurl .= ':'. $rurl['port'];
  807. }
  808. $correcturl = $wwwroot['host'];
  809. if (!empty($wwwrootport)) {
  810. $correcturl .= ':'. $wwwrootport;
  811. }
  812. throw new moodle_exception('requirecorrectaccess', 'error', '', null,
  813. 'You called ' . $calledurl .', you should have called ' . $correcturl);
  814. }
  815. redirect($CFG->wwwroot, get_string('wwwrootmismatch', 'error', $CFG->wwwroot), 3);
  816. }
  817. }
  818. // Check that URL is under $CFG->wwwroot.
  819. if (strpos($rurl['path'], $wwwroot['path']) === 0) {
  820. $SCRIPT = substr($rurl['path'], strlen($wwwroot['path'])-1);
  821. } else {
  822. // Probably some weird external script
  823. $SCRIPT = $FULLSCRIPT = $FULLME = $ME = null;
  824. return;
  825. }
  826. // $CFG->sslproxy specifies if external SSL appliance is used
  827. // (That is, the Moodle server uses http, with an external box translating everything to https).
  828. if (empty($CFG->sslproxy)) {
  829. if ($rurl['scheme'] === 'http' and $wwwroot['scheme'] === 'https') {
  830. if (defined('REQUIRE_CORRECT_ACCESS') && REQUIRE_CORRECT_ACCESS) {
  831. print_error('sslonlyaccess', 'error');
  832. } else {
  833. redirect($CFG->wwwroot, get_string('wwwrootmismatch', 'error', $CFG->wwwroot), 3);
  834. }
  835. }
  836. } else {
  837. if ($wwwroot['scheme'] !== 'https') {
  838. throw new coding_exception('Must use https address in wwwroot when ssl proxy enabled!');
  839. }
  840. $rurl['scheme'] = 'https'; // make moodle believe it runs on https, squid or something else it doing it
  841. $_SERVER['HTTPS'] = 'on'; // Override $_SERVER to help external libraries with their HTTPS detection.
  842. $_SERVER['SERVER_PORT'] = 443; // Assume default ssl port for the proxy.
  843. }
  844. // Hopefully this will stop all those "clever" admins trying to set up moodle
  845. // with two different addresses in intranet and Internet.
  846. // Port forwarding is still allowed!
  847. if (!empty($CFG->reverseproxy) && $rurl['host'] === $wwwroot['host'] && (empty($wwwroot['port']) || $rurl['port'] === $wwwroot['port'])) {
  848. print_error('reverseproxyabused', 'error');
  849. }
  850. $hostandport = $rurl['scheme'] . '://' . $wwwroot['host'];
  851. if (!empty($wwwroot['port'])) {
  852. $hostandport .= ':'.$wwwroot['port'];
  853. }
  854. $FULLSCRIPT = $hostandport . $rurl['path'];
  855. $FULLME = $hostandport . $rurl['fullpath'];
  856. $ME = $rurl['fullpath'];
  857. }
  858. /**
  859. * Initialises $FULLME and friends for command line scripts.
  860. * This is a private method for use by initialise_fullme.
  861. */
  862. function initialise_fullme_cli() {
  863. global $CFG, $FULLME, $ME, $SCRIPT, $FULLSCRIPT;
  864. // Urls do not make much sense in CLI scripts
  865. $backtrace = debug_backtrace();
  866. $topfile = array_pop($backtrace);
  867. $topfile = realpath($topfile['file']);
  868. $dirroot = realpath($CFG->dirroot);
  869. if (strpos($topfile, $dirroot) !== 0) {
  870. // Probably some weird external script
  871. $SCRIPT = $FULLSCRIPT = $FULLME = $ME = null;
  872. } else {
  873. $relativefile = substr($topfile, strlen($dirroot));
  874. $relativefile = str_replace('\\', '/', $relativefile); // Win fix
  875. $SCRIPT = $FULLSCRIPT = $relativefile;
  876. $FULLME = $ME = null;
  877. }
  878. }
  879. /**
  880. * Get the URL that PHP/the web server thinks it is serving. Private function
  881. * used by initialise_fullme. In your code, use $PAGE->url, $SCRIPT, etc.
  882. * @return array in the same format that parse_url returns, with the addition of
  883. * a 'fullpath' element, which includes any slasharguments path.
  884. */
  885. function setup_get_remote_url() {
  886. $rurl = array();
  887. if (isset($_SERVER['HTTP_HOST'])) {
  888. list($rurl['host']) = explode(':', $_SERVER['HTTP_HOST']);
  889. } else {
  890. $rurl['host'] = null;
  891. }
  892. $rurl['port'] = (int)$_SERVER['SERVER_PORT'];
  893. $rurl['path'] = $_SERVER['SCRIPT_NAME']; // Script path without slash arguments
  894. $rurl['scheme'] = (empty($_SERVER['HTTPS']) or $_SERVER['HTTPS'] === 'off' or $_SERVER['HTTPS'] === 'Off' or $_SERVER['HTTPS'] === 'OFF') ? 'http' : 'https';
  895. if (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false) {
  896. //Apache server
  897. $rurl['fullpath'] = $_SERVER['REQUEST_URI'];
  898. // Fixing a known issue with:
  899. // - Apache versions lesser than 2.4.11
  900. // - PHP deployed in Apache as PHP-FPM via mod_proxy_fcgi
  901. // - PHP versions lesser than 5.6.3 and 5.5.18.
  902. if (isset($_SERVER['PATH_INFO']) && (php_sapi_name() === 'fpm-fcgi') && isset($_SERVER['SCRIPT_NAME'])) {
  903. $pathinfodec = rawurldecode($_SERVER['PATH_INFO']);
  904. $lenneedle = strlen($pathinfodec);
  905. // Checks whether SCRIPT_NAME ends with PATH_INFO, URL-decoded.
  906. if (substr($_SERVER['SCRIPT_NAME'], -$lenneedle) === $pathinfodec) {
  907. // This is the "Apache 2.4.10- running PHP-FPM via mod_proxy_fcgi" fingerprint,
  908. // at least on CentOS 7 (Apache/2.4.6 PHP/5.4.16) and Ubuntu 14.04 (Apache/2.4.7 PHP/5.5.9)
  909. // => SCRIPT_NAME contains 'slash arguments' data too, which is wrongly exposed via PATH_INFO as URL-encoded.
  910. // Fix both $_SERVER['PATH_INFO'] and $_SERVER['SCRIPT_NAME'].
  911. $lenhaystack = strlen($_SERVER['SCRIPT_NAME']);
  912. $pos = $lenhaystack - $lenneedle;
  913. // Here $pos is greater than 0 but let's double check it.
  914. if ($pos > 0) {
  915. $_SERVER['PATH_INFO'] = $pathinfodec;
  916. $_SERVER['SCRIPT_NAME'] = substr($_SERVER['SCRIPT_NAME'], 0, $pos);
  917. }
  918. }
  919. }
  920. } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'iis') !== false) {
  921. //IIS - needs a lot of tweaking to make it work
  922. $rurl['fullpath'] = $_SERVER['SCRIPT_NAME'];
  923. // NOTE: we should ignore PATH_INFO because it is incorrectly encoded using 8bit filesystem legacy encoding in IIS.
  924. // Since 2.0, we rely on IIS rewrite extensions like Helicon ISAPI_rewrite
  925. // example rule: RewriteRule ^([^\?]+?\.php)(\/.+)$ $1\?file=$2 [QSA]
  926. // OR
  927. // we rely on a proper IIS 6.0+ configuration: the 'FastCGIUtf8ServerVariables' registry key.
  928. if (isset($_SERVER['PATH_INFO']) and $_SERVER['PATH_INFO'] !== '') {
  929. // Check that PATH_INFO works == must not contain the script name.
  930. if (strpos($_SERVER['PATH_INFO'], $_SERVER['SCRIPT_NAME']) === false) {
  931. $rurl['fullpath'] .= clean_param(urldecode($_SERVER['PATH_INFO']), PARAM_PATH);
  932. }
  933. }
  934. if (isset($_SERVER['QUERY_STRING']) and $_SERVER['QUERY_STRING'] !== '') {
  935. $rurl['fullpath'] .= '?'.$_SERVER['QUERY_STRING'];
  936. }
  937. $_SERVER['REQUEST_URI'] = $rurl['fullpath']; // extra IIS compatibility
  938. /* NOTE: following servers are not fully tested! */
  939. } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false) {
  940. //lighttpd - not officially supported
  941. $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
  942. } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) {
  943. //nginx - not officially supported
  944. if (!isset($_SERVER['SCRIPT_NAME'])) {
  945. die('Invalid server configuration detected, please try to add "fastcgi_param SCRIPT_NAME $fastcgi_script_name;" to the nginx server configuration.');
  946. }
  947. $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
  948. } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'cherokee') !== false) {
  949. //cherokee - not officially supported
  950. $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
  951. } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'zeus') !== false) {
  952. //zeus - not officially supported
  953. $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
  954. } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false) {
  955. //LiteSpeed - not officially supported
  956. $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
  957. } else if ($_SERVER['SERVER_SOFTWARE'] === 'HTTPD') {
  958. //obscure name found on some servers - this is definitely not supported
  959. $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
  960. } else if (strpos($_SERVER['SERVER_SOFTWARE'], 'PHP') === 0) {
  961. // built-in PHP Development Server
  962. $rurl['fullpath'] = $_SERVER['REQUEST_URI'];
  963. } else {
  964. throw new moodle_exception('unsupportedwebserver', 'error', '', $_SERVER['SERVER_SOFTWARE']);
  965. }
  966. // sanitize the url a bit more, the encoding style may be different in vars above
  967. $rurl['fullpath'] = str_replace('"', '%22', $rurl['fullpath']);
  968. $rurl['fullpath'] = str_replace('\'', '%27', $rurl['fullpath']);
  969. return $rurl;
  970. }
  971. /**
  972. * Try to work around the 'max_input_vars' restriction if necessary.
  973. */
  974. function workaround_max_input_vars() {
  975. // Make sure this gets executed only once from lib/setup.php!
  976. static $executed = false;
  977. if ($executed) {
  978. debugging('workaround_max_input_vars() must be called only once!');
  979. return;
  980. }
  981. $executed = true;
  982. if (!isset($_SERVER["CONTENT_TYPE"]) or strpos($_SERVER["CONTENT_TYPE"], 'multipart/form-data') !== false) {
  983. // Not a post or 'multipart/form-data' which is not compatible with "php://input" reading.
  984. return;
  985. }
  986. if (!isloggedin() or isguestuser()) {
  987. // Only real users post huge forms.
  988. return;
  989. }
  990. $max = (int)ini_get('max_input_vars');
  991. if ($max <= 0) {
  992. // Most probably PHP < 5.3.9 that does not implement this limit.
  993. return;
  994. }
  995. if ($max >= 200000) {
  996. // This value should be ok for all our forms, by setting it in php.ini
  997. // admins may prevent any unexpected regressions caused by this hack.
  998. // Note there is no need to worry about DDoS caused by making this limit very high
  999. // because there are very many easier ways to DDoS any Moodle server.
  1000. return;
  1001. }
  1002. // Worst case is advanced checkboxes which use up to two max_input_vars
  1003. // slots for each entry in $_POST, because of sending two fields with the
  1004. // same name. So count everything twice just in case.
  1005. if (count($_POST, COUNT_RECURSIVE) * 2 < $max) {
  1006. return;
  1007. }
  1008. // Large POST request with enctype supported by php://input.
  1009. // Parse php://input in chunks to bypass max_input_vars limit, which also applies to parse_str().
  1010. $str = file_get_contents("php://input");
  1011. if ($str === false or $str === '') {
  1012. // Some weird error.
  1013. return;
  1014. }
  1015. $delim = '&';
  1016. $fun = function($p) use ($delim) {
  1017. return implode($delim, $p);
  1018. };
  1019. $chunks = array_map($fun, array_chunk(explode($delim, $str), $max));
  1020. // Clear everything from existing $_POST array, otherwise it might be included
  1021. // twice (this affects array params primarily).
  1022. foreach ($_POST as $key => $value) {
  1023. unset($_POST[$key]);
  1024. // Also clear from request array - but only the things that are in $_POST,
  1025. // that way it will leave the things from a get request if any.
  1026. unset($_REQUEST[$key]);
  1027. }
  1028. foreach ($chunks as $chunk) {
  1029. $values = array();
  1030. parse_str($chunk, $values);
  1031. merge_query_params($_POST, $values);
  1032. merge_query_params($_REQUEST, $values);
  1033. }
  1034. }
  1035. /**
  1036. * Merge parsed POST chunks.
  1037. *
  1038. * NOTE: this is not perfect, but it should work in most cases hopefully.
  1039. *
  1040. * @param array $target
  1041. * @param array $values
  1042. */
  1043. function merge_query_params(array &$target, array $values) {
  1044. if (isset($values[0]) and isset($target[0])) {
  1045. // This looks like a split [] array, lets verify the keys are continuous starting with 0.
  1046. $keys1 = array_keys($values);
  1047. $keys2 = array_keys($target);
  1048. if ($keys1 === array_keys($keys1) and $keys2 === array_keys($keys2)) {
  1049. foreach ($values as $v) {
  1050. $target[] = $v;
  1051. }
  1052. return;
  1053. }
  1054. }
  1055. foreach ($values as $k => $v) {
  1056. if (!isset($target[$k])) {
  1057. $target[$k] = $v;
  1058. continue;
  1059. }
  1060. if (is_array($target[$k]) and is_array($v)) {
  1061. merge_query_params($target[$k], $v);
  1062. continue;
  1063. }
  1064. // We should not get here unless there are duplicates in params.
  1065. $target[$k] = $v;
  1066. }
  1067. }
  1068. /**
  1069. * Initializes our performance info early.
  1070. *
  1071. * Pairs up with get_performance_info() which is actually
  1072. * in moodlelib.php. This function is here so that we can
  1073. * call it before all the libs are pulled in.
  1074. *
  1075. * @uses $PERF
  1076. */
  1077. function init_performance_info() {
  1078. global $PERF, $CFG, $USER;
  1079. $PERF = new stdClass();
  1080. $PERF->logwrites = 0;
  1081. if (function_exists('microtime')) {
  1082. $PERF->starttime = microtime();
  1083. }
  1084. if (function_exists('memory_get_usage')) {
  1085. $PERF->startmemory = memory_get_usage();
  1086. }
  1087. if (function_exists('posix_times')) {
  1088. $PERF->startposixtimes = posix_times();
  1089. }
  1090. }
  1091. /**
  1092. * Indicates whether we are in the middle of the initial Moodle install.
  1093. *
  1094. * Very occasionally it is necessary avoid running certain bits of code before the
  1095. * Moodle installation has completed. The installed flag is set in admin/index.php
  1096. * after Moodle core and all the plugins have been installed, but just before
  1097. * the person doing the initial install is asked to choose the admin password.
  1098. *
  1099. * @return boolean true if the initial install is not complete.
  1100. */
  1101. function during_initial_install() {
  1102. global $CFG;
  1103. return empty($CFG->rolesactive);
  1104. }
  1105. /**
  1106. * Function to raise the memory limit to a new value.
  1107. * Will respect the memory limit if it is higher, thus allowing
  1108. * settings in php.ini, apache conf or command line switches
  1109. * to override it.
  1110. *
  1111. * The memory limit should be expressed with a constant
  1112. * MEMORY_STANDARD, MEMORY_EXTRA or MEMORY_HUGE.
  1113. * It is possible to use strings or integers too (eg:'128M').
  1114. *
  1115. * @param mixed $newlimit the new memory limit
  1116. * @return bool success
  1117. */
  1118. function raise_memory_limit($newlimit) {
  1119. global $CFG;
  1120. if ($newlimit == MEMORY_UNLIMITED) {
  1121. ini_set('memory_limit', -1);
  1122. return true;
  1123. } else if ($newlimit == MEMORY_STANDARD) {
  1124. if (PHP_INT_SIZE > 4) {
  1125. $newlimit = get_real_size('128M'); // 64bit needs more memory
  1126. } else {
  1127. $newlimit = get_real_size('96M');
  1128. }
  1129. } else if ($newlimit == MEMORY_EXTRA) {
  1130. if (PHP_INT_SIZE > 4) {
  1131. $newlimit = get_real_size('384M'); // 64bit needs more memory
  1132. } else {
  1133. $newlimit = get_real_size('256M');
  1134. }
  1135. if (!empty($CFG->extramemorylimit)) {
  1136. $extra = get_real_size($CFG->extramemorylimit);
  1137. if ($extra > $newlimit) {
  1138. $newlimit = $extra;
  1139. }
  1140. }
  1141. } else if ($newlimit == MEMORY_HUGE) {
  1142. // MEMORY_HUGE uses 2G or MEMORY_EXTRA, whichever is bigger.
  1143. $newlimit = get_real_size('2G');
  1144. if (!empty($CFG->extramemorylimit)) {
  1145. $extra = get_real_size($CFG->extramemorylimit);
  1146. if ($extra > $newlimit) {
  1147. $newlimit = $extra;
  1148. }
  1149. }
  1150. } else {
  1151. $newlimit = get_real_size($newlimit);
  1152. }
  1153. if ($newlimit <= 0) {
  1154. debugging('Invalid memory limit specified.');
  1155. return false;
  1156. }
  1157. $cur = ini_get('memory_limit');
  1158. if (empty($cur)) {
  1159. // if php is compiled without --enable-memory-limits
  1160. // apparently memory_limit is set to ''
  1161. $cur = 0;
  1162. } else {
  1163. if ($cur == -1){
  1164. return true; // unlimited mem!
  1165. }
  1166. $cur = get_real_size($cur);
  1167. }
  1168. if ($newlimit > $cur) {
  1169. ini_set('memory_limit', $newlimit);
  1170. return true;
  1171. }
  1172. return false;
  1173. }
  1174. /**
  1175. * Function to reduce the memory limit to a new value.
  1176. * Will respect the memory limit if it is lower, thus allowing
  1177. * settings in php.ini, apache conf or command line switches
  1178. * to override it
  1179. *
  1180. * The memory limit should be expressed with a string (eg:'64M')
  1181. *
  1182. * @param string $newlimit the new memory limit
  1183. * @return bool
  1184. */
  1185. function reduce_memory_limit($newlimit) {
  1186. if (empty($newlimit)) {
  1187. return false;
  1188. }
  1189. $cur = ini_get('memory_limit');
  1190. if (empty($cur)) {
  1191. // if php is compiled without --enable-memory-limits
  1192. // apparently memory_limit is set to ''
  1193. $cur = 0;
  1194. } else {
  1195. if ($cur == -1){
  1196. return true; // unlimited mem!
  1197. }
  1198. $cur = get_real_size($cur);
  1199. }
  1200. $new = get_real_size($newlimit);
  1201. // -1 is smaller, but it means unlimited
  1202. if ($new < $cur && $new != -1) {
  1203. ini_set('memory_limit', $newlimit);
  1204. return true;
  1205. }
  1206. return false;
  1207. }
  1208. /**
  1209. * Converts numbers like 10M into bytes.
  1210. *
  1211. * @param string $size The size to be converted
  1212. * @return int
  1213. */
  1214. function get_real_size($size = 0) {
  1215. if (!$size) {
  1216. return 0;
  1217. }
  1218. static $binaryprefixes = array(
  1219. 'K' => 1024 ** 1,
  1220. 'k' => 1024 ** 1,
  1221. 'M' => 1024 ** 2,
  1222. 'm' => 1024 ** 2,
  1223. 'G' => 1024 ** 3,
  1224. 'g' => 1024 ** 3,
  1225. 'T' => 1024 ** 4,
  1226. 't' => 1024 ** 4,
  1227. 'P' => 1024 ** 5,
  1228. 'p' => 1024 ** 5,
  1229. );
  1230. if (preg_match('/^([0-9]+)([KMGTP])/i', $size, $matches)) {
  1231. return $matches[1] * $binaryprefixes[$matches[2]];
  1232. }
  1233. return (int) $size;
  1234. }
  1235. /**
  1236. * Try to disable all output buffering and purge
  1237. * all headers.
  1238. *
  1239. * @access private to be called only from lib/setup.php !
  1240. * @return void
  1241. */
  1242. function disable_output_buffering() {
  1243. $olddebug = error_reporting(0);
  1244. // disable compression, it would prevent closing of buffers
  1245. if (ini_get_bool('zlib.output_compression')) {
  1246. ini_set('zlib.output_compression', 'Off');
  1247. }
  1248. // try to flush everything all the time
  1249. ob_implicit_flush(true);
  1250. // close all buffers if possible and discard any existing output
  1251. // this can actually work around some whitespace problems in config.php
  1252. while(ob_get_level()) {
  1253. if (!ob_end_clean()) {
  1254. // prevent infinite loop when buffer can not be closed
  1255. break;
  1256. }
  1257. }
  1258. // disable any other output handlers
  1259. ini_set('output_handler', '');
  1260. error_reporting($olddebug);
  1261. // Disable buffering in nginx.
  1262. header('X-Accel-Buffering: no');
  1263. }
  1264. /**
  1265. * Check whether a major upgrade is needed.
  1266. *
  1267. * That is defined as an upgrade that changes something really fundamental
  1268. * in the database, so nothing can possibly work until the database has
  1269. * been updated, and that is defined by the hard-coded version number in
  1270. * this function.
  1271. *
  1272. * @return bool
  1273. */
  1274. function is_major_upgrade_required() {
  1275. global $CFG;
  1276. $lastmajordbchanges = 2021101900.01;
  1277. $required = empty($CFG->version);
  1278. $required = $required || (float)$CFG->version < $lastmajordbchanges;
  1279. $required = $required || during_initial_install();
  1280. $required = $required || !empty($CFG->adminsetuppending);
  1281. return $required;
  1282. }
  1283. /**
  1284. * Redirect to the Notifications page if a major upgrade is required, and
  1285. * terminate the current user session.
  1286. */
  1287. function redirect_if_major_upgrade_required() {
  1288. global $CFG;
  1289. if (is_major_upgrade_required()) {
  1290. try {
  1291. @\core\session\manager::terminate_current();
  1292. } catch (Exception $e) {
  1293. // Ignore any errors, redirect to upgrade anyway.
  1294. }
  1295. $url = $CFG->wwwroot . '/' . $CFG->admin . '/index.php';
  1296. @header($_SERVER['SERVER_PROTOCOL'] . ' 303 See Other');
  1297. @header('Location: ' . $url);
  1298. echo bootstrap_renderer::plain_redirect_message(htmlspecialchars($url));
  1299. exit;
  1300. }
  1301. }
  1302. /**
  1303. * Makes sure that upgrade process is not running
  1304. *
  1305. * To be inserted in the core functions that can not be called by pluigns during upgrade.
  1306. * Core upgrade should not use any API functions at all.
  1307. * See {@link http://docs.moodle.org/dev/Upgrade_API#Upgrade_code_restrictions}
  1308. *
  1309. * @throws moodle_exception if executed from inside of upgrade script and $warningonly is false
  1310. * @param bool $warningonly if true displays a warning instead of throwing an exception
  1311. * @return bool true if executed from outside of upgrade process, false if from inside upgrade process and function is used for warning only
  1312. */
  1313. function upgrade_ensure_not_running($warningonly = false) {
  1314. global $CFG;
  1315. if (!empty($CFG->upgraderunning)) {
  1316. if (!$warningonly) {
  1317. throw new moodle_exception('cannotexecduringupgrade');
  1318. } else {
  1319. debugging(get_string('cannotexecduringupgrade', 'error'), DEBUG_DEVELOPER);
  1320. return false;
  1321. }
  1322. }
  1323. return true;
  1324. }
  1325. /**
  1326. * Function to check if a directory exists and by default create it if not exists.
  1327. *
  1328. * Previously this was accepting paths only from dataroot, but we now allow
  1329. * files outside of dataroot if you supply custom paths for some settings in config.php.
  1330. * This function does not verify that the directory is writable.
  1331. *
  1332. * NOTE: this function uses current file stat cache,
  1333. * please use clearstatcache() before this if you expect that the
  1334. * directories may have been removed recently from a different request.
  1335. *
  1336. * @param string $dir absolute directory path
  1337. * @param boolean $create directory if does not exist
  1338. * @param boolean $recursive create directory recursively
  1339. * @return boolean true if directory exists or created, false otherwise
  1340. */
  1341. function check_dir_exists($dir, $create = true, $recursive = true) {
  1342. global $CFG;
  1343. umask($CFG->umaskpermissions);
  1344. if (is_dir($dir)) {
  1345. return true;
  1346. }
  1347. if (!$create) {
  1348. return false;
  1349. }
  1350. return mkdir($dir, $CFG->directorypermissions, $recursive);
  1351. }
  1352. /**
  1353. * Create a new unique directory within the specified directory.
  1354. *
  1355. * @param string $basedir The directory to create your new unique directory within.
  1356. * @param bool $exceptiononerror throw exception if error encountered
  1357. * @return string The created directory
  1358. * @throws invalid_dataroot_permissions
  1359. */
  1360. function make_unique_writable_directory($basedir, $exceptiononerror = true) {
  1361. if (!is_dir($basedir) || !is_writable($basedir)) {
  1362. // The basedir is not writable. We will not be able to create the child directory.
  1363. if ($exceptiononerror) {
  1364. throw new invalid_dataroot_permissions($basedir . ' is not writable. Unable to create a unique directory within it.');
  1365. } else {
  1366. return false;
  1367. }
  1368. }
  1369. do {
  1370. // Let's use uniqid() because it's "unique enough" (microtime based). The loop does handle repetitions.
  1371. // Windows and old PHP don't like very long paths, so try to keep this shorter. See MDL-69975.
  1372. $uniquedir = $basedir . DIRECTORY_SEPARATOR . uniqid();
  1373. } while (
  1374. // Ensure that basedir is still writable - if we do not check, we could get stuck in a loop here.
  1375. is_writable($basedir) &&
  1376. // Make the new unique directory. If the directory already exists, it will return false.
  1377. !make_writable_directory($uniquedir, $exceptiononerror) &&
  1378. // Ensure that the directory now exists
  1379. file_exists($uniquedir) && is_dir($uniquedir)
  1380. );
  1381. // Check that the directory was correctly created.
  1382. if (!file_exists($uniquedir) || !is_dir($uniquedir) || !is_writable($uniquedir)) {
  1383. if ($exceptiononerror) {
  1384. throw new invalid_dataroot_permissions('Unique directory creation failed.');
  1385. } else {
  1386. return false;
  1387. }
  1388. }
  1389. return $uniquedir;
  1390. }
  1391. /**
  1392. * Create a directory and make sure it is writable.
  1393. *
  1394. * @private
  1395. * @param string $dir the full path of the directory to be created
  1396. * @param bool $exceptiononerror throw exception if error encountered
  1397. * @return string|false Returns full path to directory if successful, false if not; may throw exception
  1398. */
  1399. function make_writable_directory($dir, $exceptiononerror = true) {
  1400. global $CFG;
  1401. if (file_exists($dir) and !is_dir($dir)) {
  1402. if ($exceptiononerror) {
  1403. throw new coding_exception($dir.' directory can not be created, file with the same name already exists.');
  1404. } else {
  1405. return false;
  1406. }
  1407. }
  1408. umask($CFG->umaskpermissions);
  1409. if (!file_exists($dir)) {
  1410. if (!@mkdir($dir, $CFG->directorypermissions, true)) {
  1411. clearstatcache();
  1412. // There might be a race condition when creating directory.
  1413. if (!is_dir($dir)) {
  1414. if ($exceptiononerror) {
  1415. throw new invalid_dataroot_permissions($dir.' can not be created, check permissions.');
  1416. } else {
  1417. debugging('Can not create directory: '.$dir, DEBUG_DEVELOPER);
  1418. return false;
  1419. }
  1420. }
  1421. }
  1422. }
  1423. if (!is_writable($dir)) {
  1424. if ($exceptiononerror) {
  1425. throw new invalid_dataroot_permissions($dir.' is not writable, check permissions.');
  1426. } else {
  1427. return false;
  1428. }
  1429. }
  1430. return $dir;
  1431. }
  1432. /**
  1433. * Protect a directory from web access.
  1434. * Could be extended in the future to support other mechanisms (e.g. other webservers).
  1435. *
  1436. * @private
  1437. * @param string $dir the full path of the directory to be protected
  1438. */
  1439. function protect_directory($dir) {
  1440. global $CFG;
  1441. // Make sure a .htaccess file is here, JUST IN CASE the files area is in the open and .htaccess is supported
  1442. if (!file_exists("$dir/.htaccess")) {
  1443. if ($handle = fopen("$dir/.htaccess", 'w')) { // For safety
  1444. @fwrite($handle, "deny from all\r\nAllowOverride None\r\nNote: this file is broken intentionally, we do not want anybody to undo it in subdirectory!\r\n");
  1445. @fclose($handle);
  1446. @chmod("$dir/.htaccess", $CFG->filepermissions);
  1447. }
  1448. }
  1449. }
  1450. /**
  1451. * Create a directory under dataroot and make sure it is writable.
  1452. * Do not use for temporary and cache files - see make_temp_directory() and make_cache_directory().
  1453. *
  1454. * @param string $directory the full path of the directory to be created under $CFG->dataroot
  1455. * @param bool $exceptiononerror throw exception if error encountered
  1456. * @return string|false Returns full path to directory if successful, false if not; may throw exception
  1457. */
  1458. function make_upload_directory($directory, $exceptiononerror = true) {
  1459. global $CFG;
  1460. if (strpos($directory, 'temp/') === 0 or $directory === 'temp') {
  1461. debugging('Use make_temp_directory() for creation of temporary directory and $CFG->tempdir to get the location.');
  1462. } else if (strpos($directory, 'cache/') === 0 or $directory === 'cache') {
  1463. debugging('Use make_cache_directory() for creation of cache directory and $CFG->cachedir to get the location.');
  1464. } else if (strpos($directory, 'localcache/') === 0 or $directory === 'localcache') {
  1465. debugging('Use make_localcache_directory() for creation of local cache directory and $CFG->localcachedir to get the location.');
  1466. }
  1467. protect_directory($CFG->dataroot);
  1468. return make_writable_directory("$CFG->dataroot/$directory", $exceptiononerror);
  1469. }
  1470. /**
  1471. * Get a per-request storage directory in the tempdir.
  1472. *
  1473. * The directory is automatically cleaned up during the shutdown handler.
  1474. *
  1475. * @param bool $exceptiononerror throw exception if error encountered
  1476. * @param bool $forcecreate Force creation of a new parent directory
  1477. * @return string Returns full path to directory if successful, false if not; may throw exception
  1478. */
  1479. function get_request_storage_directory($exceptiononerror = true, bool $forcecreate = false) {
  1480. global $CFG;
  1481. static $requestdir = null;
  1482. $writabledirectoryexists = (null !== $requestdir);
  1483. $writabledirectoryexists = $writabledirectoryexists && file_exists($requestdir);
  1484. $writabledirectoryexists = $writabledirectoryexists && is_dir($requestdir);
  1485. $writabledirectoryexists = $writabledirectoryexists && is_writable($requestdir);
  1486. $createnewdirectory = $forcecreate || !$writabledirectoryexists;
  1487. if ($createnewdirectory) {
  1488. // Let's add the first chars of siteidentifier only. This is to help separate
  1489. // paths on systems which host multiple moodles. We don't use the full id
  1490. // as Windows and old PHP don't like very long paths. See MDL-69975.
  1491. $basedir = $CFG->localrequestdir . '/' . substr($CFG->siteidentifier, 0, 4);
  1492. make_writable_directory($basedir);
  1493. protect_directory($basedir);
  1494. if ($dir = make_unique_writable_directory($basedir, $exceptiononerror)) {
  1495. // Register a shutdown handler to remove the directory.
  1496. \core_shutdown_manager::register_function('remove_dir', [$dir]);
  1497. }
  1498. $requestdir = $dir;
  1499. }
  1500. return $requestdir;
  1501. }
  1502. /**
  1503. * Create a per-request directory and make sure it is writable.
  1504. * This can only be used during the current request and will be tidied away
  1505. * automatically afterwards.
  1506. *
  1507. * A new, unique directory is always created within a shared base request directory.
  1508. *
  1509. * In some exceptional cases an alternative base directory may be required. This can be accomplished using the
  1510. * $forcecreate parameter. Typically this will only be requried where the file may be required during a shutdown handler
  1511. * which may or may not be registered after a previous request directory has been created.
  1512. *
  1513. * @param bool $exceptiononerror throw exception if error encountered
  1514. * @param bool $forcecreate Force creation of a new parent directory
  1515. * @return string The full path to directory if successful, false if not; may throw exception
  1516. */
  1517. function make_request_directory($exceptiononerror = true, bool $forcecreate = false) {
  1518. $basedir = get_request_storage_directory($exceptiononerror, $forcecreate);
  1519. return make_unique_writable_directory($basedir, $exceptiononerror);
  1520. }
  1521. /**
  1522. * Get the full path of a directory under $CFG->backuptempdir.
  1523. *
  1524. * @param string $directory the relative path of the directory under $CFG->backuptempdir
  1525. * @return string|false Returns full path to directory given a valid string; otherwise, false.
  1526. */
  1527. function get_backup_temp_directory($directory) {
  1528. global $CFG;
  1529. if (($directory === null) || ($directory === false)) {
  1530. return false;
  1531. }
  1532. return "$CFG->backuptempdir/$directory";
  1533. }
  1534. /**
  1535. * Create a directory under $CFG->backuptempdir and make sure it is writable.
  1536. *
  1537. * Do not use for storing generic temp files - see make_temp_directory() instead for this purpose.
  1538. *
  1539. * Backup temporary files must be on a shared storage.
  1540. *
  1541. * @param string $directory the relative path of the directory to be created under $CFG->backuptempdir
  1542. * @param bool $exceptiononerror throw exception if error encountered
  1543. * @return string|false Returns full path to directory if successful, false if not; may throw exception
  1544. */
  1545. function make_backup_temp_directory($directory, $exceptiononerror = true) {
  1546. global $CFG;
  1547. if ($CFG->backuptempdir !== "$CFG->tempdir/backup") {
  1548. check_dir_exists($CFG->backuptempdir, true, true);
  1549. protect_directory($CFG->backuptempdir);
  1550. } else {
  1551. protect_directory($CFG->tempdir);
  1552. }
  1553. return make_writable_directory("$CFG->backuptempdir/$directory", $exceptiononerror);
  1554. }
  1555. /**
  1556. * Create a directory under tempdir and make sure it is writable.
  1557. *
  1558. * Where possible, please use make_request_directory() and limit the scope
  1559. * of your data to the current HTTP request.
  1560. *
  1561. * Do not use for storing cache files - see make_cache_directory(), and
  1562. * make_localcache_directory() instead for this purpose.
  1563. *
  1564. * Temporary files must be on a shared storage, and heavy usage is
  1565. * discouraged due to the performance impact upon clustered environments.
  1566. *
  1567. * @param string $directory the full path of the directory to be created under $CFG->tempdir
  1568. * @param bool $exceptiononerror throw exception if error encountered
  1569. * @return string|false Returns full path to directory if successful, false if not; may throw exception
  1570. */
  1571. function make_temp_directory($directory, $exceptiononerror = true) {
  1572. global $CFG;
  1573. if ($CFG->tempdir !== "$CFG->dataroot/temp") {
  1574. check_dir_exists($CFG->tempdir, true, true);
  1575. protect_directory($CFG->tempdir);
  1576. } else {
  1577. protect_directory($CFG->dataroot);
  1578. }
  1579. return make_writable_directory("$CFG->tempdir/$directory", $exceptiononerror);
  1580. }
  1581. /**
  1582. * Create a directory under cachedir and make sure it is writable.
  1583. *
  1584. * Note: this cache directory is shared by all cluster nodes.
  1585. *
  1586. * @param string $directory the full path of the directory to be created under $CFG->cachedir
  1587. * @param bool $exceptiononerror throw exception if error encountered
  1588. * @return string|false Returns full path to directory if successful, false if not; may throw exception
  1589. */
  1590. function make_cache_directory($directory, $exceptiononerror = true) {
  1591. global $CFG;
  1592. if ($CFG->cachedir !== "$CFG->dataroot/cache") {
  1593. check_dir_exists($CFG->cachedir, true, true);
  1594. protect_directory($CFG->cachedir);
  1595. } else {
  1596. protect_directory($CFG->dataroot);
  1597. }
  1598. return make_writable_directory("$CFG->cachedir/$directory", $exceptiononerror);
  1599. }
  1600. /**
  1601. * Create a directory under localcachedir and make sure it is writable.
  1602. * The files in this directory MUST NOT change, use revisions or content hashes to
  1603. * work around this limitation - this means you can only add new files here.
  1604. *
  1605. * The content of this directory gets purged automatically on all cluster nodes
  1606. * after calling purge_all_caches() before new data is written to this directory.
  1607. *
  1608. * Note: this local cache directory does not need to be shared by cluster nodes.
  1609. *
  1610. * @param string $directory the relative path of the directory to be created under $CFG->localcachedir
  1611. * @param bool $exceptiononerror throw exception if error encountered
  1612. * @return string|false Returns full path to directory if successful, false if not; may throw exception
  1613. */
  1614. function make_localcache_directory($directory, $exceptiononerror = true) {
  1615. global $CFG;
  1616. make_writable_directory($CFG->localcachedir, $exceptiononerror);
  1617. if ($CFG->localcachedir !== "$CFG->dataroot/localcache") {
  1618. protect_directory($CFG->localcachedir);
  1619. } else {
  1620. protect_directory($CFG->dataroot);
  1621. }
  1622. if (!isset($CFG->localcachedirpurged)) {
  1623. $CFG->localcachedirpurged = 0;
  1624. }
  1625. $timestampfile = "$CFG->localcachedir/.lastpurged";
  1626. if (!file_exists($timestampfile)) {
  1627. touch($timestampfile);
  1628. @chmod($timestampfile, $CFG->filepermissions);
  1629. } else if (filemtime($timestampfile) < $CFG->localcachedirpurged) {
  1630. // This means our local cached dir was not purged yet.
  1631. remove_dir($CFG->localcachedir, true);
  1632. if ($CFG->localcachedir !== "$CFG->dataroot/localcache") {
  1633. protect_directory($CFG->localcachedir);
  1634. }
  1635. touch($timestampfile);
  1636. @chmod($timestampfile, $CFG->filepermissions);
  1637. clearstatcache();
  1638. }
  1639. if ($directory === '') {
  1640. return $CFG->localcachedir;
  1641. }
  1642. return make_writable_directory("$CFG->localcachedir/$directory", $exceptiononerror);
  1643. }
  1644. /**
  1645. * Webserver access user logging
  1646. */
  1647. function set_access_log_user() {
  1648. global $USER, $CFG;
  1649. if ($USER && isset($USER->username)) {
  1650. $logmethod = '';
  1651. $logvalue = 0;
  1652. if (!empty($CFG->apacheloguser) && function_exists('apache_note')) {
  1653. $logmethod = 'apache';
  1654. $logvalue = $CFG->apacheloguser;
  1655. }
  1656. if (!empty($CFG->headerloguser)) {
  1657. $logmethod = 'header';
  1658. $logvalue = $CFG->headerloguser;
  1659. }
  1660. if (!empty($logmethod)) {
  1661. $loguserid = $USER->id;
  1662. $logusername = clean_filename($USER->username);
  1663. $logname = '';
  1664. if (isset($USER->firstname)) {
  1665. // We can assume both will be set
  1666. // - even if to empty.
  1667. $logname = clean_filename($USER->firstname . " " . $USER->lastname);
  1668. }
  1669. if (\core\session\manager::is_loggedinas()) {
  1670. $realuser = \core\session\manager::get_realuser();
  1671. $logusername = clean_filename($realuser->username." as ".$logusername);
  1672. $logname = clean_filename($realuser->firstname." ".$realuser->lastname ." as ".$logname);
  1673. $loguserid = clean_filename($realuser->id." as ".$loguserid);
  1674. }
  1675. switch ($logvalue) {
  1676. case 3:
  1677. $logname = $logusername;
  1678. break;
  1679. case 2:
  1680. $logname = $logname;
  1681. break;
  1682. case 1:
  1683. default:
  1684. $logname = $loguserid;
  1685. break;
  1686. }
  1687. if ($logmethod == 'apache') {
  1688. apache_note('MOODLEUSER', $logname);
  1689. }
  1690. if ($logmethod == 'header') {
  1691. header("X-MOODLEUSER: $logname");
  1692. }
  1693. }
  1694. }
  1695. }
  1696. /**
  1697. * This class solves the problem of how to initialise $OUTPUT.
  1698. *
  1699. * The problem is caused be two factors
  1700. * <ol>
  1701. * <li>On the one hand, we cannot be sure when output will start. In particular,
  1702. * an error, which needs to be displayed, could be thrown at any time.</li>
  1703. * <li>On the other hand, we cannot be sure when we will have all the information
  1704. * necessary to correctly initialise $OUTPUT. $OUTPUT depends on the theme, which
  1705. * (potentially) depends on the current course, course categories, and logged in user.
  1706. * It also depends on whether the current page requires HTTPS.</li>
  1707. * </ol>
  1708. *
  1709. * So, it is hard to find a single natural place during Moodle script execution,
  1710. * which we can guarantee is the right time to initialise $OUTPUT. Instead we
  1711. * adopt the following strategy
  1712. * <ol>
  1713. * <li>We will initialise $OUTPUT the first time it is used.</li>
  1714. * <li>If, after $OUTPUT has been initialised, the script tries to change something
  1715. * that $OUTPUT depends on, we throw an exception making it clear that the script
  1716. * did something wrong.
  1717. * </ol>
  1718. *
  1719. * The only problem with that is, how do we initialise $OUTPUT on first use if,
  1720. * it is going to be used like $OUTPUT->somthing(...)? Well that is where this
  1721. * class comes in. Initially, we set up $OUTPUT = new bootstrap_renderer(). Then,
  1722. * when any method is called on that object, we initialise $OUTPUT, and pass the call on.
  1723. *
  1724. * Note that this class is used before lib/outputlib.php has been loaded, so we
  1725. * must be careful referring to classes/functions from there, they may not be
  1726. * defined yet, and we must avoid fatal errors.
  1727. *
  1728. * @copyright 2009 Tim Hunt
  1729. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1730. * @since Moodle 2.0
  1731. */
  1732. class bootstrap_renderer {
  1733. /**
  1734. * Handles re-entrancy. Without this, errors or debugging output that occur
  1735. * during the initialisation of $OUTPUT, cause infinite recursion.
  1736. * @var boolean
  1737. */
  1738. protected $initialising = false;
  1739. /**
  1740. * Have we started output yet?
  1741. * @return boolean true if the header has been printed.
  1742. */
  1743. public function has_started() {
  1744. return false;
  1745. }
  1746. /**
  1747. * Constructor - to be used by core code only.
  1748. * @param string $method The method to call
  1749. * @param array $arguments Arguments to pass to the method being called
  1750. * @return string
  1751. */
  1752. public function __call($method, $arguments) {
  1753. global $OUTPUT, $PAGE;
  1754. $recursing = false;
  1755. if ($method == 'notification') {
  1756. // Catch infinite recursion caused by debugging output during print_header.
  1757. $backtrace = debug_backtrace();
  1758. array_shift($backtrace);
  1759. array_shift($backtrace);
  1760. $recursing = is_early_init($backtrace);
  1761. }
  1762. $earlymethods = array(
  1763. 'fatal_error' => 'early_error',
  1764. 'notification' => 'early_notification',
  1765. );
  1766. // If lib/outputlib.php has been loaded, call it.
  1767. if (!empty($PAGE) && !$recursing) {
  1768. if (array_key_exists($method, $earlymethods)) {
  1769. //prevent PAGE->context warnings - exceptions might appear before we set any context
  1770. $PAGE->set_context(null);
  1771. }
  1772. $PAGE->initialise_theme_and_output();
  1773. return call_user_func_array(array($OUTPUT, $method), $arguments);
  1774. }
  1775. $this->initialising = true;
  1776. // Too soon to initialise $OUTPUT, provide a couple of key methods.
  1777. if (array_key_exists($method, $earlymethods)) {
  1778. return call_user_func_array(array('bootstrap_renderer', $earlymethods[$method]), $arguments);
  1779. }
  1780. throw new coding_exception('Attempt to start output before enough information is known to initialise the theme.');
  1781. }
  1782. /**
  1783. * Returns nicely formatted error message in a div box.
  1784. * @static
  1785. * @param string $message error message
  1786. * @param string $moreinfourl (ignored in early errors)
  1787. * @param string $link (ignored in early errors)
  1788. * @param array $backtrace
  1789. * @param string $debuginfo
  1790. * @return string
  1791. */
  1792. public static function early_error_content($message, $moreinfourl, $link, $backtrace, $debuginfo = null) {
  1793. global $CFG;
  1794. $content = "<div class='alert-danger'>$message</div>";
  1795. // Check whether debug is set.
  1796. $debug = (!empty($CFG->debug) && $CFG->debug >= DEBUG_DEVELOPER);
  1797. // Also check we have it set in the config file. This occurs if the method to read the config table from the
  1798. // database fails, reading from the config table is the first database interaction we have.
  1799. $debug = $debug || (!empty($CFG->config_php_settings['debug']) && $CFG->config_php_settings['debug'] >= DEBUG_DEVELOPER );
  1800. if ($debug) {
  1801. if (!empty($debuginfo)) {
  1802. // Remove all nasty JS.
  1803. if (function_exists('s')) { // Function may be not available for some early errors.
  1804. $debuginfo = s($debuginfo);
  1805. } else {
  1806. // Because weblib is not available for these early errors, we
  1807. // just duplicate s() code here to be safe.
  1808. $debuginfo = preg_replace('/&amp;#(\d+|x[0-9a-f]+);/i', '&#$1;',
  1809. htmlspecialchars($debuginfo, ENT_QUOTES | ENT_HTML401 | ENT_SUBSTITUTE));
  1810. }
  1811. $debuginfo = str_replace("\n", '<br />', $debuginfo); // keep newlines
  1812. $content .= '<div class="notifytiny">Debug info: ' . $debuginfo . '</div>';
  1813. }
  1814. if (!empty($backtrace)) {
  1815. $content .= '<div class="notifytiny">Stack trace: ' . format_backtrace($backtrace, false) . '</div>';
  1816. }
  1817. }
  1818. return $content;
  1819. }
  1820. /**
  1821. * This function should only be called by this class, or from exception handlers
  1822. * @static
  1823. * @param string $message error message
  1824. * @param string $moreinfourl (ignored in early errors)
  1825. * @param string $link (ignored in early errors)
  1826. * @param array $backtrace
  1827. * @param string $debuginfo extra information for developers
  1828. * @return string
  1829. */
  1830. public static function early_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null, $errorcode = null) {
  1831. global $CFG;
  1832. if (CLI_SCRIPT) {
  1833. echo "!!! $message !!!\n";
  1834. if (!empty($CFG->debug) and $CFG->debug >= DEBUG_DEVELOPER) {
  1835. if (!empty($debuginfo)) {
  1836. echo "\nDebug info: $debuginfo";
  1837. }
  1838. if (!empty($backtrace)) {
  1839. echo "\nStack trace: " . format_backtrace($backtrace, true);
  1840. }
  1841. }
  1842. return;
  1843. } else if (AJAX_SCRIPT) {
  1844. $e = new stdClass();
  1845. $e->error = $message;
  1846. $e->stacktrace = NULL;
  1847. $e->debuginfo = NULL;
  1848. if (!empty($CFG->debug) and $CFG->debug >= DEBUG_DEVELOPER) {
  1849. if (!empty($debuginfo)) {
  1850. $e->debuginfo = $debuginfo;
  1851. }
  1852. if (!empty($backtrace)) {
  1853. $e->stacktrace = format_backtrace($backtrace, true);
  1854. }
  1855. }
  1856. $e->errorcode = $errorcode;
  1857. @header('Content-Type: application/json; charset=utf-8');
  1858. echo json_encode($e);
  1859. return;
  1860. }
  1861. // In the name of protocol correctness, monitoring and performance
  1862. // profiling, set the appropriate error headers for machine consumption.
  1863. $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
  1864. @header($protocol . ' 500 Internal Server Error');
  1865. // better disable any caching
  1866. @header('Content-Type: text/html; charset=utf-8');
  1867. @header('X-UA-Compatible: IE=edge');
  1868. @header('Cache-Control: no-store, no-cache, must-revalidate');
  1869. @header('Cache-Control: post-check=0, pre-check=0', false);
  1870. @header('Pragma: no-cache');
  1871. @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
  1872. @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
  1873. if (function_exists('get_string')) {
  1874. $strerror = get_string('error');
  1875. } else {
  1876. $strerror = 'Error';
  1877. }
  1878. $content = self::early_error_content($message, $moreinfourl, $link, $backtrace, $debuginfo);
  1879. return self::plain_page($strerror, $content);
  1880. }
  1881. /**
  1882. * Early notification message
  1883. * @static
  1884. * @param string $message
  1885. * @param string $classes usually notifyproblem or notifysuccess
  1886. * @return string
  1887. */
  1888. public static function early_notification($message, $classes = 'notifyproblem') {
  1889. return '<div class="' . $classes . '">' . $message . '</div>';
  1890. }
  1891. /**
  1892. * Page should redirect message.
  1893. * @static
  1894. * @param string $encodedurl redirect url
  1895. * @return string
  1896. */
  1897. public static function plain_redirect_message($encodedurl) {
  1898. $message = '<div style="margin-top: 3em; margin-left:auto; margin-right:auto; text-align:center;">' . get_string('pageshouldredirect') . '<br /><a href="'.
  1899. $encodedurl .'">'. get_string('continue') .'</a></div>';
  1900. return self::plain_page(get_string('redirect'), $message);
  1901. }
  1902. /**
  1903. * Early redirection page, used before full init of $PAGE global
  1904. * @static
  1905. * @param string $encodedurl redirect url
  1906. * @param string $message redirect message
  1907. * @param int $delay time in seconds
  1908. * @return string redirect page
  1909. */
  1910. public static function early_redirect_message($encodedurl, $message, $delay) {
  1911. $meta = '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />';
  1912. $content = self::early_error_content($message, null, null, null);
  1913. $content .= self::plain_redirect_message($encodedurl);
  1914. return self::plain_page(get_string('redirect'), $content, $meta);
  1915. }
  1916. /**
  1917. * Output basic html page.
  1918. * @static
  1919. * @param string $title page title
  1920. * @param string $content page content
  1921. * @param string $meta meta tag
  1922. * @return string html page
  1923. */
  1924. public static function plain_page($title, $content, $meta = '') {
  1925. global $CFG;
  1926. if (function_exists('get_string') && function_exists('get_html_lang')) {
  1927. $htmllang = get_html_lang();
  1928. } else {
  1929. $htmllang = '';
  1930. }
  1931. $footer = '';
  1932. if (function_exists('get_performance_info')) { // Function may be not available for some early errors.
  1933. if (MDL_PERF_TEST) {
  1934. $perfinfo = get_performance_info();
  1935. $footer = '<footer>' . $perfinfo['html'] . '</footer>';
  1936. }
  1937. }
  1938. ob_start();
  1939. include($CFG->dirroot . '/error/plainpage.php');
  1940. $html = ob_get_contents();
  1941. ob_end_clean();
  1942. return $html;
  1943. }
  1944. }