Updated the HEAD build version to 20090118
[moodle.git] / lib / sessionlib.php
CommitLineData
57f7b7ce 1<?php //$Id$
2
542797b4 3/**
4 * Factory method returning moodle_session object.
5 * @return moodle_session
6 */
b7b64ff2 7function session_get_instance() {
5e9dd017 8 global $CFG, $DB;
0a2092a3 9
0ad6b20c 10 static $session = null;
11
12 if (is_null($session)) {
0a2092a3 13 if (defined('SESSION_CUSTOM')) {
14 // this is a hook for custom session handling, webservices, etc.
15 if (defined('SESSION_CUSTOM_FILE')) {
16 require_once($CFG->dirroot.SESSION_CUSTOM_FILE);
17 }
18 $session_class = SESSION_CUSTOM;
19 $session = new $session_class();
20
35d6a2a4 21 } else if ((!isset($CFG->dbsessions) or $CFG->dbsessions) and $DB->session_lock_supported()) {
0a2092a3 22 // default recommended session type
23 $session = new database_session();
24
25 } else {
26 // legacy limited file based storage - some features and auth plugins will not work, sorry
27 $session = new legacy_file_session();
28 }
0ad6b20c 29 }
30
31 return $session;
32}
33
0a2092a3 34interface moodle_session {
56949c17 35 /**
36 * Terminate current session
37 * @return void
38 */
39 public function terminate_current();
40
35d6a2a4 41 /**
42 * Terminates all sessions.
43 */
44 public function terminate_all();
45
56949c17 46 /**
47 * No more changes in session expected.
48 * Unblocks the sesions, other scripts may start executing in parallel.
49 * @return void
50 */
51 public function write_close();
52
0a2092a3 53}
54
57f7b7ce 55/**
56 * Class handling all session and cookies related stuff.
57 */
0a2092a3 58abstract class session_stub implements moodle_session {
b7b64ff2 59 public function __construct() {
57f7b7ce 60 global $CFG;
57f7b7ce 61
0a2092a3 62 if (!defined('NO_MOODLE_COOKIES')) {
63 if (CLI_SCRIPT) {
64 // CLI scripts can not have session
65 define('NO_MOODLE_COOKIES', true);
66 } else {
67 define('NO_MOODLE_COOKIES', false);
68 }
57f7b7ce 69 }
70
0ad6b20c 71 if (NO_MOODLE_COOKIES) {
0a2092a3 72 // session not used at all
45871c08 73 $CFG->usesid = 0;
0a2092a3 74
0ad6b20c 75 $_SESSION = array();
76 $_SESSION['SESSION'] = new object();
0a2092a3 77 $_SESSION['USER'] = new object();
0ad6b20c 78
79 } else {
0a2092a3 80 $this->prepare_cookies();
81 $this->init_session_storage();
82
35d6a2a4 83 $newsession = empty($_COOKIE['MoodleSession'.$CFG->sessioncookie]);
84
85 if (!empty($CFG->usesid) && $newsession) {
0a2092a3 86 sid_start_ob();
45871c08 87 } else {
88 $CFG->usesid = 0;
89 ini_set('session.use_trans_sid', '0');
0a2092a3 90 }
91
57f7b7ce 92 session_name('MoodleSession'.$CFG->sessioncookie);
e6e13284 93 session_set_cookie_params(0, $CFG->sessioncookiepath, $CFG->sessioncookiedomain, $CFG->cookiesecure, $CFG->cookiehttponly);
57f7b7ce 94 @session_start();
95 if (!isset($_SESSION['SESSION'])) {
96 $_SESSION['SESSION'] = new object();
35d6a2a4 97 if (!$newsession and !empty($CFG->rolesactive)) {
98 $_SESSION['SESSION']->has_timed_out = true;
99 }
57f7b7ce 100 }
101 if (!isset($_SESSION['USER'])) {
102 $_SESSION['USER'] = new object();
103 }
57f7b7ce 104 }
57f7b7ce 105
b7b64ff2 106 $this->check_user_initialised();
9bda43e6 107
108 $this->check_security();
b7b64ff2 109 }
110
56949c17 111 /**
112 * Terminates active moodle session
113 */
114 public function terminate_current() {
115 global $CFG, $SESSION, $USER;
116
117 if (NO_MOODLE_COOKIES) {
118 return;
119 }
120
121 $_SESSION = array();
35d6a2a4 122 $_SESSION['SESSION'] = new object();
123 $_SESSION['USER'] = new object();
124 $_SESSION['USER']->id = 0;
56949c17 125 if (isset($CFG->mnet_localhost_id)) {
35d6a2a4 126 $_SESSION['USER']->mnethostid = $CFG->mnet_localhost_id;
56949c17 127 }
128
35d6a2a4 129 $SESSION = $_SESSION['SESSION']; // this may not work properly
130 $USER = $_SESSION['USER']; // this may not work properly
131
56949c17 132 // Initialize variable to pass-by-reference to headers_sent(&$file, &$line)
133 $file = null;
134 $line = null;
135 if (headers_sent($file, $line)) {
136 error_log('Can not terminate session properly - headers were already sent in file: '.$file.' on line '.$line);
137 }
138
35d6a2a4 139 // now let's try to get a new session id
140 session_regenerate_id();
56949c17 141
142 // close the session
35d6a2a4 143 session_write_close();
56949c17 144 }
145
146 /**
147 * No more changes in session expected.
148 * Unblocks the sesions, other scripts may start executing in parallel.
149 * @return void
150 */
151 public function write_close() {
152 if (NO_MOODLE_COOKIES) {
153 return;
154 }
155
156 session_write_close();
157 }
158
b7b64ff2 159 /**
160 * Initialise $USER object, handles google access.
161 *
162 * @return void
163 */
164 protected function check_user_initialised() {
165 if (isset($_SESSION['USER']->id)) {
166 // already set up $USER
167 return;
168 }
169
170 $user = null;
171
172 if (!empty($CFG->opentogoogle) and !NO_MOODLE_COOKIES) {
173 if (!empty($_SERVER['HTTP_USER_AGENT'])) {
174 // allow web spiders in as guest users
175 if (strpos($_SERVER['HTTP_USER_AGENT'], 'Googlebot') !== false ) {
176 $user = guest_user();
177 } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'google.com') !== false ) { // Google
178 $user = guest_user();
179 } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Yahoo! Slurp') !== false ) { // Yahoo
180 $user = guest_user();
181 } else if (strpos($_SERVER['HTTP_USER_AGENT'], '[ZSEBOT]') !== false ) { // Zoomspider
182 $user = guest_user();
183 } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSNBOT') !== false ) { // MSN Search
184 $user = guest_user();
185 }
186 }
1b813f5c 187 if (!empty($CFG->guestloginbutton) and !$user and !empty($_SERVER['HTTP_REFERER'])) {
b7b64ff2 188 // automaticaly log in users coming from search engine results
189 if (strpos($_SERVER['HTTP_REFERER'], 'google') !== false ) {
190 $user = guest_user();
191 } else if (strpos($_SERVER['HTTP_REFERER'], 'altavista') !== false ) {
192 $user = guest_user();
193 }
194 }
195 }
196
197 if (!$user) {
198 $user = new object();
199 $user->id = 0; // to enable proper function of $CFG->notloggedinroleid hack
0ad6b20c 200 if (isset($CFG->mnet_localhost_id)) {
b7b64ff2 201 $user->mnethostid = $CFG->mnet_localhost_id;
0a2092a3 202 } else {
203 $user->mnethostid = 1;
57f7b7ce 204 }
205 }
b7b64ff2 206 session_set_user($user);
57f7b7ce 207 }
208
9bda43e6 209 /**
210 * Does various session security checks
211 * @global void
212 */
93f66983 213 protected function check_security() {
214 global $CFG;
215
0a2092a3 216 if (NO_MOODLE_COOKIES) {
217 return;
218 }
219
9bda43e6 220 if (!empty($_SESSION['USER']->id) and !empty($CFG->tracksessionip)) {
221 /// Make sure current IP matches the one for this session
93f66983 222 $remoteaddr = getremoteaddr();
223
224 if (empty($_SESSION['USER']->sessionip)) {
225 $_SESSION['USER']->sessionip = $remoteaddr;
226 }
227
228 if ($_SESSION['USER']->sessionip != $remoteaddr) {
9bda43e6 229 // this is a security feature - terminate the session in case of any doubt
56949c17 230 $this->terminate_current();
9bda43e6 231 print_error('sessionipnomatch2', 'error');
93f66983 232 }
233 }
93f66983 234 }
235
57f7b7ce 236 /**
237 * Prepare cookies and varions system settings
238 */
b7b64ff2 239 protected function prepare_cookies() {
0a2092a3 240 global $CFG;
57f7b7ce 241
11e7b506 242 if (!isset($CFG->cookiesecure) or (strpos($CFG->wwwroot, 'https://') !== 0 and empty($CFG->sslproxy))) {
57f7b7ce 243 $CFG->cookiesecure = 0;
244 }
245
246 if (!isset($CFG->cookiehttponly)) {
247 $CFG->cookiehttponly = 0;
248 }
249
250 /// Set sessioncookie and sessioncookiepath variable if it isn't already
251 if (!isset($CFG->sessioncookie)) {
252 $CFG->sessioncookie = '';
253 }
e6e13284 254 if (!isset($CFG->sessioncookiedomain)) {
255 $CFG->sessioncookiedomain = '';
256 }
57f7b7ce 257 if (!isset($CFG->sessioncookiepath)) {
258 $CFG->sessioncookiepath = '/';
259 }
260
261 //discard session ID from POST, GET and globals to tighten security,
262 //this session fixation prevention can not be used in cookieless mode
263 if (empty($CFG->usesid)) {
264 unset(${'MoodleSession'.$CFG->sessioncookie});
265 unset($_GET['MoodleSession'.$CFG->sessioncookie]);
266 unset($_POST['MoodleSession'.$CFG->sessioncookie]);
b7b64ff2 267 unset($_REQUEST['MoodleSession'.$CFG->sessioncookie]);
57f7b7ce 268 }
269 //compatibility hack for Moodle Cron, cookies not deleted, but set to "deleted" - should not be needed with NO_MOODLE_COOKIES in cron.php now
270 if (!empty($_COOKIE['MoodleSession'.$CFG->sessioncookie]) && $_COOKIE['MoodleSession'.$CFG->sessioncookie] == "deleted") {
271 unset($_COOKIE['MoodleSession'.$CFG->sessioncookie]);
272 }
57f7b7ce 273 }
274
275 /**
276 * Inits session storage.
277 */
f61a032a 278 protected abstract function init_session_storage();
279
280}
281
282/**
283 * Legacy moodle sessions stored in files, not recommended any more.
284 */
0a2092a3 285class legacy_file_session extends session_stub {
b7b64ff2 286 protected function init_session_storage() {
57f7b7ce 287 global $CFG;
288
0a2092a3 289 ini_set('session.save_handler', 'files');
290
f61a032a 291 // Some distros disable GC by setting probability to 0
292 // overriding the PHP default of 1
293 // (gc_probability is divided by gc_divisor, which defaults to 1000)
294 if (ini_get('session.gc_probability') == 0) {
295 ini_set('session.gc_probability', 1);
296 }
57f7b7ce 297
3b1a9849 298 if (empty($CFG->sessiontimeout)) {
299 $CFG->sessiontimeout = 7200;
f61a032a 300 }
3b1a9849 301 ini_set('session.gc_maxlifetime', $CFG->sessiontimeout);
57f7b7ce 302
f61a032a 303 if (!file_exists($CFG->dataroot .'/sessions')) {
304 make_upload_directory('sessions');
305 }
306 if (!is_writable($CFG->dataroot .'/sessions/')) {
307 print_error('sessionnotwritable', 'error');
57f7b7ce 308 }
f61a032a 309 ini_set('session.save_path', $CFG->dataroot .'/sessions');
310 }
56949c17 311
35d6a2a4 312 public function terminate_all() {
313 // TODO
314 }
f61a032a 315}
316
317/**
318 * Recommended moodle session storage.
319 */
0a2092a3 320class database_session extends session_stub {
321 protected $record = null;
322 protected $database = null;
323
f61a032a 324 protected function init_session_storage() {
325 global $CFG;
326
ef159e5f 327 // ini_get('session.gc_probability') == 0 means we rely on cron cleanup only
328 // TODO: implement cron db session cleanup
0a2092a3 329
ef159e5f 330 if (empty($CFG->sessiontimeout)) {
331 $CFG->sessiontimeout = 7200;
0a2092a3 332 }
ef159e5f 333 ini_set('session.gc_maxlifetime', $CFG->sessiontimeout);
0a2092a3 334
335 $result = session_set_save_handler(array($this, 'handler_open'),
336 array($this, 'handler_close'),
337 array($this, 'handler_read'),
338 array($this, 'handler_write'),
339 array($this, 'handler_destroy'),
340 array($this, 'handler_gc'));
341 if (!$result) {
eee3bd3f 342 print_error('dbsessionhandlerproblem', 'error');
0a2092a3 343 }
344 }
345
35d6a2a4 346 public function terminate_all() {
347 try {
348 // do not show any warnings - might be during upgrade/installation
349 $this->database->delete_records('sessions');
350 } catch (dml_exception $ignored) {
351
352 }
353 }
354
0a2092a3 355 public function handler_open($save_path, $session_name) {
356 global $DB;
357
358 $this->database = $DB;
0a2092a3 359 return true;
360 }
361
362 public function handler_close() {
363 $this->record = null;
364 return true;
365 }
366
367 public function handler_read($sid) {
368 global $CFG;
369
0a2092a3 370 if ($this->record and $this->record->sid != $sid) {
371 error_log('Weird error reading session - mismatched sid');
372 return '';
373 }
374
375 try {
3b1a9849 376 if ($record = $this->database->get_record('sessions', array('sid'=>$sid))) {
377 $this->database->get_session_lock($record->id);
378
379 } else {
0a2092a3 380 $record = new object();
381 $record->state = 0;
382 $record->sid = $sid;
383 $record->sessdata = null;
17d93489 384 $record->sessdatahash = null;
0a2092a3 385 $record->userid = 0;
386 $record->timecreated = $record->timemodified = time();
387 $record->firstip = $record->lastip = getremoteaddr();
0a2092a3 388 $record->id = $this->database->insert_record_raw('sessions', $record);
389
3b1a9849 390 $this->database->get_session_lock($record->id);
0a2092a3 391 }
392 } catch (dml_exception $ex) {
393 if (!empty($CFG->rolesactive)) {
394 error_log('Can not read or insert database sessions');
395 }
396 return '';
397 }
398
3b1a9849 399 // verify timeout
400 if ($record->timemodified + $CFG->sessiontimeout < time()) {
401 // TODO: implement auth plugin timeout hook (see gc)
402 $record->state = 0;
403 $record->sessdata = null;
404 $record->sessdatahash = null;
405 $record->userid = 0;
406 $record->timecreated = $record->timemodified = time();
407 $record->firstip = $record->lastip = getremoteaddr();
eee3bd3f 408 try {
3b1a9849 409 $this->database->update_record('sessions', $record);
410 } catch (dml_exception $ex) {
a644e212 411 error_log('Can not time out database session');
3b1a9849 412 return '';
eee3bd3f 413 }
eee3bd3f 414 }
415
3b1a9849 416 if ($record->sessdatahash !== null) {
417 if (md5($record->sessdata) !== $record->sessdatahash) {
418 // probably this is caused by misconfigured mysql - the allowed request size might be too small
419 try {
420 $this->database->delete_records('sessions', array('sid'=>$record->sid));
421 } catch (dml_exception $ignored) {
422 }
423 print_error('dbsessionbroken', 'error');
424 }
425
426 $data = base64_decode($record->sessdata);
427 } else {
428 $data = '';
429 }
430
0a2092a3 431 unset($record->sessdata); // conserve memory
432 $this->record = $record;
433
434 return $data;
435 }
436
437 public function handler_write($sid, $session_data) {
438 global $USER;
439
440 if (!$this->record) {
441 error_log('Weird error writing session');
442 return true;
443 }
444
3b1a9849 445 $this->database->release_session_lock($this->record->id);
7f79aaea 446
0a2092a3 447 $this->record->sid = $sid; // it might be regenerated
448 $this->record->sessdata = base64_encode($session_data); // there might be some binary mess :-(
17d93489 449 $this->record->sessdatahash = md5($this->record->sessdata);
0a2092a3 450 $this->record->userid = empty($USER->realuser) ? $USER->id : $USER->realuser;
451 $this->record->timemodified = time();
452 $this->record->lastip = getremoteaddr();
453
eee3bd3f 454 // TODO: verify session changed before doing update
455
0a2092a3 456 try {
457 $this->database->update_record_raw('sessions', $this->record);
458 } catch (dml_exception $ex) {
459 error_log('Can not write session to database.');
460 }
17d93489 461
0a2092a3 462 return true;
463 }
464
465 public function handler_destroy($sid) {
466 if (!$this->record or $this->record->sid != $sid) {
467 error_log('Weird error destroying session - mismatched sid');
468 return true;
469 }
470
3b1a9849 471 $this->database->release_session_lock($this->record->id);
7f79aaea 472
0a2092a3 473 try {
474 $this->database->delete_records('sessions', array('sid'=>$this->record->sid));
475 } catch (dml_exception $ex) {
476 error_log('Can not destroy database session.');
477 }
478
479 return true;
480 }
481
3b1a9849 482 public function handler_gc($ignored_maxlifetime) {
483 global $CFG;
484 $maxlifetime = $CFG->sessiontimeout;
485
0a2092a3 486 $select = "timemodified + :maxlifetime < :now";
487 $params = array('now'=>time(), 'maxlifetime'=>$maxlifetime);
488
7f79aaea 489 // TODO: add auth plugin hook that would allow extending of max lifetime
eee3bd3f 490
0a2092a3 491 try {
492 $this->database->delete_records_select('sessions', $select, $params);
493 } catch (dml_exception $ex) {
494 error_log('Can not garbage collect database sessions.');
495 }
496
497 return true;
57f7b7ce 498 }
0a2092a3 499
0ad6b20c 500}
57f7b7ce 501
0ad6b20c 502/**
503 * Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
504 * if one does not already exist, but does not overwrite existing sesskeys. Returns the
505 * sesskey string if $USER exists, or boolean false if not.
506 *
507 * @uses $USER
508 * @return string
509 */
510function sesskey() {
511 global $USER;
57f7b7ce 512
0ad6b20c 513 if (empty($USER->sesskey)) {
514 $USER->sesskey = random_string(10);
515 }
57f7b7ce 516
0ad6b20c 517 return $USER->sesskey;
518}
57f7b7ce 519
57f7b7ce 520
0ad6b20c 521/**
522 * For security purposes, this function will check that the currently
523 * given sesskey (passed as a parameter to the script or this function)
524 * matches that of the current user.
525 *
526 * @param string $sesskey optionally provided sesskey
527 * @return bool
528 */
529function confirm_sesskey($sesskey=NULL) {
530 global $USER;
57f7b7ce 531
eb85959b 532 if (!empty($USER->ignoresesskey)) {
0ad6b20c 533 return true;
534 }
57f7b7ce 535
0ad6b20c 536 if (empty($sesskey)) {
537 $sesskey = required_param('sesskey', PARAM_RAW); // Check script parameters
57f7b7ce 538 }
539
eb85959b 540 return (sesskey() === $sesskey);
0ad6b20c 541}
542
543/**
544 * Sets a moodle cookie with a weakly encrypted string
545 *
546 * @uses $CFG
547 * @uses DAYSECS
548 * @uses HOURSECS
549 * @param string $thing The string to encrypt and place in a cookie
550 */
551function set_moodle_cookie($thing) {
552 global $CFG;
553
0a2092a3 554 if (NO_MOODLE_COOKIES) {
555 return;
556 }
557
0ad6b20c 558 if ($thing == 'guest') { // Ignore guest account
559 return;
57f7b7ce 560 }
561
0ad6b20c 562 $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
563
564 $days = 60;
565 $seconds = DAYSECS*$days;
566
567 // no need to set secure or http cookie only here - it is not secret
568 setcookie($cookiename, '', time() - HOURSECS, $CFG->sessioncookiepath, $CFG->sessioncookiedomain);
569 setcookie($cookiename, rc4encrypt($thing), time()+$seconds, $CFG->sessioncookiepath, $CFG->sessioncookiedomain);
570}
571
572/**
573 * Gets a moodle cookie with a weakly encrypted string
574 *
575 * @uses $CFG
576 * @return string
577 */
578function get_moodle_cookie() {
579 global $CFG;
580
0a2092a3 581 if (NO_MOODLE_COOKIES) {
582 return '';
583 }
584
0ad6b20c 585 $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
586
587 if (empty($_COOKIE[$cookiename])) {
588 return '';
589 } else {
590 $thing = rc4decrypt($_COOKIE[$cookiename]);
591 return ($thing == 'guest') ? '': $thing; // Ignore guest account
57f7b7ce 592 }
0ad6b20c 593}
57f7b7ce 594
b7b64ff2 595/**
596 * Setup $USER object - called during login, loginas, etc.
597 * Preloads capabilities and checks enrolment plugins
598 *
599 * @param object $user full user record object
600 * @return void
601 */
602function session_set_user($user) {
603 $_SESSION['USER'] = $user;
604 check_enrolment_plugins($_SESSION['USER']);
605 load_all_capabilities();
eb85959b 606 sesskey(); // init session key
b7b64ff2 607}
608
542797b4 609/**
610 * Is current $USER logged-in-as somebody else?
611 * @return bool
612 */
b7b64ff2 613function session_is_loggedinas() {
85f6b737 614 return !empty($_SESSION['USER']->realuser);
542797b4 615}
616
6132768e 617/**
618 * Returns the $USER object ignoring current login-as session
619 * @return object user object
620 */
b7b64ff2 621function session_get_realuser() {
622 if (session_is_loggedinas()) {
6132768e 623 return $_SESSION['REALUSER'];
624 } else {
625 return $_SESSION['USER'];
626 }
627}
628
542797b4 629/**
630 * Login as another user - no security checks here.
631 * @param int $userid
632 * @param object $context
633 * @return void
634 */
8d1964c4 635function session_loginas($userid, $context) {
b7b64ff2 636 if (session_is_loggedinas()) {
8d1964c4 637 return;
638 }
639
85f6b737 640 // switch to fresh new $SESSION
641 $_SESSION['REALSESSION'] = $_SESSION['SESSION'];
6132768e 642 $_SESSION['SESSION'] = new object();
8d1964c4 643
85f6b737 644 /// Create the new $USER object with all details and reload needed capabilitites
645 $_SESSION['REALUSER'] = $_SESSION['USER'];
b7b64ff2 646 $user = get_complete_user_data('id', $userid);
647 $user->realuser = $_SESSION['REALUSER']->id;
648 $user->loginascontext = $context;
649 session_set_user($user);
8d1964c4 650}
651
542797b4 652/**
653 * Terminate login-as session
654 * @return void
655 */
8d1964c4 656function session_unloginas() {
b7b64ff2 657 if (!session_is_loggedinas()) {
8d1964c4 658 return;
659 }
660
6132768e 661 $_SESSION['SESSION'] = $_SESSION['REALSESSION'];
662 unset($_SESSION['REALSESSION']);
8d1964c4 663
6132768e 664 $_SESSION['USER'] = $_SESSION['REALUSER'];
665 unset($_SESSION['REALUSER']);
8d1964c4 666}
667
e8b7114d 668/**
669 * Sets up current user and course enviroment (lang, etc.) in cron.
670 * Do not use outside of cron script!
671 *
672 * @param object $user full user object, null means default cron user (admin)
673 * @param $course full course record, null means $SITE
674 * @return void
675 */
676function cron_setup_user($user=null, $course=null) {
677 global $CFG, $SITE;
678
679 static $cronuser = null;
680 static $cronsession = null;
681
682 if (empty($cronuser)) {
683 /// ignore admins timezone, language and locale - use site deafult instead!
684 $cronuser = get_admin();
685 $cronuser->timezone = $CFG->timezone;
686 $cronuser->lang = '';
687 $cronuser->theme = '';
688
689 $cronsession = array();
690 }
691
692 if (!$user) {
693 // cached default cron user (==modified admin for now)
694 session_set_user($cronuser);
695 $_SESSION['SESSION'] = $cronsession;
696
697 } else {
698 // emulate real user session - needed for caps in cron
699 if ($_SESSION['USER']->id != $user->id) {
700 session_set_user($user);
701 $_SESSION['SESSION'] = array();
702 }
703 }
704
705 if ($course) {
706 course_setup($course);
707 } else {
708 course_setup($SITE);
709 }
710
711 // TODO: it should be possible to improve perf by caching some limited number of users here ;-)
712
713}
714
0ad6b20c 715/**
716* Enable cookieless sessions by including $CFG->usesid=true;
717* in config.php.
718* Based on code from php manual by Richard at postamble.co.uk
719* 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.
720* 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.
721* This doesn't require trans_sid to be turned on but this is recommended for better performance
722* you should put :
723* session.use_trans_sid = 1
724* in your php.ini file and make sure that you don't have a line like this in your php.ini
725* session.use_only_cookies = 1
726* @author Richard at postamble.co.uk and Jamie Pratt
727* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
728*/
729/**
730* You won't call this function directly. This function is used to process
731* text buffered by php in an output buffer. All output is run through this function
732* before it is ouput.
733* @param string $buffer is the output sent from php
734* @return string the output sent to the browser
735*/
736function sid_ob_rewrite($buffer){
737 $replacements = array(
738 '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*")([^"]*)(")/i',
739 '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*\')([^\']*)(\')/i');
740
741 $buffer = preg_replace_callback($replacements, 'sid_rewrite_link_tag', $buffer);
742 $buffer = preg_replace('/<form\s[^>]*>/i',
743 '\0<input type="hidden" name="' . session_name() . '" value="' . session_id() . '"/>', $buffer);
744
745 return $buffer;
746}
747/**
748* You won't call this function directly. This function is used to process
749* text buffered by php in an output buffer. All output is run through this function
750* before it is ouput.
751* This function only processes absolute urls, it is used when we decide that
752* php is processing other urls itself but needs some help with internal absolute urls still.
753* @param string $buffer is the output sent from php
754* @return string the output sent to the browser
755*/
756function sid_ob_rewrite_absolute($buffer){
757 $replacements = array(
758 '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*")((?:http|https)[^"]*)(")/i',
759 '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*\')((?:http|https)[^\']*)(\')/i');
760
761 $buffer = preg_replace_callback($replacements, 'sid_rewrite_link_tag', $buffer);
762 $buffer = preg_replace('/<form\s[^>]*>/i',
763 '\0<input type="hidden" name="' . session_name() . '" value="' . session_id() . '"/>', $buffer);
764 return $buffer;
765}
57f7b7ce 766
0ad6b20c 767/**
768* A function to process link, a and script tags found
769* by preg_replace_callback in {@link sid_ob_rewrite($buffer)}.
770*/
771function sid_rewrite_link_tag($matches){
772 $url = $matches[4];
773 $url = sid_process_url($url);
774 return $matches[1].$url.$matches[5];
775}
776
777/**
778* You can call this function directly. This function is used to process
779* urls to add a moodle session id to the url for internal links.
780* @param string $url is a url
781* @return string the processed url
782*/
783function sid_process_url($url) {
784 global $CFG;
785
786 if ((preg_match('/^(http|https):/i', $url)) // absolute url
787 && ((stripos($url, $CFG->wwwroot)!==0) && stripos($url, $CFG->httpswwwroot)!==0)) { // and not local one
788 return $url; //don't attach sessid to non local urls
789 }
790 if ($url[0]=='#' || (stripos($url, 'javascript:')===0)) {
791 return $url; //don't attach sessid to anchors
792 }
793 if (strpos($url, session_name())!==FALSE) {
794 return $url; //don't attach sessid to url that already has one sessid
795 }
796 if (strpos($url, "?")===FALSE) {
797 $append = "?".strip_tags(session_name() . '=' . session_id());
798 } else {
799 $append = "&amp;".strip_tags(session_name() . '=' . session_id());
57f7b7ce 800 }
0ad6b20c 801 //put sessid before any anchor
802 $p = strpos($url, "#");
803 if ($p!==FALSE){
804 $anch = substr($url, $p);
805 $url = substr($url, 0, $p).$append.$anch ;
806 } else {
807 $url .= $append ;
808 }
809 return $url;
810}
57f7b7ce 811
0ad6b20c 812/**
813* Call this function before there has been any output to the browser to
814* buffer output and add session ids to all internal links.
815*/
816function sid_start_ob(){
817 global $CFG;
818 //don't attach sess id for bots
819
820 if (!empty($_SERVER['HTTP_USER_AGENT'])) {
821 if (!empty($CFG->opentogoogle)) {
822 if (strpos($_SERVER['HTTP_USER_AGENT'], 'Googlebot') !== false) {
823 @ini_set('session.use_trans_sid', '0'); // try and turn off trans_sid
824 $CFG->usesid=false;
825 return;
57f7b7ce 826 }
0ad6b20c 827 if (strpos($_SERVER['HTTP_USER_AGENT'], 'google.com') !== false) {
57f7b7ce 828 @ini_set('session.use_trans_sid', '0'); // try and turn off trans_sid
829 $CFG->usesid=false;
830 return;
831 }
832 }
0ad6b20c 833 if (strpos($_SERVER['HTTP_USER_AGENT'], 'W3C_Validator') !== false) {
834 @ini_set('session.use_trans_sid', '0'); // try and turn off trans_sid
835 $CFG->usesid=false;
836 return;
837 }
838 }
57f7b7ce 839
0ad6b20c 840 @ini_set('session.use_trans_sid', '1'); // try and turn on trans_sid
57f7b7ce 841
0ad6b20c 842 if (ini_get('session.use_trans_sid') != 0) {
843 // use trans sid as its available
844 ini_set('url_rewriter.tags', 'a=href,area=href,script=src,link=href,frame=src,form=fakeentry');
845 ob_start('sid_ob_rewrite_absolute');
846 } else {
847 //rewrite all links ourselves
848 ob_start('sid_ob_rewrite');
57f7b7ce 849 }
e6e13284 850}
0ad6b20c 851