Commit 11e7b506 authored by skodak's avatar skodak
Browse files

MDL-17754, MDL-11061, MDL-9276, MDL-17777, MDL-17787 - various url handling...

MDL-17754, MDL-11061, MDL-9276, MDL-17777, MDL-17787 - various url handling improvemetns; rewritten $FULLME and added $ME, $SCRIPT and $FULLSCRIPT; improved slashargument handling especially for IIS; removed legacy slasharguments functions obsoleted since 1.6; some other refactoring; reverse procy support, ssl appliance support
parent 98435117
......@@ -17,9 +17,8 @@
/// we do not want HTML in output and there is no real session ;-)
define('CLI_SCRIPT', true);
/// The following is a hack necessary to allow this script to work well
/// from the command line.
define('FULLME', 'cron'); //TODO: to be removed soon ;-)
/// Following hack used to identify cron and other CLI scripts - use CLI_SCRIPT or $FULLME == "/$CFG->admin/cropn.php" instead
//define('FULLME', 'cron');
/// Do not set moodle cookie because we do not need it here, it is better to emulate session
define('NO_MOODLE_COOKIES', true);
......
......@@ -38,7 +38,7 @@ class block_tags extends block_base {
function get_content() {
global $CFG, $SITE, $COURSE, $USER;
global $CFG, $SITE, $COURSE, $USER, $SCRIPT;
if (empty($CFG->usetags)) {
$this->content->text = '';
......@@ -80,7 +80,7 @@ class block_tags extends block_base {
$loggedin = isloggedin() && !$isguest;
$coursepage = $canedit = false;
$coursepage = (isset($COURSE->id) && $COURSE->id != SITEID);
$mymoodlepage = strpos($_SERVER['PHP_SELF'], 'my') > 0 ? true : false;
$mymoodlepage = ($SCRIPT == '/my/index.php') ? true : false;
$sitepage = (isset($COURSE->id) && $COURSE->id == SITEID && !$mymoodlepage);
$coursecontext = get_context_instance(CONTEXT_COURSE, $COURSE->id);
if ($coursepage) {
......
......@@ -141,7 +141,7 @@
* display the entry in its abbreviated format (eg. index page)
*/
function blog_print_entry($blogEntry, $viewtype='full', $filtertype='', $filterselect='', $mode='loud') {
global $USER, $CFG, $COURSE, $ME, $DB;
global $USER, $CFG, $COURSE, $DB;
$template['body'] = format_text($blogEntry->summary, $blogEntry->format);
$template['title'] = '<a id=b"'. s($blogEntry->id) .'" />';
......
......@@ -68,6 +68,9 @@ $CFG->dbpersist = false; // Should database connections be reused?
// web address to where moodle has been installed. If your web site
// is accessible via multiple URLs then choose the most natural one
// that your students would use. Do not include a trailing slash
//
// If you need both intranet and Internet access please read
// http://docs.moodle.org/en/masquerading
$CFG->wwwroot = 'http://example.com/moodle';
......@@ -193,6 +196,13 @@ $CFG->admin = 'admin';
// $CFG->mailprefix = 'mdl-'; // - is the separator for qmail
// $CFG->maildomain = 'youremaildomain.com';
//
// Enable when setting up advanced reverse proxy load balancing configurations.
// $CFG->reverseproxy = true;
//
// Enable when using external SSL appliance for performance reasons.
// Please note that site may be accessible via https: or https:, but not both!
// $CFG->sslproxy = true;
//
// The following setting will tell Moodle to respect your PHP session
// settings. Use this if you want to control session configuration
// from php.ini, httpd.conf or .htaccess files.
......
......@@ -286,7 +286,7 @@ function checknos() {
echo '<form action="'.$CFG->wwwroot.'/user/action_redir.php" method="post" id="studentsform" onsubmit="return checksubmit(this);">'."\n";
echo '<div>'."\n";
echo '<input type="hidden" name="id" value="'.$id.'" />'."\n";
echo '<input type="hidden" name="returnto" value="'. format_string($_SERVER['REQUEST_URI']) .'" />'."\n";
echo '<input type="hidden" name="returnto" value="'. s($FULLME) .'" />'."\n";
echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />'."\n";
foreach ($users as $u) {
......
......@@ -11,7 +11,7 @@
// disable moodle specific debug messages
disable_debugging();
$relativepath = get_file_argument('draftfile.php');
$relativepath = get_file_argument();
// relative path must start with '/'
if (!$relativepath) {
......
......@@ -27,7 +27,7 @@
// disable moodle specific debug messages
disable_debugging();
$relativepath = get_file_argument('file.php');
$relativepath = get_file_argument();
$forcedownload = optional_param('forcedownload', 0, PARAM_BOOL);
// relative path must start with '/', because of backup/restore!!!
......
......@@ -27,7 +27,7 @@
//error_reporting(E_ALL);
$relativepath = get_file_argument('pix.php');
$relativepath = get_file_argument();
$args = explode('/', trim($relativepath, '/'));
......
......@@ -28,7 +28,7 @@ define('NO_MOODLE_COOKIES', true); // Because it interferes with caching
error_reporting(E_ALL);
$relativepath = get_file_argument('pix.php');
$relativepath = get_file_argument();
$args = explode('/', trim($relativepath, '/'));
......
......@@ -180,7 +180,7 @@ foreach($supported as $driver) {
/// guess the www root
if ($INSTALL['wwwroot'] == '') {
list($INSTALL['wwwroot'], $xtra) = explode('/install.php', qualified_me());
$INSTALL['wwwroot'] = install_guess_wwwroot();
$INSTALL['wwwrootform'] = $INSTALL['wwwroot'];
// now try to guess the correct dataroot not accessible via web
......
......@@ -403,6 +403,7 @@ $string['sessionipnomatch'] = 'Sorry, but your IP number seems to have changed f
$string['sessionipnomatch2'] = 'Sorry, but your IP number seems to have changed from when you first logged in. This security feature prevents crackers stealing your identity while logged in to this site. You may see this error if you use wireless networks or if you are roaming between different networks. Please ask the site administrator for more help.<br /><br />If you want to continue please press F5 key to refresh this page.';
$string['socksnotsupported'] = 'SOCKS5 proxy is not supported in PHP4';
$string['spellcheckernotconf'] = 'Spellchecker not configured';
$string['sslonlyaccess'] = 'For security reasons only https connections are allowed, sorry.';
$string['statscatchupmode'] = 'Statistics is currently in catchup mode. So far $a->daysdone day(s) have been processed and $a->dayspending are pending. Check back soon!';
$string['storedfilecannotcreatefiledirs'] = 'Can not create local file pool directories, please verify permissions in dataroot.';
$string['storedfilecannotread'] = 'Can not read file, either file does not exist or there are permission problems';
......@@ -440,6 +441,7 @@ $string['unknowquestiontype'] = 'Unsupported question type $a';
$string['unknowuploadaction'] = 'Error: Unknown upload action ($a)';
$string['unsupportedevent'] = 'Unsupported event type';
$string['unsupportedstate'] = 'Unsupported completion state';
$string['usupportedwebserver'] = 'Web server software ($a) is not suported, sorry.';
$string['upgraderequires19'] = 'Error: New Moodle version was installed on server, unfortunately upgrade from the previous version is not supported.<br />Please upgrade first to latest 1.9.x release. You can also return to previous version by reinstalling original files.';
$string['urlnotdefinerss'] = 'URL not defined for RSS feed';
$string['userautherror'] = 'Unknown auth plugin';
......@@ -473,6 +475,7 @@ $string['wrongdestpath'] = 'Wrong destination path';
$string['wrongroleid'] = 'Incorrect role ID!';
$string['wrongsourcebase'] = 'Wrong source URL base';
$string['wrongzipfilename'] = 'Wrong ZIP file name';
$string['wwwrootmismatch'] = 'Incorrect access detected, this server may be accessed only through \"$a\" address, sorry.<br />Please notify server administrator.';
$string['xmldberror'] = 'XMLDB error!';
$string['youcannotdeletecategory'] = 'You cannot delete category \'$a\' becuase you can neither delete the contents, nor move them elsewhere.';
......
......@@ -32,7 +32,7 @@ function get_file_url($path, $options=null, $type='coursefile') {
$url = $wwwroot."/user/pix.php";
break;
case 'usergroup':
$url = $CFG->wwwroot."/user/pixgroup.php";
$url = $CFG->wwwroot."/user/grouppix.php";
break;
case 'httpscoursefile':
$url = $CFG->httpswwwroot."/file.php";
......
......@@ -229,11 +229,12 @@ class phpFlickr {
function auth ($perms = "write", $remember_uri = true)
{
global $FULLME;
// Redirects to Flickr's authentication piece if there is no valid token.
// If remember_uri is set to false, the callback script (included) will
// redirect to its default page.
if ($remember_uri) {
$redirect = $_SERVER['REQUEST_URI'];
$redirect = $FULLME;
}
$api_sig = md5($this->secret . "api_key" . $this->api_key . "perms" . $perms);
$url = 'http://www.flickr.com/services/auth/?api_key=' . $this->api_key . "&perms=" . $perms . '&api_sig='. $api_sig;
......
......@@ -4,6 +4,30 @@
* Functions to support installation process
*/
/**
*Tries to detect the right www root setting.
*
* @return string detected www root
*/
function install_guess_wwwroot() {
$wwwroot = '';
if (empty($_SERVER['HTTPS']) or $_SERVER['HTTPS'] == 'off') {
$wwwroot .= 'http://';
} else {
$wwwroot .= 'https://';
}
$hostport = explode(':', $_SERVER['HTTP_HOST']);
$wwwroot .= reset($hostport);
if ($_SERVER['SERVER_PORT'] != 80 and $_SERVER['SERVER_PORT'] != '443') {
$wwwroot .= ':'.$_SERVER['SERVER_PORT'];
}
$wwwroot .= $_SERVER['SCRIPT_NAME'];
list($wwwroot, $xtra) = explode('/install.php', $wwwroot);
return $wwwroot;
}
/**
* This function returns a list of languages and their full names. The
* list of available languages is fetched from install/lang/xx/installer.php
......
......@@ -46,6 +46,8 @@ class portfolio_export_exception extends portfolio_exception {
* @param mixed $a language string data (optional, defaults to null)
*/
public function __construct($exporter, $errorcode, $module=null, $continue=null, $a=null) {
global $SCRIPT, $CFG;
if (!empty($exporter) && $exporter instanceof portfolio_exporter) {
if (empty($continue)) {
$caller = $exporter->get('caller');
......@@ -53,7 +55,7 @@ class portfolio_export_exception extends portfolio_exception {
$continue = $exporter->get('caller')->get_return_url();
}
}
if (!defined('FULLME') || FULLME != 'cron') { // TODO: this is not nice at all
if ($SCRIPT == "/$CFG->admin/cron.php") {
$exporter->process_stage_cleanup();
}
} else {
......
......@@ -118,9 +118,6 @@ class moodle_session {
print_error('sessionipnomatch2', 'error');
}
}
// TODO: add wwwroot check here
}
/**
......@@ -171,7 +168,7 @@ class moodle_session {
}
unset($nomoodlecookie); // cleanup
if (!isset($CFG->cookiesecure) or strpos($CFG->wwwroot, 'https://') !== 0) {
if (!isset($CFG->cookiesecure) or (strpos($CFG->wwwroot, 'https://') !== 0 and empty($CFG->sslproxy))) {
$CFG->cookiesecure = 0;
}
......
......@@ -77,6 +77,14 @@ global $THEME;
*/
global $HTTPSPAGEREQUIRED;
/** Full script path including all params, slash arguments, scheme and host.*/
global $FULLME;
/** Script path including query string and slash arguments without host. */
global $ME;
/** $FULLME without slasharguments and query string.*/
global $FULLSCRIPT;
/** Relative moodle script path "/course/view.php" */
global $SCRIPT;
/// First try to detect some attacks on older buggy PHP versions
if (isset($_REQUEST['GLOBALS']) || isset($_COOKIE['GLOBALS']) || isset($_FILES['GLOBALS'])) {
......@@ -404,13 +412,7 @@ global $HTTPSPAGEREQUIRED;
}
/// initialise ME's
if (defined('FULLME')) { // Usually in command-line scripts like admin/cron.php
$FULLME = FULLME;
$ME = FULLME;
} else {
$FULLME = qualified_me();
$ME = strip_querystring($FULLME);
}
initialise_fullme();
/// start session and prepare global $SESSION, $USER
session_get_instance();
......
......@@ -84,6 +84,106 @@ function default_exception_handler($ex) {
}
}
/**
* Initialises $FULLME and friends.
* @return void
*/
function initialise_fullme() {
global $CFG, $FULLME, $ME, $SCRIPT, $FULLSCRIPT;
$url = parse_url($CFG->wwwroot);
if (CLI_SCRIPT) {
// urls do not make much sense in CLI scripts
$backtrace = debug_backtrace();
$topfile = array_pop($backtrace);
$topfile = realpath($topfile['file']);
$dirroot = realpath($CFG->dirroot);
if (strpos($topfile, $dirroot) !== 0) {
$SCRIPT = $FULLSCRIPT = $FULLME = $ME = null;
} else {
$relme = substr($topfile, strlen($dirroot));
$relme = str_replace('\\', '/', $relme); // Win fix
$SCRIPT = $FULLSCRIPT = $FULLME = $ME = $relme;
}
return;
}
$rurl = array();
$hostport = explode(':', $_SERVER['HTTP_HOST']);
$rurl['host'] = reset($hostport);
$rurl['port'] = $_SERVER['SERVER_PORT'];
$rurl['path'] = $_SERVER['SCRIPT_NAME']; // script path without slash arguments
if (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false) {
//Apache server
$rurl['scheme'] = empty($_SERVER['HTTPS']) ? 'http' : 'https';
$rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
} else if (stripos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false) {
//lighttpd
$rurl['scheme'] = empty($_SERVER['HTTPS']) ? 'http' : 'https';
$rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
} else if (stripos($_SERVER['SERVER_SOFTWARE'], 'iis') !== false) {
//IIS
$rurl['scheme'] = ($_SERVER['HTTPS'] == 'off') ? 'http' : 'https';
$rurl['fullpath'] = $_SERVER['SCRIPT_NAME'];
// NOTE: ignore PATH_INFO because it is incorrectly encoded using 8bit filesystem legacy encoding in IIS
// since 2.0 we rely on iis rewrite extenssion like Helicon ISAPI_rewrite
// example rule: RewriteRule ^([^\?]+\.php)(\/.+)$ $1\?file=$2 [QSA]
if ($_SERVER['QUERY_STRING'] != '') {
// iis is decoding the query string, let's reencode it in order to emulate QUERY_STRING in Apache
// TODO: move this into lib/setup.php
$encoded = urlencode($_SERVER['QUERY_STRING']);
$encoded = str_replace(urlencode('='), '=', $encoded);
$encoded = str_replace(urlencode('%'), '%', $encoded);
$encoded = str_replace(urlencode('&'), '&', $encoded);
$rurl['fullpath'] .= '?'.$encoded;
}
$_SERVER['REQUEST_URI'] = $rurl['fullpath']; // extra IIS compatibility
} else {
print_error('usupportedwebserver', 'error', '', $_SERVER['SERVER_SOFTWARE']);
}
if (strpos($rurl['path'], $url['path']) === 0) {
$SCRIPT = substr($rurl['path'], strlen($url['path']));
} else {
// probably some weird external script
$SCRIPT = $FULLSCRIPT = $FULLME = $ME = null;
return;
}
// $CFG->sslproxy specifies if external SSL apliance is used (server using http, ext box translating everything to https)
if (empty($CFG->sslproxy)) {
if ($rurl['scheme'] == 'http' and $url['scheme'] == 'https') {
print_error('sslonlyaccess', 'error');
}
}
// $CFG->reverseproxy specifies if reverse proxy server used - used in advanced load balancing setups only!
// this is not supposed to solve lan/wan access problems!!!!!
if (empty($CFG->reverseproxy)) {
if (($rurl['host'] != $url['host']) or (!empty($url['port']) and $rurl['port'] != $url['port'])) {
print_error('wwwrootmismatch', 'error', '', $CFG->wwwroot);
}
}
$FULLME = $rurl['scheme'].'://'.$url['host'];
if (!empty($url['port'])) {
$FULLME .= ':'.$url['port'];
}
$FULLSCRIPT = $FULLME.$rurl['path'];
$FULLME = $FULLME.$rurl['fullpath'];
$ME = $rurl['fullpath'];
}
/**
* Initializes our performance info early.
*
......@@ -130,7 +230,7 @@ function init_performance_info() {
* @param string $newlimit the new memory limit
* @return bool
*/
function raise_memory_limit ($newlimit) {
function raise_memory_limit($newlimit) {
if (empty($newlimit)) {
return false;
......@@ -166,6 +266,7 @@ function get_real_size($size=0) {
if (!$size) {
return 0;
}
$scan = array();
$scan['MB'] = 1048576;
$scan['Mb'] = 1048576;
$scan['M'] = 1048576;
......
......@@ -215,32 +215,8 @@ function get_referer($stripquery=true) {
* @return string
*/
function me() {
if (!empty($_SERVER['REQUEST_URI'])) {
return $_SERVER['REQUEST_URI'];
} else if (!empty($_SERVER['PHP_SELF'])) {
if (!empty($_SERVER['QUERY_STRING'])) {
return $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
}
return $_SERVER['PHP_SELF'];
} else if (!empty($_SERVER['SCRIPT_NAME'])) {
if (!empty($_SERVER['QUERY_STRING'])) {
return $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
}
return $_SERVER['SCRIPT_NAME'];
} else if (!empty($_SERVER['URL'])) { // May help IIS (not well tested)
if (!empty($_SERVER['QUERY_STRING'])) {
return $_SERVER['URL'] .'?'. $_SERVER['QUERY_STRING'];
}
return $_SERVER['URL'];
} else {
notify('Warning: Could not find any of these web server variables: $REQUEST_URI, $PHP_SELF, $SCRIPT_NAME or $URL');
return false;
}
global $ME;
return $ME;
}
/**
......@@ -249,49 +225,8 @@ function get_referer($stripquery=true) {
* @return string
*/
function qualified_me() {
global $CFG;
if (!empty($CFG->wwwroot)) {
$url = parse_url($CFG->wwwroot);
}
if (!empty($url['host'])) {
$hostname = $url['host'];
} else if (!empty($_SERVER['SERVER_NAME'])) {
$hostname = $_SERVER['SERVER_NAME'];
} else if (!empty($_ENV['SERVER_NAME'])) {
$hostname = $_ENV['SERVER_NAME'];
} else if (!empty($_SERVER['HTTP_HOST'])) {
$hostname = $_SERVER['HTTP_HOST'];
} else if (!empty($_ENV['HTTP_HOST'])) {
$hostname = $_ENV['HTTP_HOST'];
} else {
notify('Warning: could not find the name of this server!');
return false;
}
if (!empty($url['port'])) {
$hostname .= ':'.$url['port'];
} else if (!empty($_SERVER['SERVER_PORT'])) {
if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
$hostname .= ':'.$_SERVER['SERVER_PORT'];
}
}
// TODO, this does not work in the situation described in MDL-11061, but
// I don't know how to fix it. Possibly believe $CFG->wwwroot ahead of what
// the server reports.
if (isset($_SERVER['HTTPS'])) {
$protocol = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
} else if (isset($_SERVER['SERVER_PORT'])) { # Apache2 does not export $_SERVER['HTTPS']
$protocol = ($_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://';
} else {
$protocol = 'http://';
}
$url_prefix = $protocol.$hostname;
return $url_prefix . me();
global $FULLME;
return $FULLME;
}
......@@ -319,10 +254,10 @@ class moodle_url {
* @param array $params these params override anything in the query string where params have the same name.
*/
function moodle_url($url = null, $params = array()){
global $FULLME;
global $ME;
if ($url !== ''){
if ($url === null){
$url = strip_querystring($FULLME);
$url = $ME;
}
$parts = parse_url($url);
if ($parts === FALSE){
......@@ -1216,98 +1151,28 @@ function validate_email($address) {
/**
* Extracts file argument either from file parameter or PATH_INFO
* Note: $scriptname parameter is not needed anymore
*
* @param string $scriptname name of the calling script
* @return string file path (only safe characters)
*/
function get_file_argument($scriptname) {
global $_SERVER;
$relativepath = FALSE;
function get_file_argument() {
global $SCRIPT;
// first try normal parameter (compatible method == no relative links!)
$relativepath = optional_param('file', FALSE, PARAM_PATH);
if ($relativepath === '/testslasharguments') {
echo 'test -1 : Incorrect use - try "file.php/testslasharguments" instead'; //indicate fopen/fread works for health center
die;
}
// then try extract file from PATH_INFO (slasharguments method)
if (!$relativepath and !empty($_SERVER['PATH_INFO'])) {
$path_info = $_SERVER['PATH_INFO'];
if ($relativepath === false and isset($_SERVER['PATH_INFO']) and $_SERVER['PATH_INFO'] !== '') {
// check that PATH_INFO works == must not contain the script name
if (!strpos($path_info, $scriptname)) {
$relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
if ($relativepath === '/testslasharguments') {
echo 'test 1 : Slasharguments test passed. Server confguration is compatible with file.php/1/pic.jpg slashargument setting.'; //indicate ok for health center
die;
}
if (strpos($_SERVER['PATH_INFO'], $SCRIPT) === false) {
$relativepath = clean_param(urldecode($_SERVER['PATH_INFO']), PARAM_PATH);
}
}
// now if both fail try the old way
// (for compatibility with misconfigured or older buggy php implementations)
if (!$relativepath) {
$arr = explode($scriptname, me());
if (!empty($arr[1])) {
$path_info = strip_querystring($arr[1]);
$relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
if ($relativepath === '/testslasharguments') {
echo 'test 2 : Slasharguments test passed (compatibility hack). Server confguration may be compatible with file.php/1/pic.jpg slashargument setting'; //indicate ok for health center
die;
}
}
}
// note: we are not using any other way because they are not compatible with unicode file names ;-)
return $relativepath;
}
/**
* Searches the current environment variables for some slash arguments
*
* @param string $file ?
* @todo Finish documenting this function
*/
function get_slash_arguments($file='file.php') {
if (!$string = me()) {
return false;
}
$pathinfo = explode($file, $string);
if (!empty($pathinfo[1])) {
return $pathinfo[1];
} else {
return false;
}
}
/**
* Extracts arguments from "/foo/bar/something"
* eg http://mysite.com/script.php/foo/bar/something
*
* @param string $string ?
* @param int $i ?
* @return array|string
* @todo Finish documenting this function
*/
function parse_slash_arguments($string, $i=0) {
if (detect_munged_arguments($string)) {
return false;
}
$args = explode('/', $string);
if ($i) { // return just the required argument
return $args[$i];
} else { // return the whole array
array_shift($args); // get rid of the empty first one
return $args;
}
}
/**
* Just returns an array of text formats suitable for a popup menu
*
......@@ -3086,11 +2951,11 @@ function print_footer($course=NULL, $usercourse=NULL, $return=false) {
* @uses $USER
* @uses $SESSION
* @uses $COURSE
* @uses $FULLME
* @uses $SCRIPT
* @return string
*/
function current_theme() {
global $CFG, $USER, $SESSION, $COURSE, $FULLME;
global $CFG, $USER, $SESSION, $COURSE, $SCRIPT;
if (empty($CFG->themeorder)) {
$themeorder = array('page', 'course', 'category', 'session', 'user', 'site');
......@@ -3123,7 +2988,7 @@ function current_theme() {
case 'category':
if (!empty($CFG->allowcategorythemes)) {