3 // This file is part of Moodle - http://moodle.org/
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
21 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
22 * @copyright 2008, 2009 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') ||
die();
29 * Factory method returning moodle_session object.
30 * @return moodle_session
32 function session_get_instance() {
35 static $session = null;
37 if (is_null($session)) {
38 if (empty($CFG->sessiontimeout
)) {
39 $CFG->sessiontimeout
= 7200;
42 if (defined('SESSION_CUSTOM_CLASS')) {
43 // this is a hook for webservices, key based login, etc.
44 if (defined('SESSION_CUSTOM_FILE')) {
45 require_once($CFG->dirroot
.SESSION_CUSTOM_FILE
);
47 $session_class = SESSION_CUSTOM_CLASS
;
48 $session = new $session_class();
50 } else if ((!isset($CFG->dbsessions
) or $CFG->dbsessions
) and $DB->session_lock_supported()) {
51 // default recommended session type
52 $session = new database_session();
55 // legacy limited file based storage - some features and auth plugins will not work, sorry
56 $session = new legacy_file_session();
64 * Moodle session abstraction
68 * @copyright 2008 Petr Skoda {@link http://skodak.org}
69 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
71 interface moodle_session
{
73 * Terminate current session
76 public function terminate_current();
79 * No more changes in session expected.
80 * Unblocks the sessions, other scripts may start executing in parallel.
83 public function write_close();
86 * Check for existing session with id $sid
87 * @param unknown_type $sid
88 * @return boolean true if session found.
90 public function session_exists($sid);
94 * Class handling all session and cookies related stuff.
98 * @copyright 2009 Petr Skoda {@link http://skodak.org}
99 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
101 abstract class session_stub
implements moodle_session
{
102 protected $justloggedout;
104 public function __construct() {
107 if (NO_MOODLE_COOKIES
) {
108 // session not used at all
112 $_SESSION['SESSION'] = new stdClass();
113 $_SESSION['USER'] = new stdClass();
116 $this->prepare_cookies();
117 $this->init_session_storage();
119 $newsession = empty($_COOKIE['MoodleSession'.$CFG->sessioncookie
]);
121 if (!empty($CFG->usesid
) && $newsession) {
125 ini_set('session.use_trans_sid', '0');
128 session_name('MoodleSession'.$CFG->sessioncookie
);
129 session_set_cookie_params(0, $CFG->sessioncookiepath
, $CFG->sessioncookiedomain
, $CFG->cookiesecure
, $CFG->cookiehttponly
);
131 if (!isset($_SESSION['SESSION'])) {
132 $_SESSION['SESSION'] = new stdClass();
133 if (!$newsession and !$this->justloggedout
) {
134 $_SESSION['SESSION']->has_timed_out
= true;
137 if (!isset($_SESSION['USER'])) {
138 $_SESSION['USER'] = new stdClass();
142 $this->check_user_initialised();
144 $this->check_security();
148 * Terminates active moodle session
150 public function terminate_current() {
151 global $CFG, $SESSION, $USER, $DB;
154 $DB->delete_records('external_tokens', array('sid'=>session_id(), 'tokentype'=>EXTERNAL_TOKEN_EMBEDDED
));
155 } catch (Exception
$ignored) {
156 // probably install/upgrade - ignore this problem
159 if (NO_MOODLE_COOKIES
) {
163 // Initialize variable to pass-by-reference to headers_sent(&$file, &$line)
165 $_SESSION['SESSION'] = new stdClass();
166 $_SESSION['USER'] = new stdClass();
167 $_SESSION['USER']->id
= 0;
168 if (isset($CFG->mnet_localhost_id
)) {
169 $_SESSION['USER']->mnethostid
= $CFG->mnet_localhost_id
;
171 $SESSION = $_SESSION['SESSION']; // this may not work properly
172 $USER = $_SESSION['USER']; // this may not work properly
176 if (headers_sent($file, $line)) {
177 error_log('Can not terminate session properly - headers were already sent in file: '.$file.' on line '.$line);
180 // now let's try to get a new session id and delete the old one
181 $this->justloggedout
= true;
182 session_regenerate_id(true);
183 $this->justloggedout
= false;
185 // write the new session
186 session_write_close();
190 * No more changes in session expected.
191 * Unblocks the sessions, other scripts may start executing in parallel.
194 public function write_close() {
195 if (NO_MOODLE_COOKIES
) {
199 session_write_close();
203 * Initialise $USER object, handles google access
204 * and sets up not logged in user properly.
208 protected function check_user_initialised() {
211 if (isset($_SESSION['USER']->id
)) {
212 // already set up $USER
218 if (!empty($CFG->opentogoogle
) and !NO_MOODLE_COOKIES
) {
219 if (is_web_crawler()) {
220 $user = guest_user();
222 if (!empty($CFG->guestloginbutton
) and !$user and !empty($_SERVER['HTTP_REFERER'])) {
223 // automaticaly log in users coming from search engine results
224 if (strpos($_SERVER['HTTP_REFERER'], 'google') !== false ) {
225 $user = guest_user();
226 } else if (strpos($_SERVER['HTTP_REFERER'], 'altavista') !== false ) {
227 $user = guest_user();
233 $user = new stdClass();
234 $user->id
= 0; // to enable proper function of $CFG->notloggedinroleid hack
235 if (isset($CFG->mnet_localhost_id
)) {
236 $user->mnethostid
= $CFG->mnet_localhost_id
;
238 $user->mnethostid
= 1;
241 session_set_user($user);
245 * Does various session security checks
248 protected function check_security() {
251 if (NO_MOODLE_COOKIES
) {
255 if (!empty($_SESSION['USER']->id
) and !empty($CFG->tracksessionip
)) {
256 /// Make sure current IP matches the one for this session
257 $remoteaddr = getremoteaddr();
259 if (empty($_SESSION['USER']->sessionip
)) {
260 $_SESSION['USER']->sessionip
= $remoteaddr;
263 if ($_SESSION['USER']->sessionip
!= $remoteaddr) {
264 // this is a security feature - terminate the session in case of any doubt
265 $this->terminate_current();
266 print_error('sessionipnomatch2', 'error');
272 * Prepare cookies and various system settings
274 protected function prepare_cookies() {
277 if (!isset($CFG->cookiesecure
) or (strpos($CFG->wwwroot
, 'https://') !== 0 and empty($CFG->sslproxy
))) {
278 $CFG->cookiesecure
= 0;
281 if (!isset($CFG->cookiehttponly
)) {
282 $CFG->cookiehttponly
= 0;
285 /// Set sessioncookie and sessioncookiepath variable if it isn't already
286 if (!isset($CFG->sessioncookie
)) {
287 $CFG->sessioncookie
= '';
289 if (!isset($CFG->sessioncookiedomain
)) {
290 $CFG->sessioncookiedomain
= '';
292 if (!isset($CFG->sessioncookiepath
)) {
293 $CFG->sessioncookiepath
= '/';
296 //discard session ID from POST, GET and globals to tighten security,
297 //this session fixation prevention can not be used in cookieless mode
298 if (empty($CFG->usesid
)) {
299 unset($
{'MoodleSession'.$CFG->sessioncookie
});
300 unset($_GET['MoodleSession'.$CFG->sessioncookie
]);
301 unset($_POST['MoodleSession'.$CFG->sessioncookie
]);
302 unset($_REQUEST['MoodleSession'.$CFG->sessioncookie
]);
304 //compatibility hack for Moodle Cron, cookies not deleted, but set to "deleted" - should not be needed with NO_MOODLE_COOKIES in cron.php now
305 if (!empty($_COOKIE['MoodleSession'.$CFG->sessioncookie
]) && $_COOKIE['MoodleSession'.$CFG->sessioncookie
] == "deleted") {
306 unset($_COOKIE['MoodleSession'.$CFG->sessioncookie
]);
311 * Inits session storage.
313 protected abstract function init_session_storage();
317 * Legacy moodle sessions stored in files, not recommended any more.
320 * @subpackage session
321 * @copyright 2009 Petr Skoda {@link http://skodak.org}
322 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
324 class legacy_file_session
extends session_stub
{
325 protected function init_session_storage() {
328 ini_set('session.save_handler', 'files');
330 // Some distros disable GC by setting probability to 0
331 // overriding the PHP default of 1
332 // (gc_probability is divided by gc_divisor, which defaults to 1000)
333 if (ini_get('session.gc_probability') == 0) {
334 ini_set('session.gc_probability', 1);
337 ini_set('session.gc_maxlifetime', $CFG->sessiontimeout
);
339 // make sure sessions dir exists and is writable, throws exception if not
340 make_upload_directory('sessions');
342 // Need to disable debugging since disk_free_space()
343 // will fail on very large partitions (see MDL-19222)
344 $freespace = @disk_free_space
($CFG->dataroot
.'/sessions');
345 if (!($freespace > 2048) and $freespace !== false) {
346 print_error('sessiondiskfull', 'error');
348 ini_set('session.save_path', $CFG->dataroot
.'/sessions');
351 * Check for existing session with id $sid
352 * @param unknown_type $sid
353 * @return boolean true if session found.
355 public function session_exists($sid){
358 $sid = clean_param($sid, PARAM_FILE
);
359 $sessionfile = "$CFG->dataroot/sessions/sess_$sid";
360 return file_exists($sessionfile);
365 * Recommended moodle session storage.
368 * @subpackage session
369 * @copyright 2009 Petr Skoda {@link http://skodak.org}
370 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
372 class database_session
extends session_stub
{
373 protected $record = null;
374 protected $database = null;
376 public function __construct() {
378 $this->database
= $DB;
379 parent
::__construct();
381 if (!empty($this->record
->state
)) {
382 // something is very wrong
383 session_kill($this->record
->sid
);
385 if ($this->record
->state
== 9) {
386 print_error('dbsessionmysqlpacketsize', 'error');
391 public function session_exists($sid){
394 $sql = "SELECT * FROM {sessions} WHERE timemodified < ? AND sid=? AND state=?";
395 $params = array(time() +
$CFG->sessiontimeout
, $sid, 0);
397 return $this->database
->record_exists_sql($sql, $params);
398 } catch (dml_exception
$ex) {
399 error_log('Error checking existance of database session');
404 protected function init_session_storage() {
407 // gc only from CRON - individual user timeouts now checked during each access
408 ini_set('session.gc_probability', 0);
410 ini_set('session.gc_maxlifetime', $CFG->sessiontimeout
);
412 $result = session_set_save_handler(array($this, 'handler_open'),
413 array($this, 'handler_close'),
414 array($this, 'handler_read'),
415 array($this, 'handler_write'),
416 array($this, 'handler_destroy'),
417 array($this, 'handler_gc'));
419 print_error('dbsessionhandlerproblem', 'error');
423 public function handler_open($save_path, $session_name) {
427 public function handler_close() {
428 if (isset($this->record
->id
)) {
429 $this->database
->release_session_lock($this->record
->id
);
431 $this->record
= null;
435 public function handler_read($sid) {
438 if ($this->record
and $this->record
->sid
!= $sid) {
439 error_log('Weird error reading database session - mismatched sid');
444 if ($record = $this->database
->get_record('sessions', array('sid'=>$sid))) {
445 $this->database
->get_session_lock($record->id
);
448 $record = new stdClass();
451 $record->sessdata
= null;
453 $record->timecreated
= $record->timemodified
= time();
454 $record->firstip
= $record->lastip
= getremoteaddr();
455 $record->id
= $this->database
->insert_record_raw('sessions', $record);
457 $this->database
->get_session_lock($record->id
);
459 } catch (dml_exception
$ex) {
460 error_log('Can not read or insert database sessions');
465 if ($record->timemodified +
$CFG->sessiontimeout
< time()) {
466 $ignoretimeout = false;
467 if (!empty($record->userid
)) { // skips not logged in
468 if ($user = $this->database
->get_record('user', array('id'=>$record->userid
))) {
469 if (!isguestuser($user)) {
470 $authsequence = get_enabled_auth_plugins(); // auths, in sequence
471 foreach($authsequence as $authname) {
472 $authplugin = get_auth_plugin($authname);
473 if ($authplugin->ignore_timeout_hook($user, $record->sid
, $record->timecreated
, $record->timemodified
)) {
474 $ignoretimeout = true;
481 if ($ignoretimeout) {
483 $record->timemodified
= time();
485 $this->database
->update_record('sessions', $record);
486 } catch (dml_exception
$ex) {
487 error_log('Can not refresh database session');
493 $record->sessdata
= null;
495 $record->timecreated
= $record->timemodified
= time();
496 $record->firstip
= $record->lastip
= getremoteaddr();
498 $this->database
->update_record('sessions', $record);
499 } catch (dml_exception
$ex) {
500 error_log('Can not time out database session');
506 $data = is_null($record->sessdata
) ?
'' : base64_decode($record->sessdata
);
508 unset($record->sessdata
); // conserve memory
509 $this->record
= $record;
514 public function handler_write($sid, $session_data) {
517 // TODO: MDL-20625 we need to rollback all active transactions and log error if any open needed
520 if (!empty($USER->realuser
)) {
521 $userid = $USER->realuser
;
522 } else if (!empty($USER->id
)) {
526 if (isset($this->record
->id
)) {
527 $record = new stdClass();
529 $record->sid
= $sid; // might be regenerating sid
530 $this->record
->sessdata
= base64_encode($session_data); // there might be some binary mess :-(
531 $this->record
->userid
= $userid;
532 $this->record
->timemodified
= time();
533 $this->record
->lastip
= getremoteaddr();
535 // TODO: verify session changed before doing update,
536 // also make sure the timemodified field is changed only every 10s if nothing else changes MDL-20462
539 $this->database
->update_record_raw('sessions', $this->record
);
540 } catch (dml_exception
$ex) {
541 if ($this->database
->get_dbfamily() === 'mysql') {
543 $this->database
->set_field('sessions', 'state', 9, array('id'=>$this->record
->id
));
544 } catch (Exception
$ignored) {
547 error_log('Can not write database session - please verify max_allowed_packet is at least 4M!');
549 error_log('Can not write database session');
554 // session already destroyed
555 $record = new stdClass();
558 $record->sessdata
= base64_encode($session_data); // there might be some binary mess :-(
559 $record->userid
= $userid;
560 $record->timecreated
= $record->timemodified
= time();
561 $record->firstip
= $record->lastip
= getremoteaddr();
562 $record->id
= $this->database
->insert_record_raw('sessions', $record);
563 $this->record
= $record;
566 $this->database
->get_session_lock($this->record
->id
);
567 } catch (dml_exception
$ex) {
568 error_log('Can not write new database session');
575 public function handler_destroy($sid) {
578 if (isset($this->record
->id
) and $this->record
->sid
=== $sid) {
579 $this->database
->release_session_lock($this->record
->id
);
580 $this->record
= null;
586 public function handler_gc($ignored_maxlifetime) {
593 * returns true if legacy session used.
594 * @return bool true if legacy(==file) based session used
596 function session_is_legacy() {
598 return ((isset($CFG->dbsessions
) and !$CFG->dbsessions
) or !$DB->session_lock_supported());
602 * Terminates all sessions, auth hooks are not executed.
603 * Useful in upgrade scripts.
605 function session_kill_all() {
608 // always check db table - custom session classes use sessions table
610 $DB->delete_records('sessions');
611 } catch (dml_exception
$ignored) {
612 // do not show any warnings - might be during upgrade/installation
615 if (session_is_legacy()) {
616 $sessiondir = "$CFG->dataroot/sessions";
617 if (is_dir($sessiondir)) {
618 foreach (glob("$sessiondir/sess_*") as $filename) {
626 * Mark session as accessed, prevents timeouts.
629 function session_touch($sid) {
632 // always check db table - custom session classes use sessions table
634 $sql = "UPDATE {sessions} SET timemodified=? WHERE sid=?";
635 $params = array(time(), $sid);
636 $DB->execute($sql, $params);
637 } catch (dml_exception
$ignored) {
638 // do not show any warnings - might be during upgrade/installation
641 if (session_is_legacy()) {
642 $sid = clean_param($sid, PARAM_FILE
);
643 $sessionfile = clean_param("$CFG->dataroot/sessions/sess_$sid", PARAM_FILE
);
644 if (file_exists($sessionfile)) {
645 // if the file is locked it means that it will be updated anyway
646 @touch
($sessionfile);
652 * Terminates one sessions, auth hooks are not executed.
654 * @param string $sid session id
656 function session_kill($sid) {
659 // always check db table - custom session classes use sessions table
661 $DB->delete_records('sessions', array('sid'=>$sid));
662 } catch (dml_exception
$ignored) {
663 // do not show any warnings - might be during upgrade/installation
666 if (session_is_legacy()) {
667 $sid = clean_param($sid, PARAM_FILE
);
668 $sessionfile = "$CFG->dataroot/sessions/sess_$sid";
669 if (file_exists($sessionfile)) {
670 @unlink
($sessionfile);
676 * Terminates all sessions of one user, auth hooks are not executed.
677 * NOTE: This can not work for file based sessions!
679 * @param int $userid user id
681 function session_kill_user($userid) {
684 // always check db table - custom session classes use sessions table
686 $DB->delete_records('sessions', array('userid'=>$userid));
687 } catch (dml_exception
$ignored) {
688 // do not show any warnings - might be during upgrade/installation
691 if (session_is_legacy()) {
697 * Session garbage collection
698 * - verify timeout for all users
699 * - kill sessions of all deleted users
700 * - kill sessions of users with disabled plugins or 'nologin' plugin
702 * NOTE: this can not work when legacy file sessions used!
704 function session_gc() {
707 $maxlifetime = $CFG->sessiontimeout
;
710 /// kill all sessions of deleted users
711 $DB->delete_records_select('sessions', "userid IN (SELECT id FROM {user} WHERE deleted <> 0)");
713 /// kill sessions of users with disabled plugins
714 $auth_sequence = get_enabled_auth_plugins(true);
715 $auth_sequence = array_flip($auth_sequence);
716 unset($auth_sequence['nologin']); // no login allowed
717 $auth_sequence = array_flip($auth_sequence);
719 list($notplugins, $params) = $DB->get_in_or_equal($auth_sequence, SQL_PARAMS_QM
, '', false);
720 $DB->delete_records_select('sessions', "userid IN (SELECT id FROM {user} WHERE auth $notplugins)", $params);
722 /// now get a list of time-out candidates
723 $sql = "SELECT u.*, s.sid, s.timecreated AS s_timecreated, s.timemodified AS s_timemodified
725 JOIN {sessions} s ON s.userid = u.id
726 WHERE s.timemodified + ? < ? AND u.id <> ?";
727 $params = array($maxlifetime, time(), $CFG->siteguest
);
729 $authplugins = array();
730 foreach($auth_sequence as $authname) {
731 $authplugins[$authname] = get_auth_plugin($authname);
733 $rs = $DB->get_recordset_sql($sql, $params);
734 foreach ($rs as $user) {
735 foreach ($authplugins as $authplugin) {
736 if ($authplugin->ignore_timeout_hook($user, $user->sid
, $user->s_timecreated
, $user->s_timemodified
)) {
740 $DB->delete_records('sessions', array('sid'=>$user->sid
));
744 $purgebefore = time() - $maxlifetime;
745 // delete expired sessions for guest user account
746 $DB->delete_records_select('sessions', 'userid = ? AND timemodified < ?', array($CFG->siteguest
, $purgebefore));
747 // delete expired sessions for userid = 0 (not logged in)
748 $DB->delete_records_select('sessions', 'userid = 0 AND timemodified < ?', array($purgebefore));
749 } catch (dml_exception
$ex) {
750 error_log('Error gc-ing sessions');
755 * Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
756 * if one does not already exist, but does not overwrite existing sesskeys. Returns the
757 * sesskey string if $USER exists, or boolean false if not.
763 // note: do not use $USER because it may not be initialised yet
764 if (empty($_SESSION['USER']->sesskey
)) {
765 $_SESSION['USER']->sesskey
= random_string(10);
768 return $_SESSION['USER']->sesskey
;
773 * Check the sesskey and return true of false for whether it is valid.
774 * (You might like to imagine this function is called sesskey_is_valid().)
776 * Every script that lets the user perform a significant action (that is,
777 * changes data in the database) should check the sesskey before doing the action.
778 * Depending on your code flow, you may want to use the {@link require_sesskey()}
781 * @param string $sesskey The sesskey value to check (optional). Normally leave this blank
782 * and this function will do required_param('sesskey', ...).
783 * @return bool whether the sesskey sent in the request matches the one stored in the session.
785 function confirm_sesskey($sesskey=NULL) {
788 if (!empty($USER->ignoresesskey
)) {
792 if (empty($sesskey)) {
793 $sesskey = required_param('sesskey', PARAM_RAW
); // Check script parameters
796 return (sesskey() === $sesskey);
800 * Check the session key using {@link confirm_sesskey()},
801 * and cause a fatal error if it does not match.
803 function require_sesskey() {
804 if (!confirm_sesskey()) {
805 print_error('invalidsesskey');
810 * Sets a moodle cookie with a weakly encrypted username
812 * @param string $username to encrypt and place in a cookie, '' means delete current cookie
815 function set_moodle_cookie($username) {
818 if (NO_MOODLE_COOKIES
) {
822 if ($username === 'guest') {
823 // keep previous cookie in case of guest account login
827 $cookiename = 'MOODLEID_'.$CFG->sessioncookie
;
830 setcookie($cookiename, '', time() - HOURSECS
, $CFG->sessioncookiepath
, $CFG->sessioncookiedomain
, $CFG->cookiesecure
, $CFG->cookiehttponly
);
832 if ($username !== '') {
833 // set username cookie for 60 days
834 setcookie($cookiename, rc4encrypt($username), time()+
(DAYSECS
*60), $CFG->sessioncookiepath
, $CFG->sessioncookiedomain
, $CFG->cookiesecure
, $CFG->cookiehttponly
);
839 * Gets a moodle cookie with a weakly encrypted username
841 * @return string username
843 function get_moodle_cookie() {
846 if (NO_MOODLE_COOKIES
) {
850 $cookiename = 'MOODLEID_'.$CFG->sessioncookie
;
852 if (empty($_COOKIE[$cookiename])) {
855 $username = rc4decrypt($_COOKIE[$cookiename]);
856 if ($username === 'guest' or $username === 'nobody') {
857 // backwards compatibility - we do not set these cookies any more
866 * Setup $USER object - called during login, loginas, etc.
867 * Preloads capabilities and checks enrolment plugins
869 * @param stdClass $user full user record object
872 function session_set_user($user) {
873 $_SESSION['USER'] = $user;
874 unset($_SESSION['USER']->description
); // conserve memory
875 if (!isset($_SESSION['USER']->access
)) {
876 // check enrolments and load caps only once
877 enrol_check_plugins($_SESSION['USER']);
878 load_all_capabilities();
880 sesskey(); // init session key
884 * Is current $USER logged-in-as somebody else?
887 function session_is_loggedinas() {
888 return !empty($_SESSION['USER']->realuser
);
892 * Returns the $USER object ignoring current login-as session
893 * @return stdClass user object
895 function session_get_realuser() {
896 if (session_is_loggedinas()) {
897 return $_SESSION['REALUSER'];
899 return $_SESSION['USER'];
904 * Login as another user - no security checks here.
906 * @param stdClass $context
909 function session_loginas($userid, $context) {
910 if (session_is_loggedinas()) {
914 // switch to fresh new $SESSION
915 $_SESSION['REALSESSION'] = $_SESSION['SESSION'];
916 $_SESSION['SESSION'] = new stdClass();
918 /// Create the new $USER object with all details and reload needed capabilities
919 $_SESSION['REALUSER'] = $_SESSION['USER'];
920 $user = get_complete_user_data('id', $userid);
921 $user->realuser
= $_SESSION['REALUSER']->id
;
922 $user->loginascontext
= $context;
923 session_set_user($user);
927 * Sets up current user and course environment (lang, etc.) in cron.
928 * Do not use outside of cron script!
930 * @param stdClass $user full user object, null means default cron user (admin)
931 * @param $course full course record, null means $SITE
934 function cron_setup_user($user = NULL, $course = NULL) {
935 global $CFG, $SITE, $PAGE;
937 static $cronuser = NULL;
938 static $cronsession = NULL;
940 if (empty($cronuser)) {
941 /// ignore admins timezone, language and locale - use site default instead!
942 $cronuser = get_admin();
943 $cronuser->timezone
= $CFG->timezone
;
944 $cronuser->lang
= '';
945 $cronuser->theme
= '';
946 unset($cronuser->description
);
948 $cronsession = new stdClass();
952 // cached default cron user (==modified admin for now)
953 session_set_user($cronuser);
954 $_SESSION['SESSION'] = $cronsession;
957 // emulate real user session - needed for caps in cron
958 if ($_SESSION['USER']->id
!= $user->id
) {
959 session_set_user($user);
960 $_SESSION['SESSION'] = new stdClass();
964 // TODO MDL-19774 relying on global $PAGE in cron is a bad idea.
965 // Temporary hack so that cron does not give fatal errors.
966 $PAGE = new moodle_page();
968 $PAGE->set_course($course);
970 $PAGE->set_course($SITE);
973 // TODO: it should be possible to improve perf by caching some limited number of users here ;-)
978 * Enable cookieless sessions by including $CFG->usesid=true;
980 * Based on code from php manual by Richard at postamble.co.uk
981 * Attempts to use cookies if cookies not present then uses session ids attached to all urls and forms to pass session id from page to page.
982 * If site is open to google, google is given guest access as usual and there are no sessions. No session ids will be attached to urls for googlebot.
983 * This doesn't require trans_sid to be turned on but this is recommended for better performance
985 * session.use_trans_sid = 1
986 * in your php.ini file and make sure that you don't have a line like this in your php.ini
987 * session.use_only_cookies = 1
988 * @author Richard at postamble.co.uk and Jamie Pratt
989 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
992 * You won't call this function directly. This function is used to process
993 * text buffered by php in an output buffer. All output is run through this function
994 * before it is ouput.
995 * @param string $buffer is the output sent from php
996 * @return string the output sent to the browser
998 function sid_ob_rewrite($buffer){
999 $replacements = array(
1000 '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*")([^"]*)(")/i',
1001 '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*\')([^\']*)(\')/i');
1003 $buffer = preg_replace_callback($replacements, 'sid_rewrite_link_tag', $buffer);
1004 $buffer = preg_replace('/<form\s[^>]*>/i',
1005 '\0<input type="hidden" name="' . session_name() . '" value="' . session_id() . '"/>', $buffer);
1010 * You won't call this function directly. This function is used to process
1011 * text buffered by php in an output buffer. All output is run through this function
1012 * before it is ouput.
1013 * This function only processes absolute urls, it is used when we decide that
1014 * php is processing other urls itself but needs some help with internal absolute urls still.
1015 * @param string $buffer is the output sent from php
1016 * @return string the output sent to the browser
1018 function sid_ob_rewrite_absolute($buffer){
1019 $replacements = array(
1020 '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*")((?:http|https)[^"]*)(")/i',
1021 '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*\')((?:http|https)[^\']*)(\')/i');
1023 $buffer = preg_replace_callback($replacements, 'sid_rewrite_link_tag', $buffer);
1024 $buffer = preg_replace('/<form\s[^>]*>/i',
1025 '\0<input type="hidden" name="' . session_name() . '" value="' . session_id() . '"/>', $buffer);
1030 * A function to process link, a and script tags found
1031 * by preg_replace_callback in {@link sid_ob_rewrite($buffer)}.
1033 function sid_rewrite_link_tag($matches){
1035 $url = sid_process_url($url);
1036 return $matches[1].$url.$matches[5];
1040 * You can call this function directly. This function is used to process
1041 * urls to add a moodle session id to the url for internal links.
1042 * @param string $url is a url
1043 * @return string the processed url
1045 function sid_process_url($url) {
1048 if ((preg_match('/^(http|https):/i', $url)) // absolute url
1049 && ((stripos($url, $CFG->wwwroot
)!==0) && stripos($url, $CFG->httpswwwroot
)!==0)) { // and not local one
1050 return $url; //don't attach sessid to non local urls
1052 if ($url[0]=='#' ||
(stripos($url, 'javascript:')===0)) {
1053 return $url; //don't attach sessid to anchors
1055 if (strpos($url, session_name())!==FALSE) {
1056 return $url; //don't attach sessid to url that already has one sessid
1058 if (strpos($url, "?")===FALSE) {
1059 $append = "?".strip_tags(session_name() . '=' . session_id());
1061 $append = "&".strip_tags(session_name() . '=' . session_id());
1063 //put sessid before any anchor
1064 $p = strpos($url, "#");
1066 $anch = substr($url, $p);
1067 $url = substr($url, 0, $p).$append.$anch ;
1075 * Call this function before there has been any output to the browser to
1076 * buffer output and add session ids to all internal links.
1078 function sid_start_ob(){
1080 //don't attach sess id for bots
1082 if (!empty($_SERVER['HTTP_USER_AGENT'])) {
1083 if (!empty($CFG->opentogoogle
)) {
1084 if (strpos($_SERVER['HTTP_USER_AGENT'], 'Googlebot') !== false) {
1085 @ini_set
('session.use_trans_sid', '0'); // try and turn off trans_sid
1089 if (strpos($_SERVER['HTTP_USER_AGENT'], 'google.com') !== false) {
1090 @ini_set
('session.use_trans_sid', '0'); // try and turn off trans_sid
1095 if (strpos($_SERVER['HTTP_USER_AGENT'], 'W3C_Validator') !== false) {
1096 @ini_set
('session.use_trans_sid', '0'); // try and turn off trans_sid
1102 @ini_set
('session.use_trans_sid', '1'); // try and turn on trans_sid
1104 if (ini_get('session.use_trans_sid') != 0) {
1105 // use trans sid as its available
1106 ini_set('url_rewriter.tags', 'a=href,area=href,script=src,link=href,frame=src,form=fakeentry');
1107 ob_start('sid_ob_rewrite_absolute');
1109 //rewrite all links ourselves
1110 ob_start('sid_ob_rewrite');