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