MDL-12922 restore roll dates - prevent wrong rolls to 0 happening to teachers. Merged...
[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 */
0ad6b20c 7function get_session() {
8 static $session = null;
9
10 if (is_null($session)) {
11 $session = new moodle_session();
12 }
13
14 return $session;
15}
16
57f7b7ce 17/**
18 * Class handling all session and cookies related stuff.
19 */
20class moodle_session {
57f7b7ce 21 function __construct() {
22 global $CFG;
57f7b7ce 23 $this->prepare_cookies();
24 $this->init_session_storage();
25
26 if (!empty($CFG->usesid) && empty($_COOKIE['MoodleSession'.$CFG->sessioncookie])) {
0ad6b20c 27 sid_start_ob();
57f7b7ce 28 }
29
0ad6b20c 30 if (NO_MOODLE_COOKIES) {
31 $_SESSION = array();
32 $_SESSION['SESSION'] = new object();
33 $_SESSION['USER'] = new object();
34
35 } else {
57f7b7ce 36 session_name('MoodleSession'.$CFG->sessioncookie);
e6e13284 37 session_set_cookie_params(0, $CFG->sessioncookiepath, $CFG->sessioncookiedomain, $CFG->cookiesecure, $CFG->cookiehttponly);
57f7b7ce 38 @session_start();
39 if (!isset($_SESSION['SESSION'])) {
40 $_SESSION['SESSION'] = new object();
57f7b7ce 41 }
42 if (!isset($_SESSION['USER'])) {
43 $_SESSION['USER'] = new object();
44 }
57f7b7ce 45 }
57f7b7ce 46
0ad6b20c 47 if (!isset($_SESSION['USER']->id)) {
48 $_SESSION['USER']->id = 0; // to enable proper function of $CFG->notloggedinroleid hack
49 if (isset($CFG->mnet_localhost_id)) {
50 $_SESSION['USER']->mnethostid = $CFG->mnet_localhost_id;
57f7b7ce 51 }
52 }
53 }
54
57f7b7ce 55 /**
56 * Terminates active moodle session
57 */
58 public function terminate() {
59 global $CFG, $SESSION, $USER;
60
0ad6b20c 61 $_SESSION = array();
57f7b7ce 62
0ad6b20c 63 $SESSION = new object();
57f7b7ce 64 $USER = new object();
65 $USER->id = 0;
66 if (isset($CFG->mnet_localhost_id)) {
67 $USER->mnethostid = $CFG->mnet_localhost_id;
68 }
69
0ad6b20c 70 // Initialize variable to pass-by-reference to headers_sent(&$file, &$line)
71 $file = null;
72 $line = null;
73 if (headers_sent($file, $line)) {
74 error_log('Can not terminate session properly - headers were already sent in file: '.$file.' on line '.$line);
57f7b7ce 75 } else {
0ad6b20c 76 // TODO: regenerate session ID here
542797b4 77
57f7b7ce 78 }
57f7b7ce 79
0ad6b20c 80 @session_write_close();
57f7b7ce 81 }
82
83 /**
84 * Prepare cookies and varions system settings
85 */
86 private function prepare_cookies() {
87 global $CFG, $nomoodlecookie;
88
89 if (!defined('NO_MOODLE_COOKIES')) {
90 if (isset($nomoodlecookie)) {
91 // backwards compatibility only
92 define('NO_MOODLE_COOKIES', $nomoodlecookie);
93 unset($nomoodlecookie);
94 } else {
95 define('NO_MOODLE_COOKIES', false);
96 }
97 }
98
99 if (!isset($CFG->cookiesecure) or strpos($CFG->wwwroot, 'https://') !== 0) {
100 $CFG->cookiesecure = 0;
101 }
102
103 if (!isset($CFG->cookiehttponly)) {
104 $CFG->cookiehttponly = 0;
105 }
106
107 /// Set sessioncookie and sessioncookiepath variable if it isn't already
108 if (!isset($CFG->sessioncookie)) {
109 $CFG->sessioncookie = '';
110 }
e6e13284 111 if (!isset($CFG->sessioncookiedomain)) {
112 $CFG->sessioncookiedomain = '';
113 }
57f7b7ce 114 if (!isset($CFG->sessioncookiepath)) {
115 $CFG->sessioncookiepath = '/';
116 }
117
118 //discard session ID from POST, GET and globals to tighten security,
119 //this session fixation prevention can not be used in cookieless mode
120 if (empty($CFG->usesid)) {
121 unset(${'MoodleSession'.$CFG->sessioncookie});
122 unset($_GET['MoodleSession'.$CFG->sessioncookie]);
123 unset($_POST['MoodleSession'.$CFG->sessioncookie]);
124 }
125 //compatibility hack for Moodle Cron, cookies not deleted, but set to "deleted" - should not be needed with NO_MOODLE_COOKIES in cron.php now
126 if (!empty($_COOKIE['MoodleSession'.$CFG->sessioncookie]) && $_COOKIE['MoodleSession'.$CFG->sessioncookie] == "deleted") {
127 unset($_COOKIE['MoodleSession'.$CFG->sessioncookie]);
128 }
57f7b7ce 129 }
130
131 /**
132 * Inits session storage.
133 */
134 private function init_session_storage() {
135 global $CFG;
136
137 /// Set up session handling
138 if(empty($CFG->respectsessionsettings)) {
139 if (true) { /// File-based sessions
140 // Some distros disable GC by setting probability to 0
141 // overriding the PHP default of 1
142 // (gc_probability is divided by gc_divisor, which defaults to 1000)
143 if (ini_get('session.gc_probability') == 0) {
144 ini_set('session.gc_probability', 1);
145 }
146
147 if (!empty($CFG->sessiontimeout)) {
148 ini_set('session.gc_maxlifetime', $CFG->sessiontimeout);
149 }
150
151 if (!file_exists($CFG->dataroot .'/sessions')) {
152 make_upload_directory('sessions');
153 }
154 ini_set('session.save_path', $CFG->dataroot .'/sessions');
155
156 } else { /// Database sessions
157 // TODO: implement proper database session storage
158 }
159 }
160 }
0ad6b20c 161}
57f7b7ce 162
0ad6b20c 163/**
164 * Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
165 * if one does not already exist, but does not overwrite existing sesskeys. Returns the
166 * sesskey string if $USER exists, or boolean false if not.
167 *
168 * @uses $USER
169 * @return string
170 */
171function sesskey() {
172 global $USER;
57f7b7ce 173
0ad6b20c 174 if(!isset($USER)) {
175 return false;
176 }
57f7b7ce 177
0ad6b20c 178 if (empty($USER->sesskey)) {
179 $USER->sesskey = random_string(10);
180 }
57f7b7ce 181
0ad6b20c 182 return $USER->sesskey;
183}
57f7b7ce 184
57f7b7ce 185
0ad6b20c 186/**
187 * For security purposes, this function will check that the currently
188 * given sesskey (passed as a parameter to the script or this function)
189 * matches that of the current user.
190 *
191 * @param string $sesskey optionally provided sesskey
192 * @return bool
193 */
194function confirm_sesskey($sesskey=NULL) {
195 global $USER;
57f7b7ce 196
0ad6b20c 197 if (!empty($USER->ignoresesskey) || !empty($CFG->ignoresesskey)) {
198 return true;
199 }
57f7b7ce 200
0ad6b20c 201 if (empty($sesskey)) {
202 $sesskey = required_param('sesskey', PARAM_RAW); // Check script parameters
57f7b7ce 203 }
204
0ad6b20c 205 if (!isset($USER->sesskey)) {
206 return false;
57f7b7ce 207 }
0ad6b20c 208
209 return ($USER->sesskey === $sesskey);
210}
211
212/**
213 * Sets a moodle cookie with a weakly encrypted string
214 *
215 * @uses $CFG
216 * @uses DAYSECS
217 * @uses HOURSECS
218 * @param string $thing The string to encrypt and place in a cookie
219 */
220function set_moodle_cookie($thing) {
221 global $CFG;
222
223 if ($thing == 'guest') { // Ignore guest account
224 return;
57f7b7ce 225 }
226
0ad6b20c 227 $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
228
229 $days = 60;
230 $seconds = DAYSECS*$days;
231
232 // no need to set secure or http cookie only here - it is not secret
233 setcookie($cookiename, '', time() - HOURSECS, $CFG->sessioncookiepath, $CFG->sessioncookiedomain);
234 setcookie($cookiename, rc4encrypt($thing), time()+$seconds, $CFG->sessioncookiepath, $CFG->sessioncookiedomain);
235}
236
237/**
238 * Gets a moodle cookie with a weakly encrypted string
239 *
240 * @uses $CFG
241 * @return string
242 */
243function get_moodle_cookie() {
244 global $CFG;
245
246 $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
247
248 if (empty($_COOKIE[$cookiename])) {
249 return '';
250 } else {
251 $thing = rc4decrypt($_COOKIE[$cookiename]);
252 return ($thing == 'guest') ? '': $thing; // Ignore guest account
57f7b7ce 253 }
0ad6b20c 254}
57f7b7ce 255
542797b4 256/**
257 * Is current $USER logged-in-as somebody else?
258 * @return bool
259 */
260function is_loggedinas() {
85f6b737 261 return !empty($_SESSION['USER']->realuser);
542797b4 262}
263
6132768e 264/**
265 * Returns the $USER object ignoring current login-as session
266 * @return object user object
267 */
268function get_real_user() {
269 if (is_loggedinas()) {
270 return $_SESSION['REALUSER'];
271 } else {
272 return $_SESSION['USER'];
273 }
274}
275
542797b4 276/**
277 * Login as another user - no security checks here.
278 * @param int $userid
279 * @param object $context
280 * @return void
281 */
8d1964c4 282function session_loginas($userid, $context) {
542797b4 283 if (is_loggedinas()) {
8d1964c4 284 return;
285 }
286
85f6b737 287 // switch to fresh new $SESSION
288 $_SESSION['REALSESSION'] = $_SESSION['SESSION'];
6132768e 289 $_SESSION['SESSION'] = new object();
8d1964c4 290
85f6b737 291 /// Create the new $USER object with all details and reload needed capabilitites
292 $_SESSION['REALUSER'] = $_SESSION['USER'];
293 $_SESSION['USER'] = get_complete_user_data('id', $userid);
294 $_SESSION['USER']->realuser = $_SESSION['REALUSER']->id;
295 $_SESSION['USER']->loginascontext = $context;
296
297 check_enrolment_plugins($_SESSION['USER']);
298 load_all_capabilities();
8d1964c4 299}
300
542797b4 301/**
302 * Terminate login-as session
303 * @return void
304 */
8d1964c4 305function session_unloginas() {
542797b4 306 if (!is_loggedinas()) {
8d1964c4 307 return;
308 }
309
6132768e 310 $_SESSION['SESSION'] = $_SESSION['REALSESSION'];
311 unset($_SESSION['REALSESSION']);
8d1964c4 312
6132768e 313 $_SESSION['USER'] = $_SESSION['REALUSER'];
314 unset($_SESSION['REALUSER']);
8d1964c4 315}
316
0ad6b20c 317/**
318* Enable cookieless sessions by including $CFG->usesid=true;
319* in config.php.
320* Based on code from php manual by Richard at postamble.co.uk
321* 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.
322* 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.
323* This doesn't require trans_sid to be turned on but this is recommended for better performance
324* you should put :
325* session.use_trans_sid = 1
326* in your php.ini file and make sure that you don't have a line like this in your php.ini
327* session.use_only_cookies = 1
328* @author Richard at postamble.co.uk and Jamie Pratt
329* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
330*/
331/**
332* You won't call this function directly. This function is used to process
333* text buffered by php in an output buffer. All output is run through this function
334* before it is ouput.
335* @param string $buffer is the output sent from php
336* @return string the output sent to the browser
337*/
338function sid_ob_rewrite($buffer){
339 $replacements = array(
340 '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*")([^"]*)(")/i',
341 '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*\')([^\']*)(\')/i');
342
343 $buffer = preg_replace_callback($replacements, 'sid_rewrite_link_tag', $buffer);
344 $buffer = preg_replace('/<form\s[^>]*>/i',
345 '\0<input type="hidden" name="' . session_name() . '" value="' . session_id() . '"/>', $buffer);
346
347 return $buffer;
348}
349/**
350* You won't call this function directly. This function is used to process
351* text buffered by php in an output buffer. All output is run through this function
352* before it is ouput.
353* This function only processes absolute urls, it is used when we decide that
354* php is processing other urls itself but needs some help with internal absolute urls still.
355* @param string $buffer is the output sent from php
356* @return string the output sent to the browser
357*/
358function sid_ob_rewrite_absolute($buffer){
359 $replacements = array(
360 '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*")((?:http|https)[^"]*)(")/i',
361 '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*\')((?:http|https)[^\']*)(\')/i');
362
363 $buffer = preg_replace_callback($replacements, 'sid_rewrite_link_tag', $buffer);
364 $buffer = preg_replace('/<form\s[^>]*>/i',
365 '\0<input type="hidden" name="' . session_name() . '" value="' . session_id() . '"/>', $buffer);
366 return $buffer;
367}
57f7b7ce 368
0ad6b20c 369/**
370* A function to process link, a and script tags found
371* by preg_replace_callback in {@link sid_ob_rewrite($buffer)}.
372*/
373function sid_rewrite_link_tag($matches){
374 $url = $matches[4];
375 $url = sid_process_url($url);
376 return $matches[1].$url.$matches[5];
377}
378
379/**
380* You can call this function directly. This function is used to process
381* urls to add a moodle session id to the url for internal links.
382* @param string $url is a url
383* @return string the processed url
384*/
385function sid_process_url($url) {
386 global $CFG;
387
388 if ((preg_match('/^(http|https):/i', $url)) // absolute url
389 && ((stripos($url, $CFG->wwwroot)!==0) && stripos($url, $CFG->httpswwwroot)!==0)) { // and not local one
390 return $url; //don't attach sessid to non local urls
391 }
392 if ($url[0]=='#' || (stripos($url, 'javascript:')===0)) {
393 return $url; //don't attach sessid to anchors
394 }
395 if (strpos($url, session_name())!==FALSE) {
396 return $url; //don't attach sessid to url that already has one sessid
397 }
398 if (strpos($url, "?")===FALSE) {
399 $append = "?".strip_tags(session_name() . '=' . session_id());
400 } else {
401 $append = "&amp;".strip_tags(session_name() . '=' . session_id());
57f7b7ce 402 }
0ad6b20c 403 //put sessid before any anchor
404 $p = strpos($url, "#");
405 if ($p!==FALSE){
406 $anch = substr($url, $p);
407 $url = substr($url, 0, $p).$append.$anch ;
408 } else {
409 $url .= $append ;
410 }
411 return $url;
412}
57f7b7ce 413
0ad6b20c 414/**
415* Call this function before there has been any output to the browser to
416* buffer output and add session ids to all internal links.
417*/
418function sid_start_ob(){
419 global $CFG;
420 //don't attach sess id for bots
421
422 if (!empty($_SERVER['HTTP_USER_AGENT'])) {
423 if (!empty($CFG->opentogoogle)) {
424 if (strpos($_SERVER['HTTP_USER_AGENT'], 'Googlebot') !== false) {
425 @ini_set('session.use_trans_sid', '0'); // try and turn off trans_sid
426 $CFG->usesid=false;
427 return;
57f7b7ce 428 }
0ad6b20c 429 if (strpos($_SERVER['HTTP_USER_AGENT'], 'google.com') !== false) {
57f7b7ce 430 @ini_set('session.use_trans_sid', '0'); // try and turn off trans_sid
431 $CFG->usesid=false;
432 return;
433 }
434 }
0ad6b20c 435 if (strpos($_SERVER['HTTP_USER_AGENT'], 'W3C_Validator') !== false) {
436 @ini_set('session.use_trans_sid', '0'); // try and turn off trans_sid
437 $CFG->usesid=false;
438 return;
439 }
440 }
57f7b7ce 441
0ad6b20c 442 @ini_set('session.use_trans_sid', '1'); // try and turn on trans_sid
57f7b7ce 443
0ad6b20c 444 if (ini_get('session.use_trans_sid') != 0) {
445 // use trans sid as its available
446 ini_set('url_rewriter.tags', 'a=href,area=href,script=src,link=href,frame=src,form=fakeentry');
447 ob_start('sid_ob_rewrite_absolute');
448 } else {
449 //rewrite all links ourselves
450 ob_start('sid_ob_rewrite');
57f7b7ce 451 }
e6e13284 452}
0ad6b20c 453