| [ Index ] |
PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008] |
[Summary view] [Print] [Text view]
1 <?php // $Id$ 2 3 /////////////////////////////////////////////////////////////////////////// 4 // // 5 // NOTICE OF COPYRIGHT // 6 // // 7 // Moodle - Modular Object-Oriented Dynamic Learning Environment // 8 // http://moodle.org // 9 // // 10 // Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com // 11 // // 12 // This program is free software; you can redistribute it and/or modify // 13 // it under the terms of the GNU General Public License as published by // 14 // the Free Software Foundation; either version 2 of the License, or // 15 // (at your option) any later version. // 16 // // 17 // This program is distributed in the hope that it will be useful, // 18 // but WITHOUT ANY WARRANTY; without even the implied warranty of // 19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 20 // GNU General Public License for more details: // 21 // // 22 // http://www.gnu.org/copyleft/gpl.html // 23 // // 24 /////////////////////////////////////////////////////////////////////////// 25 26 /** 27 * moodlelib.php - Moodle main library 28 * 29 * Main library file of miscellaneous general-purpose Moodle functions. 30 * Other main libraries: 31 * - weblib.php - functions that produce web output 32 * - datalib.php - functions that access the database 33 * @author Martin Dougiamas 34 * @version $Id$ 35 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 36 * @package moodlecore 37 */ 38 39 /// CONSTANTS (Encased in phpdoc proper comments)///////////////////////// 40 41 /** 42 * Used by some scripts to check they are being called by Moodle 43 */ 44 define('MOODLE_INTERNAL', true); 45 46 /// Date and time constants /// 47 /** 48 * Time constant - the number of seconds in a year 49 */ 50 51 define('YEARSECS', 31536000); 52 53 /** 54 * Time constant - the number of seconds in a week 55 */ 56 define('WEEKSECS', 604800); 57 58 /** 59 * Time constant - the number of seconds in a day 60 */ 61 define('DAYSECS', 86400); 62 63 /** 64 * Time constant - the number of seconds in an hour 65 */ 66 define('HOURSECS', 3600); 67 68 /** 69 * Time constant - the number of seconds in a minute 70 */ 71 define('MINSECS', 60); 72 73 /** 74 * Time constant - the number of minutes in a day 75 */ 76 define('DAYMINS', 1440); 77 78 /** 79 * Time constant - the number of minutes in an hour 80 */ 81 define('HOURMINS', 60); 82 83 /// Parameter constants - every call to optional_param(), required_param() /// 84 /// or clean_param() should have a specified type of parameter. ////////////// 85 86 /** 87 * PARAM_RAW specifies a parameter that is not cleaned/processed in any way; 88 * originally was 0, but changed because we need to detect unknown 89 * parameter types and swiched order in clean_param(). 90 */ 91 define('PARAM_RAW', 666); 92 93 /** 94 * PARAM_CLEAN - obsoleted, please try to use more specific type of parameter. 95 * It was one of the first types, that is why it is abused so much ;-) 96 */ 97 define('PARAM_CLEAN', 0x0001); 98 99 /** 100 * PARAM_INT - integers only, use when expecting only numbers. 101 */ 102 define('PARAM_INT', 0x0002); 103 104 /** 105 * PARAM_INTEGER - an alias for PARAM_INT 106 */ 107 define('PARAM_INTEGER', 0x0002); 108 109 /** 110 * PARAM_NUMBER - a real/floating point number. 111 */ 112 define('PARAM_NUMBER', 0x000a); 113 114 /** 115 * PARAM_ALPHA - contains only english letters. 116 */ 117 define('PARAM_ALPHA', 0x0004); 118 119 /** 120 * PARAM_ACTION - an alias for PARAM_ALPHA, use for various actions in formas and urls 121 * @TODO: should we alias it to PARAM_ALPHANUM ? 122 */ 123 define('PARAM_ACTION', 0x0004); 124 125 /** 126 * PARAM_FORMAT - an alias for PARAM_ALPHA, use for names of plugins, formats, etc. 127 * @TODO: should we alias it to PARAM_ALPHANUM ? 128 */ 129 define('PARAM_FORMAT', 0x0004); 130 131 /** 132 * PARAM_NOTAGS - all html tags are stripped from the text. Do not abuse this type. 133 */ 134 define('PARAM_NOTAGS', 0x0008); 135 136 /** 137 * PARAM_MULTILANG - alias of PARAM_TEXT. 138 */ 139 define('PARAM_MULTILANG', 0x0009); 140 141 /** 142 * PARAM_TEXT - general plain text compatible with multilang filter, no other html tags. 143 */ 144 define('PARAM_TEXT', 0x0009); 145 146 /** 147 * PARAM_FILE - safe file name, all dangerous chars are stripped, protects against XSS, SQL injections and directory traversals 148 */ 149 define('PARAM_FILE', 0x0010); 150 151 /** 152 * PARAM_TAG - one tag (interests, blogs, etc.) - mostly international alphanumeric with spaces 153 */ 154 define('PARAM_TAG', 0x0011); 155 156 /** 157 * PARAM_TAGLIST - list of tags separated by commas (interests, blogs, etc.) 158 */ 159 define('PARAM_TAGLIST', 0x0012); 160 161 /** 162 * PARAM_PATH - safe relative path name, all dangerous chars are stripped, protects against XSS, SQL injections and directory traversals 163 * note: the leading slash is not removed, window drive letter is not allowed 164 */ 165 define('PARAM_PATH', 0x0020); 166 167 /** 168 * PARAM_HOST - expected fully qualified domain name (FQDN) or an IPv4 dotted quad (IP address) 169 */ 170 define('PARAM_HOST', 0x0040); 171 172 /** 173 * PARAM_URL - expected properly formatted URL. Please note that domain part is required, http://localhost/ is not acceppted but http://localhost.localdomain/ is ok. 174 */ 175 define('PARAM_URL', 0x0080); 176 177 /** 178 * PARAM_LOCALURL - expected properly formatted URL as well as one that refers to the local server itself. (NOT orthogonal to the others! Implies PARAM_URL!) 179 */ 180 define('PARAM_LOCALURL', 0x0180); 181 182 /** 183 * PARAM_CLEANFILE - safe file name, all dangerous and regional chars are removed, 184 * use when you want to store a new file submitted by students 185 */ 186 define('PARAM_CLEANFILE',0x0200); 187 188 /** 189 * PARAM_ALPHANUM - expected numbers and letters only. 190 */ 191 define('PARAM_ALPHANUM', 0x0400); 192 193 /** 194 * PARAM_BOOL - converts input into 0 or 1, use for switches in forms and urls. 195 */ 196 define('PARAM_BOOL', 0x0800); 197 198 /** 199 * PARAM_CLEANHTML - cleans submitted HTML code and removes slashes 200 * note: do not forget to addslashes() before storing into database! 201 */ 202 define('PARAM_CLEANHTML',0x1000); 203 204 /** 205 * PARAM_ALPHAEXT the same contents as PARAM_ALPHA plus the chars in quotes: "/-_" allowed, 206 * suitable for include() and require() 207 * @TODO: should we rename this function to PARAM_SAFEDIRS?? 208 */ 209 define('PARAM_ALPHAEXT', 0x2000); 210 211 /** 212 * PARAM_SAFEDIR - safe directory name, suitable for include() and require() 213 */ 214 define('PARAM_SAFEDIR', 0x4000); 215 216 /** 217 * PARAM_SEQUENCE - expects a sequence of numbers like 8 to 1,5,6,4,6,8,9. Numbers and comma only. 218 */ 219 define('PARAM_SEQUENCE', 0x8000); 220 221 /** 222 * PARAM_PEM - Privacy Enhanced Mail format 223 */ 224 define('PARAM_PEM', 0x10000); 225 226 /** 227 * PARAM_BASE64 - Base 64 encoded format 228 */ 229 define('PARAM_BASE64', 0x20000); 230 231 232 /// Page types /// 233 /** 234 * PAGE_COURSE_VIEW is a definition of a page type. For more information on the page class see moodle/lib/pagelib.php. 235 */ 236 define('PAGE_COURSE_VIEW', 'course-view'); 237 238 /// Debug levels /// 239 /** no warnings at all */ 240 define ('DEBUG_NONE', 0); 241 /** E_ERROR | E_PARSE */ 242 define ('DEBUG_MINIMAL', 5); 243 /** E_ERROR | E_PARSE | E_WARNING | E_NOTICE */ 244 define ('DEBUG_NORMAL', 15); 245 /** E_ALL without E_STRICT for now, do show recoverable fatal errors */ 246 define ('DEBUG_ALL', 6143); 247 /** DEBUG_ALL with extra Moodle debug messages - (DEBUG_ALL | 32768) */ 248 define ('DEBUG_DEVELOPER', 38911); 249 250 /** 251 * Blog access level constant declaration 252 */ 253 define ('BLOG_USER_LEVEL', 1); 254 define ('BLOG_GROUP_LEVEL', 2); 255 define ('BLOG_COURSE_LEVEL', 3); 256 define ('BLOG_SITE_LEVEL', 4); 257 define ('BLOG_GLOBAL_LEVEL', 5); 258 259 /** 260 * Tag constanst 261 */ 262 //To prevent problems with multibytes strings, this should not exceed the 263 //length of "varchar(255) / 3 (bytes / utf-8 character) = 85". 264 define('TAG_MAX_LENGTH', 50); 265 266 /** 267 * Password policy constants 268 */ 269 define ('PASSWORD_LOWER', 'abcdefghijklmnopqrstuvwxyz'); 270 define ('PASSWORD_UPPER', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'); 271 define ('PASSWORD_DIGITS', '0123456789'); 272 define ('PASSWORD_NONALPHANUM', '.,;:!?_-+/*@#&$'); 273 274 if (!defined('SORT_LOCALE_STRING')) { // PHP < 4.4.0 - TODO: remove in 2.0 275 define('SORT_LOCALE_STRING', SORT_STRING); 276 } 277 278 279 /// PARAMETER HANDLING //////////////////////////////////////////////////// 280 281 /** 282 * Returns a particular value for the named variable, taken from 283 * POST or GET. If the parameter doesn't exist then an error is 284 * thrown because we require this variable. 285 * 286 * This function should be used to initialise all required values 287 * in a script that are based on parameters. Usually it will be 288 * used like this: 289 * $id = required_param('id'); 290 * 291 * @param string $parname the name of the page parameter we want 292 * @param int $type expected type of parameter 293 * @return mixed 294 */ 295 function required_param($parname, $type=PARAM_CLEAN) { 296 297 // detect_unchecked_vars addition 298 global $CFG; 299 if (!empty($CFG->detect_unchecked_vars)) { 300 global $UNCHECKED_VARS; 301 unset ($UNCHECKED_VARS->vars[$parname]); 302 } 303 304 if (isset($_POST[$parname])) { // POST has precedence 305 $param = $_POST[$parname]; 306 } else if (isset($_GET[$parname])) { 307 $param = $_GET[$parname]; 308 } else { 309 error('A required parameter ('.$parname.') was missing'); 310 } 311 312 return clean_param($param, $type); 313 } 314 315 /** 316 * Returns a particular value for the named variable, taken from 317 * POST or GET, otherwise returning a given default. 318 * 319 * This function should be used to initialise all optional values 320 * in a script that are based on parameters. Usually it will be 321 * used like this: 322 * $name = optional_param('name', 'Fred'); 323 * 324 * @param string $parname the name of the page parameter we want 325 * @param mixed $default the default value to return if nothing is found 326 * @param int $type expected type of parameter 327 * @return mixed 328 */ 329 function optional_param($parname, $default=NULL, $type=PARAM_CLEAN) { 330 331 // detect_unchecked_vars addition 332 global $CFG; 333 if (!empty($CFG->detect_unchecked_vars)) { 334 global $UNCHECKED_VARS; 335 unset ($UNCHECKED_VARS->vars[$parname]); 336 } 337 338 if (isset($_POST[$parname])) { // POST has precedence 339 $param = $_POST[$parname]; 340 } else if (isset($_GET[$parname])) { 341 $param = $_GET[$parname]; 342 } else { 343 return $default; 344 } 345 346 return clean_param($param, $type); 347 } 348 349 /** 350 * Used by {@link optional_param()} and {@link required_param()} to 351 * clean the variables and/or cast to specific types, based on 352 * an options field. 353 * <code> 354 * $course->format = clean_param($course->format, PARAM_ALPHA); 355 * $selectedgrade_item = clean_param($selectedgrade_item, PARAM_CLEAN); 356 * </code> 357 * 358 * @uses $CFG 359 * @uses PARAM_RAW 360 * @uses PARAM_CLEAN 361 * @uses PARAM_CLEANHTML 362 * @uses PARAM_INT 363 * @uses PARAM_NUMBER 364 * @uses PARAM_ALPHA 365 * @uses PARAM_ALPHANUM 366 * @uses PARAM_ALPHAEXT 367 * @uses PARAM_SEQUENCE 368 * @uses PARAM_BOOL 369 * @uses PARAM_NOTAGS 370 * @uses PARAM_TEXT 371 * @uses PARAM_SAFEDIR 372 * @uses PARAM_CLEANFILE 373 * @uses PARAM_FILE 374 * @uses PARAM_PATH 375 * @uses PARAM_HOST 376 * @uses PARAM_URL 377 * @uses PARAM_LOCALURL 378 * @uses PARAM_PEM 379 * @uses PARAM_BASE64 380 * @uses PARAM_TAG 381 * @uses PARAM_SEQUENCE 382 * @param mixed $param the variable we are cleaning 383 * @param int $type expected format of param after cleaning. 384 * @return mixed 385 */ 386 function clean_param($param, $type) { 387 388 global $CFG; 389 390 if (is_array($param)) { // Let's loop 391 $newparam = array(); 392 foreach ($param as $key => $value) { 393 $newparam[$key] = clean_param($value, $type); 394 } 395 return $newparam; 396 } 397 398 switch ($type) { 399 case PARAM_RAW: // no cleaning at all 400 return $param; 401 402 case PARAM_CLEAN: // General HTML cleaning, try to use more specific type if possible 403 if (is_numeric($param)) { 404 return $param; 405 } 406 $param = stripslashes($param); // Needed for kses to work fine 407 $param = clean_text($param); // Sweep for scripts, etc 408 return addslashes($param); // Restore original request parameter slashes 409 410 case PARAM_CLEANHTML: // prepare html fragment for display, do not store it into db!! 411 $param = stripslashes($param); // Remove any slashes 412 $param = clean_text($param); // Sweep for scripts, etc 413 return trim($param); 414 415 case PARAM_INT: 416 return (int)$param; // Convert to integer 417 418 case PARAM_NUMBER: 419 return (float)$param; // Convert to integer 420 421 case PARAM_ALPHA: // Remove everything not a-z 422 return eregi_replace('[^a-zA-Z]', '', $param); 423 424 case PARAM_ALPHANUM: // Remove everything not a-zA-Z0-9 425 return eregi_replace('[^A-Za-z0-9]', '', $param); 426 427 case PARAM_ALPHAEXT: // Remove everything not a-zA-Z/_- 428 return eregi_replace('[^a-zA-Z/_-]', '', $param); 429 430 case PARAM_SEQUENCE: // Remove everything not 0-9, 431 return eregi_replace('[^0-9,]', '', $param); 432 433 case PARAM_BOOL: // Convert to 1 or 0 434 $tempstr = strtolower($param); 435 if ($tempstr == 'on' or $tempstr == 'yes' ) { 436 $param = 1; 437 } else if ($tempstr == 'off' or $tempstr == 'no') { 438 $param = 0; 439 } else { 440 $param = empty($param) ? 0 : 1; 441 } 442 return $param; 443 444 case PARAM_NOTAGS: // Strip all tags 445 return strip_tags($param); 446 447 case PARAM_TEXT: // leave only tags needed for multilang 448 return clean_param(strip_tags($param, '<lang><span>'), PARAM_CLEAN); 449 450 case PARAM_SAFEDIR: // Remove everything not a-zA-Z0-9_- 451 return eregi_replace('[^a-zA-Z0-9_-]', '', $param); 452 453 case PARAM_CLEANFILE: // allow only safe characters 454 return clean_filename($param); 455 456 case PARAM_FILE: // Strip all suspicious characters from filename 457 $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':\\/]', '', $param); 458 $param = ereg_replace('\.\.+', '', $param); 459 if($param == '.') { 460 $param = ''; 461 } 462 return $param; 463 464 case PARAM_PATH: // Strip all suspicious characters from file path 465 $param = str_replace('\\\'', '\'', $param); 466 $param = str_replace('\\"', '"', $param); 467 $param = str_replace('\\', '/', $param); 468 $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':]', '', $param); 469 $param = ereg_replace('\.\.+', '', $param); 470 $param = ereg_replace('//+', '/', $param); 471 return ereg_replace('/(\./)+', '/', $param); 472 473 case PARAM_HOST: // allow FQDN or IPv4 dotted quad 474 $param = preg_replace('/[^\.\d\w-]/','', $param ); // only allowed chars 475 // match ipv4 dotted quad 476 if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/',$param, $match)){ 477 // confirm values are ok 478 if ( $match[0] > 255 479 || $match[1] > 255 480 || $match[3] > 255 481 || $match[4] > 255 ) { 482 // hmmm, what kind of dotted quad is this? 483 $param = ''; 484 } 485 } elseif ( preg_match('/^[\w\d\.-]+$/', $param) // dots, hyphens, numbers 486 && !preg_match('/^[\.-]/', $param) // no leading dots/hyphens 487 && !preg_match('/[\.-]$/', $param) // no trailing dots/hyphens 488 ) { 489 // all is ok - $param is respected 490 } else { 491 // all is not ok... 492 $param=''; 493 } 494 return $param; 495 496 case PARAM_URL: // allow safe ftp, http, mailto urls 497 include_once($CFG->dirroot . '/lib/validateurlsyntax.php'); 498 if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p?f?q?r?')) { 499 // all is ok, param is respected 500 } else { 501 $param =''; // not really ok 502 } 503 return $param; 504 505 case PARAM_LOCALURL: // allow http absolute, root relative and relative URLs within wwwroot 506 $param = clean_param($param, PARAM_URL); 507 if (!empty($param)) { 508 if (preg_match(':^/:', $param)) { 509 // root-relative, ok! 510 } elseif (preg_match('/^'.preg_quote($CFG->wwwroot, '/').'/i',$param)) { 511 // absolute, and matches our wwwroot 512 } else { 513 // relative - let's make sure there are no tricks 514 if (validateUrlSyntax($param, 's-u-P-a-p-f+q?r?')) { 515 // looks ok. 516 } else { 517 $param = ''; 518 } 519 } 520 } 521 return $param; 522 523 case PARAM_PEM: 524 $param = trim($param); 525 // PEM formatted strings may contain letters/numbers and the symbols 526 // forward slash: / 527 // plus sign: + 528 // equal sign: = 529 // , surrounded by BEGIN and END CERTIFICATE prefix and suffixes 530 if (preg_match('/^-----BEGIN CERTIFICATE-----([\s\w\/\+=]+)-----END CERTIFICATE-----$/', trim($param), $matches)) { 531 list($wholething, $body) = $matches; 532 unset($wholething, $matches); 533 $b64 = clean_param($body, PARAM_BASE64); 534 if (!empty($b64)) { 535 return "-----BEGIN CERTIFICATE-----\n$b64\n-----END CERTIFICATE-----\n"; 536 } else { 537 return ''; 538 } 539 } 540 return ''; 541 542 case PARAM_BASE64: 543 if (!empty($param)) { 544 // PEM formatted strings may contain letters/numbers and the symbols 545 // forward slash: / 546 // plus sign: + 547 // equal sign: = 548 if (0 >= preg_match('/^([\s\w\/\+=]+)$/', trim($param))) { 549 return ''; 550 } 551 $lines = preg_split('/[\s]+/', $param, -1, PREG_SPLIT_NO_EMPTY); 552 // Each line of base64 encoded data must be 64 characters in 553 // length, except for the last line which may be less than (or 554 // equal to) 64 characters long. 555 for ($i=0, $j=count($lines); $i < $j; $i++) { 556 if ($i + 1 == $j) { 557 if (64 < strlen($lines[$i])) { 558 return ''; 559 } 560 continue; 561 } 562 563 if (64 != strlen($lines[$i])) { 564 return ''; 565 } 566 } 567 return implode("\n",$lines); 568 } else { 569 return ''; 570 } 571 572 case PARAM_TAG: 573 //as long as magic_quotes_gpc is used, a backslash will be a 574 //problem, so remove *all* backslash. 575 $param = str_replace('\\', '', $param); 576 //convert many whitespace chars into one 577 $param = preg_replace('/\s+/', ' ', $param); 578 $textlib = textlib_get_instance(); 579 $param = $textlib->substr(trim($param), 0, TAG_MAX_LENGTH); 580 return $param; 581 582 583 case PARAM_TAGLIST: 584 $tags = explode(',', $param); 585 $result = array(); 586 foreach ($tags as $tag) { 587 $res = clean_param($tag, PARAM_TAG); 588 if ($res != '') { 589 $result[] = $res; 590 } 591 } 592 if ($result) { 593 return implode(',', $result); 594 } else { 595 return ''; 596 } 597 598 default: // throw error, switched parameters in optional_param or another serious problem 599 error("Unknown parameter type: $type"); 600 } 601 } 602 603 /** 604 * This function is useful for testing whether something you got back from 605 * the HTML editor actually contains anything. Sometimes the HTML editor 606 * appear to be empty, but actually you get back a <br> tag or something. 607 * 608 * @param string $string a string containing HTML. 609 * @return boolean does the string contain any actual content - that is text, 610 * images, objcts, etc. 611 */ 612 function html_is_blank($string) { 613 return trim(strip_tags($string, '<img><object><applet><input><select><textarea><hr>')) == ''; 614 } 615 616 /** 617 * Set a key in global configuration 618 * 619 * Set a key/value pair in both this session's {@link $CFG} global variable 620 * and in the 'config' database table for future sessions. 621 * 622 * Can also be used to update keys for plugin-scoped configs in config_plugin table. 623 * In that case it doesn't affect $CFG. 624 * 625 * A NULL value will delete the entry. 626 * 627 * @param string $name the key to set 628 * @param string $value the value to set (without magic quotes) 629 * @param string $plugin (optional) the plugin scope 630 * @uses $CFG 631 * @return bool 632 */ 633 function set_config($name, $value, $plugin=NULL) { 634 /// No need for get_config because they are usually always available in $CFG 635 636 global $CFG; 637 638 if (empty($plugin)) { 639 if (!array_key_exists($name, $CFG->config_php_settings)) { 640 // So it's defined for this invocation at least 641 if (is_null($value)) { 642 unset($CFG->$name); 643 } else { 644 $CFG->$name = (string)$value; // settings from db are always strings 645 } 646 } 647 648 if (get_field('config', 'name', 'name', $name)) { 649 if ($value===null) { 650 return delete_records('config', 'name', $name); 651 } else { 652 return set_field('config', 'value', addslashes($value), 'name', $name); 653 } 654 } else { 655 if ($value===null) { 656 return true; 657 } 658 $config = new object(); 659 $config->name = $name; 660 $config->value = addslashes($value); 661 return insert_record('config', $config); 662 } 663 } else { // plugin scope 664 if ($id = get_field('config_plugins', 'id', 'name', $name, 'plugin', $plugin)) { 665 if ($value===null) { 666 return delete_records('config_plugins', 'name', $name, 'plugin', $plugin); 667 } else { 668 return set_field('config_plugins', 'value', addslashes($value), 'id', $id); 669 } 670 } else { 671 if ($value===null) { 672 return true; 673 } 674 $config = new object(); 675 $config->plugin = addslashes($plugin); 676 $config->name = $name; 677 $config->value = addslashes($value); 678 return insert_record('config_plugins', $config); 679 } 680 } 681 } 682 683 /** 684 * Get configuration values from the global config table 685 * or the config_plugins table. 686 * 687 * If called with no parameters it will do the right thing 688 * generating $CFG safely from the database without overwriting 689 * existing values. 690 * 691 * If called with 2 parameters it will return a $string single 692 * value or false of the value is not found. 693 * 694 * @param string $plugin 695 * @param string $name 696 * @uses $CFG 697 * @return hash-like object or single value 698 * 699 */ 700 function get_config($plugin=NULL, $name=NULL) { 701 702 global $CFG; 703 704 if (!empty($name)) { // the user is asking for a specific value 705 if (!empty($plugin)) { 706 return get_field('config_plugins', 'value', 'plugin' , $plugin, 'name', $name); 707 } else { 708 return get_field('config', 'value', 'name', $name); 709 } 710 } 711 712 // the user is after a recordset 713 if (!empty($plugin)) { 714 if ($configs=get_records('config_plugins', 'plugin', $plugin, '', 'name,value')) { 715 $configs = (array)$configs; 716 $localcfg = array(); 717 foreach ($configs as $config) { 718 $localcfg[$config->name] = $config->value; 719 } 720 return (object)$localcfg; 721 } else { 722 return false; 723 } 724 } else { 725 // this was originally in setup.php 726 if ($configs = get_records('config')) { 727 $localcfg = (array)$CFG; 728 foreach ($configs as $config) { 729 if (!isset($localcfg[$config->name])) { 730 $localcfg[$config->name] = $config->value; 731 } 732 // do not complain anymore if config.php overrides settings from db 733 } 734 735 $localcfg = (object)$localcfg; 736 return $localcfg; 737 } else { 738 // preserve $CFG if DB returns nothing or error 739 return $CFG; 740 } 741 742 } 743 } 744 745 /** 746 * Removes a key from global configuration 747 * 748 * @param string $name the key to set 749 * @param string $plugin (optional) the plugin scope 750 * @uses $CFG 751 * @return bool 752 */ 753 function unset_config($name, $plugin=NULL) { 754 755 global $CFG; 756 757 unset($CFG->$name); 758 759 if (empty($plugin)) { 760 return delete_records('config', 'name', $name); 761 } else { 762 return delete_records('config_plugins', 'name', $name, 'plugin', $plugin); 763 } 764 } 765 766 /** 767 * Get volatile flags 768 * 769 * @param string $type 770 * @param int $changedsince 771 * @return records array 772 * 773 */ 774 function get_cache_flags($type, $changedsince=NULL) { 775 776 $type = addslashes($type); 777 778 $sqlwhere = 'flagtype=\'' . $type . '\' AND expiry >= ' . time(); 779 if ($changedsince !== NULL) { 780 $changedsince = (int)$changedsince; 781 $sqlwhere .= ' AND timemodified > ' . $changedsince; 782 } 783 $cf = array(); 784 if ($flags=get_records_select('cache_flags', $sqlwhere, '', 'name,value')) { 785 foreach ($flags as $flag) { 786 $cf[$flag->name] = $flag->value; 787 } 788 } 789 return $cf; 790 } 791 792 /** 793 * Get volatile flags 794 * 795 * @param string $type 796 * @param string $name 797 * @param int $changedsince 798 * @return records array 799 * 800 */ 801 function get_cache_flag($type, $name, $changedsince=NULL) { 802 803 $type = addslashes($type); 804 $name = addslashes($name); 805 806 $sqlwhere = 'flagtype=\'' . $type . '\' AND name=\'' . $name . '\' AND expiry >= ' . time(); 807 if ($changedsince !== NULL) { 808 $changedsince = (int)$changedsince; 809 $sqlwhere .= ' AND timemodified > ' . $changedsince; 810 } 811 return get_field_select('cache_flags', 'value', $sqlwhere); 812 } 813 814 /** 815 * Set a volatile flag 816 * 817 * @param string $type the "type" namespace for the key 818 * @param string $name the key to set 819 * @param string $value the value to set (without magic quotes) - NULL will remove the flag 820 * @param int $expiry (optional) epoch indicating expiry - defaults to now()+ 24hs 821 * @return bool 822 */ 823 function set_cache_flag($type, $name, $value, $expiry=NULL) { 824 825 826 $timemodified = time(); 827 if ($expiry===NULL || $expiry < $timemodified) { 828 $expiry = $timemodified + 24 * 60 * 60; 829 } else { 830 $expiry = (int)$expiry; 831 } 832 833 if ($value === NULL) { 834 return unset_cache_flag($type,$name); 835 } 836 837 $type = addslashes($type); 838 $name = addslashes($name); 839 if ($f = get_record('cache_flags', 'name', $name, 'flagtype', $type)) { // this is a potentail problem in DEBUG_DEVELOPER 840 if ($f->value == $value and $f->expiry == $expiry and $f->timemodified == $timemodified) { 841 return true; //no need to update; helps rcache too 842 } 843 $f->value = addslashes($value); 844 $f->expiry = $expiry; 845 $f->timemodified = $timemodified; 846 return update_record('cache_flags', $f); 847 } else { 848 $f = new object(); 849 $f->flagtype = $type; 850 $f->name = $name; 851 $f->value = addslashes($value); 852 $f->expiry = $expiry; 853 $f->timemodified = $timemodified; 854 return (bool)insert_record('cache_flags', $f); 855 } 856 } 857 858 /** 859 * Removes a single volatile flag 860 * 861 * @param string $type the "type" namespace for the key 862 * @param string $name the key to set 863 * @uses $CFG 864 * @return bool 865 */ 866 function unset_cache_flag($type, $name) { 867 868 return delete_records('cache_flags', 869 'name', addslashes($name), 870 'flagtype', addslashes($type)); 871 } 872 873 /** 874 * Garbage-collect volatile flags 875 * 876 */ 877 function gc_cache_flags() { 878 return delete_records_select('cache_flags', 'expiry < ' . time()); 879 } 880 881 /** 882 * Refresh current $USER session global variable with all their current preferences. 883 * @uses $USER 884 */ 885 function reload_user_preferences() { 886 887 global $USER; 888 889 //reset preference 890 $USER->preference = array(); 891 892 if (!isloggedin() or isguestuser()) { 893 // no permanent storage for not-logged-in user and guest 894 895 } else if ($preferences = get_records('user_preferences', 'userid', $USER->id)) { 896 foreach ($preferences as $preference) { 897 $USER->preference[$preference->name] = $preference->value; 898 } 899 } 900 901 return true; 902 } 903 904 /** 905 * Sets a preference for the current user 906 * Optionally, can set a preference for a different user object 907 * @uses $USER 908 * @todo Add a better description and include usage examples. Add inline links to $USER and user functions in above line. 909 910 * @param string $name The key to set as preference for the specified user 911 * @param string $value The value to set forthe $name key in the specified user's record 912 * @param int $otheruserid A moodle user ID 913 * @return bool 914 */ 915 function set_user_preference($name, $value, $otheruserid=NULL) { 916 917 global $USER; 918 919 if (!isset($USER->preference)) { 920 reload_user_preferences(); 921 } 922 923 if (empty($name)) { 924 return false; 925 } 926 927 $nostore = false; 928 929 if (empty($otheruserid)){ 930 if (!isloggedin() or isguestuser()) { 931 $nostore = true; 932 } 933 $userid = $USER->id; 934 } else { 935 if (isguestuser($otheruserid)) { 936 $nostore = true; 937 } 938 $userid = $otheruserid; 939 } 940 941 $return = true; 942 if ($nostore) { 943 // no permanent storage for not-logged-in user and guest 944 945 } else if ($preference = get_record('user_preferences', 'userid', $userid, 'name', addslashes($name))) { 946 if ($preference->value === $value) { 947 return true; 948 } 949 if (!set_field('user_preferences', 'value', addslashes((string)$value), 'id', $preference->id)) { 950 $return = false; 951 } 952 953 } else { 954 $preference = new object(); 955 $preference->userid = $userid; 956 $preference->name = addslashes($name); 957 $preference->value = addslashes((string)$value); 958 if (!insert_record('user_preferences', $preference)) { 959 $return = false; 960 } 961 } 962 963 // update value in USER session if needed 964 if ($userid == $USER->id) { 965 $USER->preference[$name] = (string)$value; 966 } 967 968 return $return; 969 } 970 971 /** 972 * Unsets a preference completely by deleting it from the database 973 * Optionally, can set a preference for a different user id 974 * @uses $USER 975 * @param string $name The key to unset as preference for the specified user 976 * @param int $otheruserid A moodle user ID 977 */ 978 function unset_user_preference($name, $otheruserid=NULL) { 979 980 global $USER; 981 982 if (!isset($USER->preference)) { 983 reload_user_preferences(); 984 } 985 986 if (empty($otheruserid)){ 987 $userid = $USER->id; 988 } else { 989 $userid = $otheruserid; 990 } 991 992 //Delete the preference from $USER if needed 993 if ($userid == $USER->id) { 994 unset($USER->preference[$name]); 995 } 996 997 //Then from DB 998 return delete_records('user_preferences', 'userid', $userid, 'name', addslashes($name)); 999 } 1000 1001 1002 /** 1003 * Sets a whole array of preferences for the current user 1004 * @param array $prefarray An array of key/value pairs to be set 1005 * @param int $otheruserid A moodle user ID 1006 * @return bool 1007 */ 1008 function set_user_preferences($prefarray, $otheruserid=NULL) { 1009 1010 if (!is_array($prefarray) or empty($prefarray)) { 1011 return false; 1012 } 1013 1014 $return = true; 1015 foreach ($prefarray as $name => $value) { 1016 // The order is important; test for return is done first 1017 $return = (set_user_preference($name, $value, $otheruserid) && $return); 1018 } 1019 return $return; 1020 } 1021 1022 /** 1023 * If no arguments are supplied this function will return 1024 * all of the current user preferences as an array. 1025 * If a name is specified then this function 1026 * attempts to return that particular preference value. If 1027 * none is found, then the optional value $default is returned, 1028 * otherwise NULL. 1029 * @param string $name Name of the key to use in finding a preference value 1030 * @param string $default Value to be returned if the $name key is not set in the user preferences 1031 * @param int $otheruserid A moodle user ID 1032 * @uses $USER 1033 * @return string 1034 */ 1035 function get_user_preferences($name=NULL, $default=NULL, $otheruserid=NULL) { 1036 global $USER; 1037 1038 if (!isset($USER->preference)) { 1039 reload_user_preferences(); 1040 } 1041 1042 if (empty($otheruserid)){ 1043 $userid = $USER->id; 1044 } else { 1045 $userid = $otheruserid; 1046 } 1047 1048 if ($userid == $USER->id) { 1049 $preference = $USER->preference; 1050 1051 } else { 1052 $preference = array(); 1053 if ($prefdata = get_records('user_preferences', 'userid', $userid)) { 1054 foreach ($prefdata as $pref) { 1055 $preference[$pref->name] = $pref->value; 1056 } 1057 } 1058 } 1059 1060 if (empty($name)) { 1061 return $preference; // All values 1062 1063 } else if (array_key_exists($name, $preference)) { 1064 return $preference[$name]; // The single value 1065 1066 } else { 1067 return $default; // Default value (or NULL) 1068 } 1069 } 1070 1071 1072 /// FUNCTIONS FOR HANDLING TIME //////////////////////////////////////////// 1073 1074 /** 1075 * Given date parts in user time produce a GMT timestamp. 1076 * 1077 * @param int $year The year part to create timestamp of 1078 * @param int $month The month part to create timestamp of 1079 * @param int $day The day part to create timestamp of 1080 * @param int $hour The hour part to create timestamp of 1081 * @param int $minute The minute part to create timestamp of 1082 * @param int $second The second part to create timestamp of 1083 * @param float $timezone ? 1084 * @param bool $applydst ? 1085 * @return int timestamp 1086 * @todo Finish documenting this function 1087 */ 1088 function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0, $timezone=99, $applydst=true) { 1089 1090 $strtimezone = NULL; 1091 if (!is_numeric($timezone)) { 1092 $strtimezone = $timezone; 1093 } 1094 1095 $timezone = get_user_timezone_offset($timezone); 1096 1097 if (abs($timezone) > 13) { 1098 $time = mktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year); 1099 } else { 1100 $time = gmmktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year); 1101 $time = usertime($time, $timezone); 1102 if($applydst) { 1103 $time -= dst_offset_on($time, $strtimezone); 1104 } 1105 } 1106 1107 return $time; 1108 1109 } 1110 1111 /** 1112 * Given an amount of time in seconds, returns string 1113 * formatted nicely as weeks, days, hours etc as needed 1114 * 1115 * @uses MINSECS 1116 * @uses HOURSECS 1117 * @uses DAYSECS 1118 * @uses YEARSECS 1119 * @param int $totalsecs ? 1120 * @param array $str ? 1121 * @return string 1122 */ 1123 function format_time($totalsecs, $str=NULL) { 1124 1125 $totalsecs = abs($totalsecs); 1126 1127 if (!$str) { // Create the str structure the slow way 1128 $str->day = get_string('day'); 1129 $str->days = get_string('days'); 1130 $str->hour = get_string('hour'); 1131 $str->hours = get_string('hours'); 1132 $str->min = get_string('min'); 1133 $str->mins = get_string('mins'); 1134 $str->sec = get_string('sec'); 1135 $str->secs = get_string('secs'); 1136 $str->year = get_string('year'); 1137 $str->years = get_string('years'); 1138 } 1139 1140 1141 $years = floor($totalsecs/YEARSECS); 1142 $remainder = $totalsecs - ($years*YEARSECS); 1143 $days = floor($remainder/DAYSECS); 1144 $remainder = $totalsecs - ($days*DAYSECS); 1145 $hours = floor($remainder/HOURSECS); 1146 $remainder = $remainder - ($hours*HOURSECS); 1147 $mins = floor($remainder/MINSECS); 1148 $secs = $remainder - ($mins*MINSECS); 1149 1150 $ss = ($secs == 1) ? $str->sec : $str->secs; 1151 $sm = ($mins == 1) ? $str->min : $str->mins; 1152 $sh = ($hours == 1) ? $str->hour : $str->hours; 1153 $sd = ($days == 1) ? $str->day : $str->days; 1154 $sy = ($years == 1) ? $str->year : $str->years; 1155 1156 $oyears = ''; 1157 $odays = ''; 1158 $ohours = ''; 1159 $omins = ''; 1160 $osecs = ''; 1161 1162 if ($years) $oyears = $years .' '. $sy; 1163 if ($days) $odays = $days .' '. $sd; 1164 if ($hours) $ohours = $hours .' '. $sh; 1165 if ($mins) $omins = $mins .' '. $sm; 1166 if ($secs) $osecs = $secs .' '. $ss; 1167 1168 if ($years) return trim($oyears .' '. $odays); 1169 if ($days) return trim($odays .' '. $ohours); 1170 if ($hours) return trim($ohours .' '. $omins); 1171 if ($mins) return trim($omins .' '. $osecs); 1172 if ($secs) return $osecs; 1173 return get_string('now'); 1174 } 1175 1176 /** 1177 * Returns a formatted string that represents a date in user time 1178 * <b>WARNING: note that the format is for strftime(), not date().</b> 1179 * Because of a bug in most Windows time libraries, we can't use 1180 * the nicer %e, so we have to use %d which has leading zeroes. 1181 * A lot of the fuss in the function is just getting rid of these leading 1182 * zeroes as efficiently as possible. 1183 * 1184 * If parameter fixday = true (default), then take off leading 1185 * zero from %d, else mantain it. 1186 * 1187 * @uses HOURSECS 1188 * @param int $date timestamp in GMT 1189 * @param string $format strftime format 1190 * @param float $timezone 1191 * @param bool $fixday If true (default) then the leading 1192 * zero from %d is removed. If false then the leading zero is mantained. 1193 * @return string 1194 */ 1195 function userdate($date, $format='', $timezone=99, $fixday = true) { 1196 1197 global $CFG; 1198 1199 $strtimezone = NULL; 1200 if (!is_numeric($timezone)) { 1201 $strtimezone = $timezone; 1202 } 1203 1204 if (empty($format)) { 1205 $format = get_string('strftimedaydatetime'); 1206 } 1207 1208 if (!empty($CFG->nofixday)) { // Config.php can force %d not to be fixed. 1209 $fixday = false; 1210 } else if ($fixday) { 1211 $formatnoday = str_replace('%d', 'DD', $format); 1212 $fixday = ($formatnoday != $format); 1213 } 1214 1215 $date += dst_offset_on($date, $strtimezone); 1216 1217 $timezone = get_user_timezone_offset($timezone); 1218 1219 if (abs($timezone) > 13) { /// Server time 1220 if ($fixday) { 1221 $datestring = strftime($formatnoday, $date); 1222 $daystring = str_replace(' 0', '', strftime(' %d', $date)); 1223 $datestring = str_replace('DD', $daystring, $datestring); 1224 } else { 1225 $datestring = strftime($format, $date); 1226 } 1227 } else { 1228 $date += (int)($timezone * 3600); 1229 if ($fixday) { 1230 $datestring = gmstrftime($formatnoday, $date); 1231 $daystring = str_replace(' 0', '', gmstrftime(' %d', $date)); 1232 $datestring = str_replace('DD', $daystring, $datestring); 1233 } else { 1234 $datestring = gmstrftime($format, $date); 1235 } 1236 } 1237 1238 /// If we are running under Windows convert from windows encoding to UTF-8 1239 /// (because it's impossible to specify UTF-8 to fetch locale info in Win32) 1240 1241 if ($CFG->ostype == 'WINDOWS') { 1242 if ($localewincharset = get_string('localewincharset')) { 1243 $textlib = textlib_get_instance(); 1244 $datestring = $textlib->convert($datestring, $localewincharset, 'utf-8'); 1245 } 1246 } 1247 1248 return $datestring; 1249 } 1250 1251 /** 1252 * Given a $time timestamp in GMT (seconds since epoch), 1253 * returns an array that represents the date in user time 1254 * 1255 * @uses HOURSECS 1256 * @param int $time Timestamp in GMT 1257 * @param float $timezone ? 1258 * @return array An array that represents the date in user time 1259 * @todo Finish documenting this function 1260 */ 1261 function usergetdate($time, $timezone=99) { 1262 1263 $strtimezone = NULL; 1264 if (!is_numeric($timezone)) { 1265 $strtimezone = $timezone; 1266 } 1267 1268 $timezone = get_user_timezone_offset($timezone); 1269 1270 if (abs($timezone) > 13) { // Server time 1271 return getdate($time); 1272 } 1273 1274 // There is no gmgetdate so we use gmdate instead 1275 $time += dst_offset_on($time, $strtimezone); 1276 $time += intval((float)$timezone * HOURSECS); 1277 1278 $datestring = gmstrftime('%S_%M_%H_%d_%m_%Y_%w_%j_%A_%B', $time); 1279 1280 list( 1281 $getdate['seconds'], 1282 $getdate['minutes'], 1283 $getdate['hours'], 1284 $getdate['mday'], 1285 $getdate['mon'], 1286 $getdate['year'], 1287 $getdate['wday'], 1288 $getdate['yday'], 1289 $getdate['weekday'], 1290 $getdate['month'] 1291 ) = explode('_', $datestring); 1292 1293 return $getdate; 1294 } 1295 1296 /** 1297 * Given a GMT timestamp (seconds since epoch), offsets it by 1298 * the timezone. eg 3pm in India is 3pm GMT - 7 * 3600 seconds 1299 * 1300 * @uses HOURSECS 1301 * @param int $date Timestamp in GMT 1302 * @param float $timezone 1303 * @return int 1304 */ 1305 function usertime($date, $timezone=99) { 1306 1307 $timezone = get_user_timezone_offset($timezone); 1308 1309 if (abs($timezone) > 13) { 1310 return $date; 1311 } 1312 return $date - (int)($timezone * HOURSECS); 1313 } 1314 1315 /** 1316 * Given a time, return the GMT timestamp of the most recent midnight 1317 * for the current user. 1318 * 1319 * @param int $date Timestamp in GMT 1320 * @param float $timezone ? 1321 * @return ? 1322 */ 1323 function usergetmidnight($date, $timezone=99) { 1324 1325 $userdate = usergetdate($date, $timezone); 1326 1327 // Time of midnight of this user's day, in GMT 1328 return make_timestamp($userdate['year'], $userdate['mon'], $userdate['mday'], 0, 0, 0, $timezone); 1329 1330 } 1331 1332 /** 1333 * Returns a string that prints the user's timezone 1334 * 1335 * @param float $timezone The user's timezone 1336 * @return string 1337 */ 1338 function usertimezone($timezone=99) { 1339 1340 $tz = get_user_timezone($timezone); 1341 1342 if (!is_float($tz)) { 1343 return $tz; 1344 } 1345 1346 if(abs($tz) > 13) { // Server time 1347 return get_string('serverlocaltime'); 1348 } 1349 1350 if($tz == intval($tz)) { 1351 // Don't show .0 for whole hours 1352 $tz = intval($tz); 1353 } 1354 1355 if($tz == 0) { 1356 return 'UTC'; 1357 } 1358 else if($tz > 0) { 1359 return 'UTC+'.$tz; 1360 } 1361 else { 1362 return 'UTC'.$tz; 1363 } 1364 1365 } 1366 1367 /** 1368 * Returns a float which represents the user's timezone difference from GMT in hours 1369 * Checks various settings and picks the most dominant of those which have a value 1370 * 1371 * @uses $CFG 1372 * @uses $USER 1373 * @param float $tz If this value is provided and not equal to 99, it will be returned as is and no other settings will be checked 1374 * @return int 1375 */ 1376 function get_user_timezone_offset($tz = 99) { 1377 1378 global $USER, $CFG; 1379 1380 $tz = get_user_timezone($tz); 1381 1382 if (is_float($tz)) { 1383 return $tz; 1384 } else { 1385 $tzrecord = get_timezone_record($tz); 1386 if (empty($tzrecord)) { 1387 return 99.0; 1388 } 1389 return (float)$tzrecord->gmtoff / HOURMINS; 1390 } 1391 } 1392 1393 /** 1394 * Returns an int which represents the systems's timezone difference from GMT in seconds 1395 * @param mixed $tz timezone 1396 * @return int if found, false is timezone 99 or error 1397 */ 1398 function get_timezone_offset($tz) { 1399 global $CFG; 1400 1401 if ($tz == 99) { 1402 return false; 1403 } 1404 1405 if (is_numeric($tz)) { 1406 return intval($tz * 60*60); 1407 } 1408 1409 if (!$tzrecord = get_timezone_record($tz)) { 1410 return false; 1411 } 1412 return intval($tzrecord->gmtoff * 60); 1413 } 1414 1415 /** 1416 * Returns a float or a string which denotes the user's timezone 1417 * A float value means that a simple offset from GMT is used, while a string (it will be the name of a timezone in the database) 1418 * means that for this timezone there are also DST rules to be taken into account 1419 * Checks various settings and picks the most dominant of those which have a value 1420 * 1421 * @uses $USER 1422 * @uses $CFG 1423 * @param float $tz If this value is provided and not equal to 99, it will be returned as is and no other settings will be checked 1424 * @return mixed 1425 */ 1426 function get_user_timezone($tz = 99) { 1427 global $USER, $CFG; 1428 1429 $timezones = array( 1430 $tz, 1431 isset($CFG->forcetimezone) ? $CFG->forcetimezone : 99, 1432 isset($USER->timezone) ? $USER->timezone : 99, 1433 isset($CFG->timezone) ? $CFG->timezone : 99, 1434 ); 1435 1436 $tz = 99; 1437 1438 while(($tz == '' || $tz == 99 || $tz == NULL) && $next = each($timezones)) { 1439 $tz = $next['value']; 1440 } 1441 1442 return is_numeric($tz) ? (float) $tz : $tz; 1443 } 1444 1445 /** 1446 * ? 1447 * 1448 * @uses $CFG 1449 * @uses $db 1450 * @param string $timezonename ? 1451 * @return object 1452 */ 1453 function get_timezone_record($timezonename) { 1454 global $CFG, $db; 1455 static $cache = NULL; 1456 1457 if ($cache === NULL) { 1458 $cache = array(); 1459 } 1460 1461 if (isset($cache[$timezonename])) { 1462 return $cache[$timezonename]; 1463 } 1464 1465 return $cache[$timezonename] = get_record_sql('SELECT * FROM '.$CFG->prefix.'timezone 1466 WHERE name = '.$db->qstr($timezonename).' ORDER BY year DESC', true); 1467 } 1468 1469 /** 1470 * ? 1471 * 1472 * @uses $CFG 1473 * @uses $USER 1474 * @param ? $fromyear ? 1475 * @param ? $to_year ? 1476 * @return bool 1477 */ 1478 function calculate_user_dst_table($from_year = NULL, $to_year = NULL, $strtimezone = NULL) { 1479 global $CFG, $SESSION; 1480 1481 $usertz = get_user_timezone($strtimezone); 1482 1483 if (is_float($usertz)) { 1484 // Trivial timezone, no DST 1485 return false; 1486 } 1487 1488 if (!empty($SESSION->dst_offsettz) && $SESSION->dst_offsettz != $usertz) { 1489 // We have precalculated values, but the user's effective TZ has changed in the meantime, so reset 1490 unset($SESSION->dst_offsets); 1491 unset($SESSION->dst_range); 1492 } 1493 1494 if (!empty($SESSION->dst_offsets) && empty($from_year) && empty($to_year)) { 1495 // Repeat calls which do not request specific year ranges stop here, we have already calculated the table 1496 // This will be the return path most of the time, pretty light computationally 1497 return true; 1498 } 1499 1500 // Reaching here means we either need to extend our table or create it from scratch 1501 1502 // Remember which TZ we calculated these changes for 1503 $SESSION->dst_offsettz = $usertz; 1504 1505 if(empty($SESSION->dst_offsets)) { 1506 // If we 're creating from scratch, put the two guard elements in there 1507 $SESSION->dst_offsets = array(1 => NULL, 0 => NULL); 1508 } 1509 if(empty($SESSION->dst_range)) { 1510 // If creating from scratch 1511 $from = max((empty($from_year) ? intval(date('Y')) - 3 : $from_year), 1971); 1512 $to = min((empty($to_year) ? intval(date('Y')) + 3 : $to_year), 2035); 1513 1514 // Fill in the array with the extra years we need to process 1515 $yearstoprocess = array(); 1516 for($i = $from; $i <= $to; ++$i) { 1517 $yearstoprocess[] = $i; 1518 } 1519 1520 // Take note of which years we have processed for future calls 1521 $SESSION->dst_range = array($from, $to); 1522 } 1523 else { 1524 // If needing to extend the table, do the same 1525 $yearstoprocess = array(); 1526 1527 $from = max((empty($from_year) ? $SESSION->dst_range[0] : $from_year), 1971); 1528 $to = min((empty($to_year) ? $SESSION->dst_range[1] : $to_year), 2035); 1529 1530 if($from < $SESSION->dst_range[0]) { 1531 // Take note of which years we need to process and then note that we have processed them for future calls 1532 for($i = $from; $i < $SESSION->dst_range[0]; ++$i) { 1533 $yearstoprocess[] = $i; 1534 } 1535 $SESSION->dst_range[0] = $from; 1536 } 1537 if($to > $SESSION->dst_range[1]) { 1538 // Take note of which years we need to process and then note that we have processed them for future calls 1539 for($i = $SESSION->dst_range[1] + 1; $i <= $to; ++$i) { 1540 $yearstoprocess[] = $i; 1541 } 1542 $SESSION->dst_range[1] = $to; 1543 } 1544 } 1545 1546 if(empty($yearstoprocess)) { 1547 // This means that there was a call requesting a SMALLER range than we have already calculated 1548 return true; 1549 } 1550 1551 // From now on, we know that the array has at least the two guard elements, and $yearstoprocess has the years we need 1552 // Also, the array is sorted in descending timestamp order! 1553 1554 // Get DB data 1555 1556 static $presets_cache = array(); 1557 if (!isset($presets_cache[$usertz])) { 1558 $presets_cache[$usertz] = get_records('timezone', 'name', $usertz, 'year DESC', 'year, gmtoff, dstoff, dst_month, dst_startday, dst_weekday, dst_skipweeks, dst_time, std_month, std_startday, std_weekday, std_skipweeks, std_time'); 1559 } 1560 if(empty($presets_cache[$usertz])) { 1561 return false; 1562 } 1563 1564 // Remove ending guard (first element of the array) 1565 reset($SESSION->dst_offsets); 1566 unset($SESSION->dst_offsets[key($SESSION->dst_offsets)]); 1567 1568 // Add all required change timestamps 1569 foreach($yearstoprocess as $y) { 1570 // Find the record which is in effect for the year $y 1571 foreach($presets_cache[$usertz] as $year => $preset) { 1572 if($year <= $y) { 1573 break; 1574 } 1575 } 1576 1577 $changes = dst_changes_for_year($y, $preset); 1578 1579 if($changes === NULL) { 1580 continue; 1581 } 1582 if($changes['dst'] != 0) { 1583 $SESSION->dst_offsets[$changes['dst']] = $preset->dstoff * MINSECS; 1584 } 1585 if($changes['std'] != 0) { 1586 $SESSION->dst_offsets[$changes['std']] = 0; 1587 } 1588 } 1589 1590 // Put in a guard element at the top 1591 $maxtimestamp = max(array_keys($SESSION->dst_offsets)); 1592 $SESSION->dst_offsets[($maxtimestamp + DAYSECS)] = NULL; // DAYSECS is arbitrary, any "small" number will do 1593 1594 // Sort again 1595 krsort($SESSION->dst_offsets); 1596 1597 return true; 1598 } 1599 1600 function dst_changes_for_year($year, $timezone) { 1601 1602 if($timezone->dst_startday == 0 && $timezone->dst_weekday == 0 && $timezone->std_startday == 0 && $timezone->std_weekday == 0) { 1603 return NULL; 1604 } 1605 1606 $monthdaydst = find_day_in_month($timezone->dst_startday, $timezone->dst_weekday, $timezone->dst_month, $year); 1607 $monthdaystd = find_day_in_month($timezone->std_startday, $timezone->std_weekday, $timezone->std_month, $year); 1608 1609 list($dst_hour, $dst_min) = explode(':', $timezone->dst_time); 1610 list($std_hour, $std_min) = explode(':', $timezone->std_time); 1611 1612 $timedst = make_timestamp($year, $timezone->dst_month, $monthdaydst, 0, 0, 0, 99, false); 1613 $timestd = make_timestamp($year, $timezone->std_month, $monthdaystd, 0, 0, 0, 99, false); 1614 1615 // Instead of putting hour and minute in make_timestamp(), we add them afterwards. 1616 // This has the advantage of being able to have negative values for hour, i.e. for timezones 1617 // where GMT time would be in the PREVIOUS day than the local one on which DST changes. 1618 1619 $timedst += $dst_hour * HOURSECS + $dst_min * MINSECS; 1620 $timestd += $std_hour * HOURSECS + $std_min * MINSECS; 1621 1622 return array('dst' => $timedst, 0 => $timedst, 'std' => $timestd, 1 => $timestd); 1623 } 1624 1625 // $time must NOT be compensated at all, it has to be a pure timestamp 1626 function dst_offset_on($time, $strtimezone = NULL) { 1627 global $SESSION; 1628 1629 if(!calculate_user_dst_table(NULL, NULL, $strtimezone) || empty($SESSION->dst_offsets)) { 1630 return 0; 1631 } 1632 1633 reset($SESSION->dst_offsets); 1634 while(list($from, $offset) = each($SESSION->dst_offsets)) { 1635 if($from <= $time) { 1636 break; 1637 } 1638 } 1639 1640 // This is the normal return path 1641 if($offset !== NULL) { 1642 return $offset; 1643 } 1644 1645 // Reaching this point means we haven't calculated far enough, do it now: 1646 // Calculate extra DST changes if needed and recurse. The recursion always 1647 // moves toward the stopping condition, so will always end. 1648 1649 if($from == 0) { 1650 // We need a year smaller than $SESSION->dst_range[0] 1651 if($SESSION->dst_range[0] == 1971) { 1652 return 0; 1653 } 1654 calculate_user_dst_table($SESSION->dst_range[0] - 5, NULL, $strtimezone); 1655 return dst_offset_on($time, $strtimezone); 1656 } 1657 else { 1658 // We need a year larger than $SESSION->dst_range[1] 1659 if($SESSION->dst_range[1] == 2035) { 1660 return 0; 1661 } 1662 calculate_user_dst_table(NULL, $SESSION->dst_range[1] + 5, $strtimezone); 1663 return dst_offset_on($time, $strtimezone); 1664 } 1665 } 1666 1667 function find_day_in_month($startday, $weekday, $month, $year) { 1668 1669 $daysinmonth = days_in_month($month, $year); 1670 1671 if($weekday == -1) { 1672 // Don't care about weekday, so return: 1673 // abs($startday) if $startday != -1 1674 // $daysinmonth otherwise 1675 return ($startday == -1) ? $daysinmonth : abs($startday); 1676 } 1677 1678 // From now on we 're looking for a specific weekday 1679 1680 // Give "end of month" its actual value, since we know it 1681 if($startday == -1) { 1682 $startday = -1 * $daysinmonth; 1683 } 1684 1685 // Starting from day $startday, the sign is the direction 1686 1687 if($startday < 1) { 1688 1689 $startday = abs($startday); 1690 $lastmonthweekday = strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0)); 1691 1692 // This is the last such weekday of the month 1693 $lastinmonth = $daysinmonth + $weekday - $lastmonthweekday; 1694 if($lastinmonth > $daysinmonth) { 1695 $lastinmonth -= 7; 1696 } 1697 1698 // Find the first such weekday <= $startday 1699 while($lastinmonth > $startday) { 1700 $lastinmonth -= 7; 1701 } 1702 1703 return $lastinmonth; 1704 1705 } 1706 else { 1707 1708 $indexweekday = strftime('%w', mktime(12, 0, 0, $month, $startday, $year, 0)); 1709 1710 $diff = $weekday - $indexweekday; 1711 if($diff < 0) { 1712 $diff += 7; 1713 } 1714 1715 // This is the first such weekday of the month equal to or after $startday 1716 $firstfromindex = $startday + $diff; 1717 1718 return $firstfromindex; 1719 1720 } 1721 } 1722 1723 /** 1724 * Calculate the number of days in a given month 1725 * 1726 * @param int $month The month whose day count is sought 1727 * @param int $year The year of the month whose day count is sought 1728 * @return int 1729 */ 1730 function days_in_month($month, $year) { 1731 return intval(date('t', mktime(12, 0, 0, $month, 1, $year, 0))); 1732 } 1733 1734 /** 1735 * Calculate the position in the week of a specific calendar day 1736 * 1737 * @param int $day The day of the date whose position in the week is sought 1738 * @param int $month The month of the date whose position in the week is sought 1739 * @param int $year The year of the date whose position in the week is sought 1740 * @return int 1741 */ 1742 function dayofweek($day, $month, $year) { 1743 // I wonder if this is any different from 1744 // strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0)); 1745 return intval(date('w', mktime(12, 0, 0, $month, $day, $year, 0))); 1746 } 1747 1748 /// USER AUTHENTICATION AND LOGIN //////////////////////////////////////// 1749 1750 /** 1751 * Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey 1752 * if one does not already exist, but does not overwrite existing sesskeys. Returns the 1753 * sesskey string if $USER exists, or boolean false if not. 1754 * 1755 * @uses $USER 1756 * @return string 1757 */ 1758 function sesskey() { 1759 global $USER; 1760 1761 if(!isset($USER)) { 1762 return false; 1763 } 1764 1765 if (empty($USER->sesskey)) { 1766 $USER->sesskey = random_string(10); 1767 } 1768 1769 return $USER->sesskey; 1770 } 1771 1772 1773 /** 1774 * For security purposes, this function will check that the currently 1775 * given sesskey (passed as a parameter to the script or this function) 1776 * matches that of the current user. 1777 * 1778 * @param string $sesskey optionally provided sesskey 1779 * @return bool 1780 */ 1781 function confirm_sesskey($sesskey=NULL) { 1782 global $USER; 1783 1784 if (!empty($USER->ignoresesskey) || !empty($CFG->ignoresesskey)) { 1785 return true; 1786 } 1787 1788 if (empty($sesskey)) { 1789 $sesskey = required_param('sesskey', PARAM_RAW); // Check script parameters 1790 } 1791 1792 if (!isset($USER->sesskey)) { 1793 return false; 1794 } 1795 1796 return ($USER->sesskey === $sesskey); 1797 } 1798 1799 /** 1800 * Setup all global $CFG course variables, set locale and also themes 1801 * This function can be used on pages that do not require login instead of require_login() 1802 * 1803 * @param mixed $courseorid id of the course or course object 1804 */ 1805 function course_setup($courseorid=0) { 1806 global $COURSE, $CFG, $SITE; 1807 1808 /// Redefine global $COURSE if needed 1809 if (empty($courseorid)) { 1810 // no change in global $COURSE - for backwards compatibiltiy 1811 // if require_rogin() used after require_login($courseid); 1812 } else if (is_object($courseorid)) { 1813 $COURSE = clone($courseorid); 1814 } else { 1815 global $course; // used here only to prevent repeated fetching from DB - may be removed later 1816 if ($courseorid == SITEID) { 1817 $COURSE = clone($SITE); 1818 } else if (!empty($course->id) and $course->id == $courseorid) { 1819 $COURSE = clone($course); 1820 } else { 1821 if (!$COURSE = get_record('course', 'id', $courseorid)) { 1822 error('Invalid course ID'); 1823 } 1824 } 1825 } 1826 1827 /// set locale and themes 1828 moodle_setlocale(); 1829 theme_setup(); 1830 1831 } 1832 1833 /** 1834 * This function checks that the current user is logged in and has the 1835 * required privileges 1836 * 1837 * This function checks that the current user is logged in, and optionally 1838 * whether they are allowed to be in a particular course and view a particular 1839 * course module. 1840 * If they are not logged in, then it redirects them to the site login unless 1841 * $autologinguest is set and {@link $CFG}->autologinguests is set to 1 in which 1842 * case they are automatically logged in as guests. 1843 * If $courseid is given and the user is not enrolled in that course then the 1844 * user is redirected to the course enrolment page. 1845 * If $cm is given and the coursemodule is hidden and the user is not a teacher 1846 * in the course then the user is redirected to the course home page. 1847 * 1848 * @uses $CFG 1849 * @uses $SESSION 1850 * @uses $USER 1851 * @uses $FULLME 1852 * @uses SITEID 1853 * @uses $COURSE 1854 * @param mixed $courseorid id of the course or course object 1855 * @param bool $autologinguest 1856 * @param object $cm course module object 1857 * @param bool $setwantsurltome Define if we want to set $SESSION->wantsurl, defaults to 1858 * true. Used to avoid (=false) some scripts (file.php...) to set that variable, 1859 * in order to keep redirects working properly. MDL-14495 1860 */ 1861 function require_login($courseorid=0, $autologinguest=true, $cm=null, $setwantsurltome=true) { 1862 1863 global $CFG, $SESSION, $USER, $COURSE, $FULLME; 1864 1865 /// setup global $COURSE, themes, language and locale 1866 course_setup($courseorid); 1867 1868 /// If the user is not even logged in yet then make sure they are 1869 if (!isloggedin()) { 1870 //NOTE: $USER->site check was obsoleted by session test cookie, 1871 // $USER->confirmed test is in login/index.php 1872 if ($setwantsurltome) { 1873 $SESSION->wantsurl = $FULLME; 1874 } 1875 if (!empty($_SERVER['HTTP_REFERER'])) { 1876 $SESSION->fromurl = $_SERVER['HTTP_REFERER']; 1877 } 1878 if ($autologinguest and !empty($CFG->guestloginbutton) and !empty($CFG->autologinguests) and ($COURSE->id == SITEID or $COURSE->guest) ) { 1879 $loginguest = '?loginguest=true'; 1880 } else { 1881 $loginguest = ''; 1882 } 1883 if (empty($CFG->loginhttps) or $loginguest) { //do not require https for guest logins 1884 redirect($CFG->wwwroot .'/login/index.php'. $loginguest); 1885 } else { 1886 $wwwroot = str_replace('http:','https:', $CFG->wwwroot); 1887 redirect($wwwroot .'/login/index.php'); 1888 } 1889 exit; 1890 } 1891 1892 /// loginas as redirection if needed 1893 if ($COURSE->id != SITEID and !empty($USER->realuser)) { 1894 if ($USER->loginascontext->contextlevel == CONTEXT_COURSE) { 1895 if ($USER->loginascontext->instanceid != $COURSE->id) { 1896 print_error('loginasonecourse', '', $CFG->wwwroot.'/course/view.php?id='.$USER->loginascontext->instanceid); 1897 } 1898 } 1899 } 1900 1901 /// check whether the user should be changing password (but only if it is REALLY them) 1902 if (get_user_preferences('auth_forcepasswordchange') && empty($USER->realuser)) { 1903 $userauth = get_auth_plugin($USER->auth); 1904 if ($userauth->can_change_password()) { 1905 $SESSION->wantsurl = $FULLME; 1906 if ($changeurl = $userauth->change_password_url()) { 1907 //use plugin custom url 1908 redirect($changeurl); 1909 } else { 1910 //use moodle internal method 1911 if (empty($CFG->loginhttps)) { 1912 redirect($CFG->wwwroot .'/login/change_password.php'); 1913 } else { 1914 $wwwroot = str_replace('http:','https:', $CFG->wwwroot); 1915 redirect($wwwroot .'/login/change_password.php'); 1916 } 1917 } 1918 } else { 1919 print_error('nopasswordchangeforced', 'auth'); 1920 } 1921 } 1922 1923 /// Check that the user account is properly set up 1924 if (user_not_fully_set_up($USER)) { 1925 $SESSION->wantsurl = $FULLME; 1926 redirect($CFG->wwwroot .'/user/edit.php?id='. $USER->id .'&course='. SITEID); 1927 } 1928 1929 /// Make sure current IP matches the one for this session (if required) 1930 if (!empty($CFG->tracksessionip)) { 1931 if ($USER->sessionIP != md5(getremoteaddr())) { 1932 print_error('sessionipnomatch', 'error'); 1933 } 1934 } 1935 1936 /// Make sure the USER has a sesskey set up. Used for checking script parameters. 1937 sesskey(); 1938 1939 // Check that the user has agreed to a site policy if there is one 1940 if (!empty($CFG->sitepolicy)) { 1941 if (!$USER->policyagreed) { 1942 $SESSION->wantsurl = $FULLME; 1943 redirect($CFG->wwwroot .'/user/policy.php'); 1944 } 1945 } 1946 1947 // Fetch the system context, we are going to use it a lot. 1948 $sysctx = get_context_instance(CONTEXT_SYSTEM); 1949 1950 /// If the site is currently under maintenance, then print a message 1951 if (!has_capability('moodle/site:config', $sysctx)) { 1952 if (file_exists($CFG->dataroot.'/'.SITEID.'/maintenance.html')) { 1953 print_maintenance_message(); 1954 exit; 1955 } 1956 } 1957 1958 /// groupmembersonly access control 1959 if (!empty($CFG->enablegroupings) and $cm and $cm->groupmembersonly and !has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) { 1960 if (isguestuser() or !groups_has_membership($cm)) { 1961 print_error('groupmembersonlyerror', 'group', $CFG->wwwroot.'/course/view.php?id='.$cm->course); 1962 } 1963 } 1964 1965 // Fetch the course context, and prefetch its child contexts 1966 if (!isset($COURSE->context)) { 1967 if ( ! $COURSE->context = get_context_instance(CONTEXT_COURSE, $COURSE->id) ) { 1968 print_error('nocontext'); 1969 } 1970 } 1971 if ($COURSE->id == SITEID) { 1972 /// Eliminate hidden site activities straight away 1973 if (!empty($cm) && !$cm->visible 1974 && !has_capability('moodle/course:viewhiddenactivities', $COURSE->context)) { 1975 redirect($CFG->wwwroot, get_string('activityiscurrentlyhidden')); 1976 } 1977 user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times 1978 return; 1979 1980 } else { 1981 1982 /// Check if the user can be in a particular course 1983 if (empty($USER->access['rsw'][$COURSE->context->path])) { 1984 // 1985 // MDL-13900 - If the course or the parent category are hidden 1986 // and the user hasn't the 'course:viewhiddencourses' capability, prevent access 1987 // 1988 if ( !($COURSE->visible && course_parent_visible($COURSE)) && 1989 !has_capability('moodle/course:viewhiddencourses', $COURSE->context)) { 1990 print_header_simple(); 1991 notice(get_string('coursehidden'), $CFG->wwwroot .'/'); 1992 } 1993 } 1994 1995 /// Non-guests who don't currently have access, check if they can be allowed in as a guest 1996 1997 if ($USER->username != 'guest' and !has_capability('moodle/course:view', $COURSE->context)) { 1998 if ($COURSE->guest == 1) { 1999 // Temporarily assign them guest role for this context, if it fails later user is asked to enrol 2000 $USER->access = load_temp_role($COURSE->context, $CFG->guestroleid, $USER->access); 2001 } 2002 } 2003 2004 /// If the user is a guest then treat them according to the course policy about guests 2005 2006 if (has_capability('moodle/legacy:guest', $COURSE->context, NULL, false)) { 2007 if (has_capability('moodle/site:doanything', $sysctx)) { 2008 // administrators must be able to access any course - even if somebody gives them guest access 2009 user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times 2010 return; 2011 } 2012 2013 switch ($COURSE->guest) { /// Check course policy about guest access 2014 2015 case 1: /// Guests always allowed 2016 if (!has_capability('moodle/course:view', $COURSE->context)) { // Prohibited by capability 2017 print_header_simple(); 2018 notice(get_string('guestsnotallowed', '', format_string($COURSE->fullname)), "$CFG->wwwroot/login/index.php"); 2019 } 2020 if (!empty($cm) and !$cm->visible) { // Not allowed to see module, send to course page 2021 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, 2022 get_string('activityiscurrentlyhidden')); 2023 } 2024 2025 user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times 2026 return; // User is allowed to see this course 2027 2028 break; 2029 2030 case 2: /// Guests allowed with key 2031 if (!empty($USER->enrolkey[$COURSE->id])) { // Set by enrol/manual/enrol.php 2032 user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times 2033 return true; 2034 } 2035 // otherwise drop through to logic below (--> enrol.php) 2036 break; 2037 2038 default: /// Guests not allowed 2039 $strloggedinasguest = get_string('loggedinasguest'); 2040 print_header_simple('', '', 2041 build_navigation(array(array('name' => $strloggedinasguest, 'link' => null, 'type' => 'misc')))); 2042 if (empty($USER->access['rsw'][$COURSE->context->path])) { // Normal guest 2043 notice(get_string('guestsnotallowed', '', format_string($COURSE->fullname)), "$CFG->wwwroot/login/index.php"); 2044 } else { 2045 notify(get_string('guestsnotallowed', '', format_string($COURSE->fullname))); 2046 echo '<div class="notifyproblem">'.switchroles_form($COURSE->id).'</div>'; 2047 print_footer($COURSE); 2048 exit; 2049 } 2050 break; 2051 } 2052 2053 /// For non-guests, check if they have course view access 2054 2055 } else if (has_capability('moodle/course:view', $COURSE->context)) { 2056 if (!empty($USER->realuser)) { // Make sure the REAL person can also access this course 2057 if (!has_capability('moodle/course:view', $COURSE->context, $USER->realuser)) { 2058 print_header_simple(); 2059 notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/'); 2060 } 2061 } 2062 2063 /// Make sure they can read this activity too, if specified 2064 2065 if (!empty($cm) and !$cm->visible and !has_capability('moodle/course:viewhiddenactivities', $COURSE->context)) { 2066 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden')); 2067 } 2068 user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times 2069 return; // User is allowed to see this course 2070 2071 } 2072 2073 2074 /// Currently not enrolled in the course, so see if they want to enrol 2075 $SESSION->wantsurl = $FULLME; 2076 redirect($CFG->wwwroot .'/course/enrol.php?id='. $COURSE->id); 2077 die; 2078 } 2079 } 2080 2081 2082 2083 /** 2084 * This function just makes sure a user is logged out. 2085 * 2086 * @uses $CFG 2087 * @uses $USER 2088 */ 2089 function require_logout() { 2090 2091 global $USER, $CFG, $SESSION; 2092 2093 if (isloggedin()) { 2094 add_to_log(SITEID, "user", "logout", "view.php?id=$USER->id&course=".SITEID, $USER->id, 0, $USER->id); 2095 2096 $authsequence = get_enabled_auth_plugins(); // auths, in sequence 2097 foreach($authsequence as $authname) { 2098 $authplugin = get_auth_plugin($authname); 2099 $authplugin->prelogout_hook(); 2100 } 2101 } 2102 2103 if (ini_get_bool("register_globals") and check_php_version("4.3.0")) { 2104 // This method is just to try to avoid silly warnings from PHP 4.3.0 2105 session_unregister("USER"); 2106 session_unregister("SESSION"); 2107 } 2108 2109 // Initialize variable to pass-by-reference to headers_sent(&$file, &$line) 2110 $file = $line = null; 2111 if (headers_sent($file, $line)) { 2112 error_log('MoodleSessionTest cookie could not be set in moodlelib.php:'.__LINE__); 2113 error_log('Headers were already sent in file: '.$file.' on line '.$line); 2114 } else { 2115 if (check_php_version('5.2.0')) { 2116 setcookie('MoodleSessionTest'.$CFG->sessioncookie, '', time() - 3600, $CFG->sessioncookiepath, '', $CFG->cookiesecure, $CFG->cookiehttponly); 2117 } else { 2118 setcookie('MoodleSessionTest'.$CFG->sessioncookie, '', time() - 3600, $CFG->sessioncookiepath, '', $CFG->cookiesecure); 2119 } 2120 } 2121 2122 unset($_SESSION['USER']); 2123 unset($_SESSION['SESSION']); 2124 2125 unset($SESSION); 2126 unset($USER); 2127 2128 } 2129 2130 /** 2131 * This is a weaker version of {@link require_login()} which only requires login 2132 * when called from within a course rather than the site page, unless 2133 * the forcelogin option is turned on. 2134 * 2135 * @uses $CFG 2136 * @param mixed $courseorid The course object or id in question 2137 * @param bool $autologinguest Allow autologin guests if that is wanted 2138 * @param object $cm Course activity module if known 2139 * @param bool $setwantsurltome Define if we want to set $SESSION->wantsurl, defaults to 2140 * true. Used to avoid (=false) some scripts (file.php...) to set that variable, 2141 * in order to keep redirects working properly. MDL-14495 2142 */ 2143 function require_course_login($courseorid, $autologinguest=true, $cm=null, $setwantsurltome=true) { 2144 global $CFG; 2145 if (!empty($CFG->forcelogin)) { 2146 // login required for both SITE and courses 2147 require_login($courseorid, $autologinguest, $cm, $setwantsurltome); 2148 2149 } else if (!empty($cm) and !$cm->visible) { 2150 // always login for hidden activities 2151 require_login($courseorid, $autologinguest, $cm, $setwantsurltome); 2152 2153 } else if ((is_object($courseorid) and $courseorid->id == SITEID) 2154 or (!is_object($courseorid) and $courseorid == SITEID)) { 2155 //login for SITE not required 2156 user_accesstime_log(SITEID); 2157 return; 2158 2159 } else { 2160 // course login always required 2161 require_login($courseorid, $autologinguest, $cm, $setwantsurltome); 2162 } 2163 } 2164 2165 /** 2166 * Require key login. Function terminates with error if key not found or incorrect. 2167 * @param string $script unique script identifier 2168 * @param int $instance optional instance id 2169 */ 2170 function require_user_key_login($script, $instance=null) { 2171 global $nomoodlecookie, $USER, $SESSION, $CFG; 2172 2173 if (empty($nomoodlecookie)) { 2174 error('Incorrect use of require_key_login() - session cookies must be disabled!'); 2175 } 2176 2177 /// extra safety 2178 @session_write_close(); 2179 2180 $keyvalue = required_param('key', PARAM_ALPHANUM); 2181 2182 if (!$key = get_record('user_private_key', 'script', $script, 'value', $keyvalue, 'instance', $instance)) { 2183 error('Incorrect key'); 2184 } 2185 2186 if (!empty($key->validuntil) and $key->validuntil < time()) { 2187 error('Expired key'); 2188 } 2189 2190 if ($key->iprestriction) { 2191 $remoteaddr = getremoteaddr(); 2192 if ($remoteaddr == '' or !address_in_subnet($remoteaddr, $key->iprestriction)) { 2193 error('Client IP address mismatch'); 2194 } 2195 } 2196 2197 if (!$user = get_record('user', 'id', $key->userid)) { 2198 error('Incorrect user record'); 2199 } 2200 2201 /// emulate normal session 2202 $SESSION = new object(); 2203 $USER = $user; 2204 2205 /// note we are not using normal login 2206 if (!defined('USER_KEY_LOGIN')) { 2207 define('USER_KEY_LOGIN', true); 2208 } 2209 2210 load_all_capabilities(); 2211 2212 /// return isntance id - it might be empty 2213 return $key->instance; 2214 } 2215 2216 /** 2217 * Creates a new private user access key. 2218 * @param string $script unique target identifier 2219 * @param int $userid 2220 * @param instance $int optional instance id 2221 * @param string $iprestriction optional ip restricted access 2222 * @param timestamp $validuntil key valid only until given data 2223 * @return string access key value 2224 */ 2225 function create_user_key($script, $userid, $instance=null, $iprestriction=null, $validuntil=null) { 2226 $key = new object(); 2227 $key->script = $script; 2228 $key->userid = $userid; 2229 $key->instance = $instance; 2230 $key->iprestriction = $iprestriction; 2231 $key->validuntil = $validuntil; 2232 $key->timecreated = time(); 2233 2234 $key->value = md5($userid.'_'.time().random_string(40)); // something long and unique 2235 while (record_exists('user_private_key', 'value', $key->value)) { 2236 // must be unique 2237