MDL-25314 improved prevention of output buffering + detection of misconfigured servers
authorPetr Skoda <skodak@moodle.org>
Fri, 19 Nov 2010 03:40:43 +0000 (03:40 +0000)
committerPetr Skoda <skodak@moodle.org>
Fri, 19 Nov 2010 03:40:43 +0000 (03:40 +0000)
Scripts that do not want buffered output just define NO_OUTPUT_BUFFERING before including config.php.
The fileserving code now checks if the headers are already sent which detects misconfigured servers.

18 files changed:
admin/bloglevelupgrade.php
admin/cron.php
admin/index.php
admin/multilangupgrade.php
admin/replace.php
admin/report/security/index.php
admin/report/unittest/dbtest.php
admin/report/unittest/index.php
calendar/export_execute.php
lang/en/error.php
lib/adminlib.php
lib/filelib.php
lib/moodlelib.php
lib/setup.php
lib/setuplib.php
mod/chat/gui_header_js/jsupdated.php
search/indexer.php
search/tests/index.php

index 3a08d45..7d20ef5 100644 (file)
@@ -1,6 +1,8 @@
 <?php
       /// Create "blog" forums in each course and copy blog entries from these courses' participants in these forums
 
+define('NO_OUTPUT_BUFFERING', true);
+
 require_once('../config.php');
 require_once($CFG->dirroot.'/course/lib.php');
 require_once($CFG->dirroot.'/blog/lib.php');
@@ -28,9 +30,7 @@ echo $OUTPUT->box_start();
 
 /// Turn off time limits, sometimes upgrades can be slow.
 
-@set_time_limit(0);
-@ob_implicit_flush(true);
-while(@ob_end_flush());
+set_time_limit(0);
 
 $i = 0;
 
index 51b34ae..e131e9b 100644 (file)
 // CLI via web interface, please do not use this hack elsewhere
 define('CLI_SCRIPT', true);
 define('WEB_CRON_EMULATED_CLI', 'defined'); // ugly ugly hack, do not use elsewhere please
+define('NO_OUTPUT_BUFFERING', true);
 
 require('../config.php');
 require_once($CFG->libdir.'/clilib.php');
 require_once($CFG->libdir.'/cronlib.php');
 
-// disable compression, it would prevent closing of buffers
-if (ini_get('zlib.output_compression')) {
-    ini_set('zlib.output_compression', 'Off');
-}
-// no more headers and buffers
-ob_implicit_flush(true);
-while(ob_get_level()) {
-    if (!ob_end_clean()) {
-        // prevent infinite loop
-        break;
-    }
-}
-
 // extra safety
 session_get_instance()->write_close();
 
index e70c14c..14b6f24 100644 (file)
@@ -38,19 +38,7 @@ if (version_compare(phpversion(), '5.2.0') < 0) {
     die;
 }
 
-// disable compression, it would prevent closing of buffers
-if (ini_get('zlib.output_compression')) {
-    ini_set('zlib.output_compression', 'Off');
-}
-
-// try to flush everything all the time
-ob_implicit_flush(true);
-while(ob_get_level()) {
-    if (!ob_end_clean()) {
-        // prevent infinite loop
-        break;
-    }
-}
+define('NO_OUTPUT_BUFFERING', true);
 
 require('../config.php');
 require_once($CFG->libdir.'/adminlib.php');    // various admin-only functions
index 453e682..d25c709 100644 (file)
@@ -1,6 +1,8 @@
 <?php
       /// Search and replace strings throughout all texts in the whole database
 
+define('NO_OUTPUT_BUFFERING', true);
+
 require_once('../config.php');
 require_once($CFG->dirroot.'/course/lib.php');
 require_once($CFG->libdir.'/adminlib.php');
@@ -33,8 +35,6 @@ echo $OUTPUT->box_start();
 /// Turn off time limits, sometimes upgrades can be slow.
 
 @set_time_limit(0);
-@ob_implicit_flush(true);
-while(@ob_end_flush());
 
 echo '<strong>Progress:</strong>';
 $i = 0;
index 37b67c0..e77070b 100644 (file)
@@ -1,15 +1,12 @@
 <?php
       /// Search and replace strings throughout all texts in the whole database
 
+define('NO_OUTPUT_BUFFERING', true);
+
 require_once('../config.php');
 require_once($CFG->dirroot.'/course/lib.php');
 require_once($CFG->libdir.'/adminlib.php');
 
-// workaround for problems with compression
-if (ini_get('zlib.output_compression')) {
-    @ini_set('zlib.output_compression', 'Off');
-}
-
 admin_externalpage_setup('replace');
 
 $search  = optional_param('search', '', PARAM_RAW);
index ddb96d7..9b7cfbe 100644 (file)
@@ -23,6 +23,8 @@
 //                                                                       //
 ///////////////////////////////////////////////////////////////////////////
 
+define('NO_OUTPUT_BUFFERING', true);
+
 require_once('../../../config.php');
 require_once($CFG->dirroot.'/'.$CFG->admin.'/report/security/lib.php');
 require_once($CFG->libdir.'/adminlib.php');
@@ -49,8 +51,6 @@ echo $OUTPUT->header();
 echo $OUTPUT->heading(get_string('pluginname', 'report_security'));
 
 echo '<div id="timewarning">'.get_string('timewarning', 'report_security').'</div>';
-while(@ob_end_flush());
-@flush();
 
 $strok       = '<span class="statusok">'.get_string('statusok', 'report_security').'</span>';
 $strinfo     = '<span class="statusinfo">'.get_string('statusinfo', 'report_security').'</span>';
index 8e47256..63060a1 100644 (file)
@@ -4,27 +4,14 @@
  * @package SimpleTestEx
  */
 
-/** */
+define('NO_OUTPUT_BUFFERING', true);
+
 require_once(dirname(__FILE__).'/../../../config.php');
 require_once($CFG->libdir.'/adminlib.php');
 require_once($CFG->libdir.'/simpletestcoveragelib.php');
 require_once('ex_simple_test.php');
 require_once('ex_reporter.php');
 
-// disable compression, it would prevent closing of buffers
-if (ini_get('zlib.output_compression')) {
-    ini_set('zlib.output_compression', 'Off');
-}
-
-// try to flush everything all the time
-ob_implicit_flush(true);
-while(ob_get_level()) {
-    if (!ob_end_clean()) {
-        // prevent infinite loop
-        break;
-    }
-}
-
 $showpasses   = optional_param('showpasses', false, PARAM_BOOL);
 $codecoverage = optional_param('codecoverage', false, PARAM_BOOL);
 $selected     = optional_param('selected', array(), PARAM_INT);
index c576899..c19f4e8 100644 (file)
@@ -8,27 +8,14 @@
  * @package SimpleTestEx
  */
 
-/** */
+define('NO_OUTPUT_BUFFERING', true);
+
 require_once(dirname(__FILE__).'/../../../config.php');
 require_once($CFG->libdir.'/adminlib.php');
 require_once($CFG->libdir.'/simpletestcoveragelib.php');
 require_once('ex_simple_test.php');
 require_once('ex_reporter.php');
 
-// disable compression, it would prevent closing of buffers
-if (ini_get('zlib.output_compression')) {
-    ini_set('zlib.output_compression', 'Off');
-}
-
-// try to flush everything all the time
-ob_implicit_flush(true);
-while(ob_get_level()) {
-    if (!ob_end_clean()) {
-        // prevent infinite loop
-        break;
-    }
-}
-
 // Always run the unit tests in developer debug mode.
 $CFG->debug = DEBUG_DEVELOPER;
 error_reporting($CFG->debug);
index 1c676be..e4d62bd 100644 (file)
@@ -152,7 +152,7 @@ if(empty($serialized)) {
 }
 
 //IE compatibility HACK!
-if(ini_get('zlib.output_compression')) {
+if (ini_get_bool('zlib.output_compression')) {
     ini_set('zlib.output_compression', 'Off');
 }
 
index f1cf1b7..c0241d9 100755 (executable)
@@ -129,6 +129,7 @@ $string['cannotsavedata'] = 'Cannot save data';
 $string['cannotsavefile'] = 'Cannot save the file "{$a}"!';
 $string['cannotsavemd5file'] = 'Cannot save md5 file';
 $string['cannotsavezipfile'] = 'Cannot save ZIP file';
+$string['cannotservefile'] = 'Can not serve file - server configuration problem.';
 $string['cannotsetparentforcatoritem'] = 'Cannot set parent for category or course item!';
 $string['cannotsetpassword'] = 'Could not set user password!';
 $string['cannotsetprefgrade'] = 'Could not set preference aggregationview to {$a} for this grade category';
index d44f3cb..f915258 100644 (file)
@@ -5919,8 +5919,6 @@ function db_replace($search, $replace) {
 
     /// Turn off time limits, sometimes upgrades can be slow.
     @set_time_limit(0);
-    @ob_implicit_flush(true);
-    while(@ob_end_flush());
 
     if (!$tables = $DB->get_tables() ) {    // No tables yet at all.
         return false;
index 2cfa789..23687bf 100644 (file)
@@ -1474,11 +1474,43 @@ function send_file_not_found() {
     print_error('filenotfound', 'error', $CFG->wwwroot.'/course/view.php?id='.$COURSE->id); //this is not displayed on IIS??
 }
 
+/**
+ * Check output buffering settings before sending file
+ * @private to be called only from lib/filelib.php !
+ * @return void
+ */
+function prepare_file_sending() {
+    $olddebug = error_reporting(0);
+
+    // IE compatibility HACK - it does not like zlib compression much
+    // there is also a problem with the length header in older PHP versions
+    if (ini_get_bool('zlib.output_compression')) {
+        ini_set('zlib.output_compression', 'Off');
+    }
+
+    // flush and close all buffers if possible
+    while(ob_get_level()) {
+        if (!ob_end_flush()) {
+            // prevent infinite loop when buffer can not be closed
+            break;
+        }
+    }
+
+    error_reporting($olddebug);
+
+    // now make sure we can actually send out headers,
+    // if not it is a fatal problem because we could
+    // create XSS problems through student files
+    // or the content of the file would be borked.
+    if (headers_sent()) {
+        throw new file_serving_exception('Headers already sent, can not serve file!');
+    }
+}
+
 /**
  * Handles the sending of temporary file to user, download is forced.
  * File is deleted after abort or successful sending.
  *
- * @global object
  * @param string $path path to file, preferably from moodledata/temp/something; or content of file itself
  * @param string $filename proposed file name when saving file
  * @param bool $path is content of file
@@ -1499,32 +1531,31 @@ function send_temp_file($path, $filename, $pathisstring=false) {
         @register_shutdown_function('send_temp_file_finished', $path);
     }
 
-    //IE compatibility HACK!
-    if (ini_get('zlib.output_compression')) {
-        ini_set('zlib.output_compression', 'Off');
-    }
-
     // if user is using IE, urlencode the filename so that multibyte file name will show up correctly on popup
     if (check_browser_version('MSIE')) {
         $filename = urlencode($filename);
     }
 
+    //flush the buffers - save memory and disable sid rewrite
+    // this also disables zlib compression
+    prepare_file_sending();
+
     $filesize = $pathisstring ? strlen($path) : filesize($path);
 
-    @header('Content-Disposition: attachment; filename='.$filename);
-    @header('Content-Length: '.$filesize);
+    header('Content-Disposition: attachment; filename='.$filename);
+    header('Content-Length: '.$filesize);
     if (strpos($CFG->wwwroot, 'https://') === 0) { //https sites - watch out for IE! KB812935 and KB316431
-        @header('Cache-Control: max-age=10');
-        @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
-        @header('Pragma: ');
+        header('Cache-Control: max-age=10');
+        header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
+        header('Pragma: ');
     } else { //normal http - prevent caching at all cost
-        @header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
-        @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
-        @header('Pragma: no-cache');
+        header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
+        header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
+        header('Pragma: no-cache');
     }
-    @header('Accept-Ranges: none'); // Do not allow byteserving
+    header('Accept-Ranges: none'); // Do not allow byteserving
 
-    while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+    // send the contents
     if ($pathisstring) {
         echo $path;
     } else {
@@ -1609,17 +1640,15 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
     }
 */
 
-    //IE compatibiltiy HACK!
-    if (ini_get('zlib.output_compression')) {
-        ini_set('zlib.output_compression', 'Off');
-    }
-
     //try to disable automatic sid rewrite in cookieless mode
     @ini_set("session.use_trans_sid", "false");
 
+    //flush the buffers - save memory and disable sid rewrite
+    //this also disables zlib compression
+    prepare_file_sending();
+
     //do not put '@' before the next header to detect incorrect moodle configurations,
     //error should be better than "weird" empty lines for admins/users
-    //TODO: should we remove all those @ before the header()? Are all of the values supported on all servers?
     header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
 
     // if user is using IE, urlencode the filename so that multibyte file name will show up correctly on popup
@@ -1628,19 +1657,19 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
     }
 
     if ($forcedownload) {
-        @header('Content-Disposition: attachment; filename="'.$filename.'"');
+        header('Content-Disposition: attachment; filename="'.$filename.'"');
     } else {
-        @header('Content-Disposition: inline; filename="'.$filename.'"');
+        header('Content-Disposition: inline; filename="'.$filename.'"');
     }
 
     if ($lifetime > 0) {
-        @header('Cache-Control: max-age='.$lifetime);
-        @header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
-        @header('Pragma: ');
+        header('Cache-Control: max-age='.$lifetime);
+        header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
+        header('Pragma: ');
 
         if (empty($CFG->disablebyteserving) && !$pathisstring && $mimetype != 'text/plain' && $mimetype != 'text/html') {
 
-            @header('Accept-Ranges: bytes');
+            header('Accept-Ranges: bytes');
 
             if (!empty($_SERVER['HTTP_RANGE']) && strpos($_SERVER['HTTP_RANGE'],'bytes=') !== FALSE) {
                 // byteserving stuff - for acrobat reader and download accelerators
@@ -1676,41 +1705,43 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
             }
         } else {
             /// Do not byteserve (disabled, strings, text and html files).
-            @header('Accept-Ranges: none');
+            header('Accept-Ranges: none');
         }
     } else { // Do not cache files in proxies and browsers
         if (strpos($CFG->wwwroot, 'https://') === 0) { //https sites - watch out for IE! KB812935 and KB316431
-            @header('Cache-Control: max-age=10');
-            @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
-            @header('Pragma: ');
+            header('Cache-Control: max-age=10');
+            header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
+            header('Pragma: ');
         } else { //normal http - prevent caching at all cost
-            @header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
-            @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
-            @header('Pragma: no-cache');
+            header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
+            header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
+            header('Pragma: no-cache');
         }
-        @header('Accept-Ranges: none'); // Do not allow byteserving when caching disabled
+        header('Accept-Ranges: none'); // Do not allow byteserving when caching disabled
     }
 
     if (empty($filter)) {
         if ($mimetype == 'text/html' && !empty($CFG->usesid)) {
             //cookieless mode - rewrite links
-            @header('Content-Type: text/html');
+            header('Content-Type: text/html');
             $path = $pathisstring ? $path : implode('', file($path));
             $path = sid_ob_rewrite($path);
             $filesize = strlen($path);
             $pathisstring = true;
         } else if ($mimetype == 'text/plain') {
-            @header('Content-Type: Text/plain; charset=utf-8'); //add encoding
+            header('Content-Type: Text/plain; charset=utf-8'); //add encoding
         } else {
-            @header('Content-Type: '.$mimetype);
+            header('Content-Type: '.$mimetype);
         }
-        @header('Content-Length: '.$filesize);
-        while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+        header('Content-Length: '.$filesize);
+
+        // send the contents
         if ($pathisstring) {
             echo $path;
         } else {
             @readfile($path);
         }
+
     } else {     // Try to put the file through filters
         if ($mimetype == 'text/html') {
             $options = new stdClass();
@@ -1725,9 +1756,9 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
                 $output = sid_ob_rewrite($output);
             }
 
-            @header('Content-Length: '.strlen($output));
-            @header('Content-Type: text/html');
-            while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+            header('Content-Length: '.strlen($output));
+            header('Content-Type: text/html');
+            // send the contents
             echo $output;
         // only filter text if filter all files is selected
         } else if (($mimetype == 'text/plain') and ($filter == 1)) {
@@ -1741,14 +1772,14 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
                 $output = sid_ob_rewrite($output);
             }
 
-            @header('Content-Length: '.strlen($output));
-            @header('Content-Type: text/html; charset=utf-8'); //add encoding
-            while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+            header('Content-Length: '.strlen($output));
+            header('Content-Type: text/html; charset=utf-8'); //add encoding
+            // send the contents
             echo $output;
         } else {    // Just send it out raw
-            @header('Content-Length: '.$filesize);
-            @header('Content-Type: '.$mimetype);
-            while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+            header('Content-Length: '.$filesize);
+            header('Content-Type: '.$mimetype);
+            // send the contents
             if ($pathisstring) {
                 echo $path;
             }else {
@@ -1808,14 +1839,13 @@ function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownl
     $lastmodified = $stored_file->get_timemodified();
     $filesize     = $stored_file->get_filesize();
 
-    //IE compatibiltiy HACK!
-    if (ini_get('zlib.output_compression')) {
-        ini_set('zlib.output_compression', 'Off');
-    }
-
     //try to disable automatic sid rewrite in cookieless mode
     @ini_set("session.use_trans_sid", "false");
 
+    //flush the buffers - save memory and disable sid rewrite
+    //this also disables zlib compression
+    prepare_file_sending();
+
     //do not put '@' before the next header to detect incorrect moodle configurations,
     //error should be better than "weird" empty lines for admins/users
     //TODO: should we remove all those @ before the header()? Are all of the values supported on all servers?
@@ -1827,19 +1857,19 @@ function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownl
     }
 
     if ($forcedownload) {
-        @header('Content-Disposition: attachment; filename="'.$filename.'"');
+        header('Content-Disposition: attachment; filename="'.$filename.'"');
     } else {
-        @header('Content-Disposition: inline; filename="'.$filename.'"');
+        header('Content-Disposition: inline; filename="'.$filename.'"');
     }
 
     if ($lifetime > 0) {
-        @header('Cache-Control: max-age='.$lifetime);
-        @header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
-        @header('Pragma: ');
+        header('Cache-Control: max-age='.$lifetime);
+        header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
+        header('Pragma: ');
 
         if (empty($CFG->disablebyteserving) && $mimetype != 'text/plain' && $mimetype != 'text/html') {
 
-            @header('Accept-Ranges: bytes');
+            header('Accept-Ranges: bytes');
 
             if (!empty($_SERVER['HTTP_RANGE']) && strpos($_SERVER['HTTP_RANGE'],'bytes=') !== FALSE) {
                 // byteserving stuff - for acrobat reader and download accelerators
@@ -1874,37 +1904,37 @@ function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownl
             }
         } else {
             /// Do not byteserve (disabled, strings, text and html files).
-            @header('Accept-Ranges: none');
+            header('Accept-Ranges: none');
         }
     } else { // Do not cache files in proxies and browsers
         if (strpos($CFG->wwwroot, 'https://') === 0) { //https sites - watch out for IE! KB812935 and KB316431
-            @header('Cache-Control: max-age=10');
-            @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
-            @header('Pragma: ');
+            header('Cache-Control: max-age=10');
+            header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
+            header('Pragma: ');
         } else { //normal http - prevent caching at all cost
-            @header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
-            @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
-            @header('Pragma: no-cache');
+            header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
+            header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
+            header('Pragma: no-cache');
         }
-        @header('Accept-Ranges: none'); // Do not allow byteserving when caching disabled
+        header('Accept-Ranges: none'); // Do not allow byteserving when caching disabled
     }
 
     if (empty($filter)) {
         $filtered = false;
         if ($mimetype == 'text/html' && !empty($CFG->usesid)) {
             //cookieless mode - rewrite links
-            @header('Content-Type: text/html');
+            header('Content-Type: text/html');
             $text = $stored_file->get_content();
             $text = sid_ob_rewrite($text);
             $filesize = strlen($text);
             $filtered = true;
         } else if ($mimetype == 'text/plain') {
-            @header('Content-Type: Text/plain; charset=utf-8'); //add encoding
+            header('Content-Type: Text/plain; charset=utf-8'); //add encoding
         } else {
-            @header('Content-Type: '.$mimetype);
+            header('Content-Type: '.$mimetype);
         }
-        @header('Content-Length: '.$filesize);
-        while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+        header('Content-Length: '.$filesize);
+        // send the contents
         if ($filtered) {
             echo $text;
         } else {
@@ -1924,9 +1954,9 @@ function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownl
                 $output = sid_ob_rewrite($output);
             }
 
-            @header('Content-Length: '.strlen($output));
-            @header('Content-Type: text/html');
-            while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+            header('Content-Length: '.strlen($output));
+            header('Content-Type: text/html');
+            // send the contents
             echo $output;
         // only filter text if filter all files is selected
         } else if (($mimetype == 'text/plain') and ($filter == 1)) {
@@ -1940,14 +1970,14 @@ function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownl
                 $output = sid_ob_rewrite($output);
             }
 
-            @header('Content-Length: '.strlen($output));
-            @header('Content-Type: text/html; charset=utf-8'); //add encoding
-            while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+            header('Content-Length: '.strlen($output));
+            header('Content-Type: text/html; charset=utf-8'); //add encoding
+            // send the contents
             echo $output;
         } else {    // Just send it out raw
-            @header('Content-Length: '.$filesize);
-            @header('Content-Type: '.$mimetype);
-            while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+            header('Content-Length: '.$filesize);
+            header('Content-Type: '.$mimetype);
+            // send the contents
             $stored_file->readfile();
         }
     }
@@ -2134,11 +2164,10 @@ function byteserving_send_file($handle, $mimetype, $ranges, $filesize) {
     }
     if (count($ranges) == 1) { //only one range requested
         $length = $ranges[0][2] - $ranges[0][1] + 1;
-        @header('HTTP/1.1 206 Partial content');
-        @header('Content-Length: '.$length);
-        @header('Content-Range: bytes '.$ranges[0][1].'-'.$ranges[0][2].'/'.$filesize);
-        @header('Content-Type: '.$mimetype);
-        while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+        header('HTTP/1.1 206 Partial content');
+        header('Content-Length: '.$length);
+        header('Content-Range: bytes '.$ranges[0][1].'-'.$ranges[0][2].'/'.$filesize);
+        header('Content-Type: '.$mimetype);
         $buffer = '';
         fseek($handle, $ranges[0][1]);
         while (!feof($handle) && $length > 0) {
@@ -2156,11 +2185,10 @@ function byteserving_send_file($handle, $mimetype, $ranges, $filesize) {
             $totallength += strlen($range[0]) + $range[2] - $range[1] + 1;
         }
         $totallength += strlen("\r\n--".BYTESERVING_BOUNDARY."--\r\n");
-        @header('HTTP/1.1 206 Partial content');
-        @header('Content-Length: '.$totallength);
-        @header('Content-Type: multipart/byteranges; boundary='.BYTESERVING_BOUNDARY);
+        header('HTTP/1.1 206 Partial content');
+        header('Content-Length: '.$totallength);
+        header('Content-Type: multipart/byteranges; boundary='.BYTESERVING_BOUNDARY);
         //TODO: check if "multipart/x-byteranges" is more compatible with current readers/browsers/servers
-        while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
         foreach($ranges as $range) {
             $length = $range[2] - $range[1] + 1;
             echo $range[0];
index 53de0b0..4a7966f 100644 (file)
@@ -7635,28 +7635,6 @@ function get_browser_version_classes() {
     return $classes;
 }
 
-/**
- * This function makes the return value of ini_get consistent if you are
- * setting server directives through the .htaccess file in apache.
- *
- * Current behavior for value set from php.ini On = 1, Off = [blank]
- * Current behavior for value set from .htaccess On = On, Off = Off
- * Contributed by jdell @ unr.edu
- *
- * @todo Finish documenting this function
- *
- * @param string $ini_get_arg The argument to get
- * @return bool True for on false for not
- */
-function ini_get_bool($ini_get_arg) {
-    $temp = ini_get($ini_get_arg);
-
-    if ($temp == '1' or strtolower($temp) == 'on') {
-        return true;
-    }
-    return false;
-}
-
 /**
  * Can handle rotated text. Whether it is safe to use the trickery in textrotate.js.
  *
index 80adab7..5ea062d 100644 (file)
@@ -113,6 +113,11 @@ if (!defined('NO_DEBUG_DISPLAY')) {
     define('NO_DEBUG_DISPLAY', false);
 }
 
+// Some scripts such as upgrade may want to prevent output buffering
+if (!defined('NO_OUTPUT_BUFFERING')) {
+    define('NO_OUTPUT_BUFFERING', false);
+}
+
 // Servers should define a default timezone in php.ini, but if they don't then make sure something is defined.
 // This is a quick hack.  Ideally we should ask the admin for a value.  See MDL-22625 for more on this.
 if (function_exists('date_default_timezone_set') and function_exists('date_default_timezone_get')) {
@@ -339,6 +344,10 @@ $CFG->httpswwwroot = $CFG->wwwroot;
 
 require_once($CFG->libdir .'/setuplib.php');        // Functions that MUST be loaded first
 
+if (NO_OUTPUT_BUFFERING) {
+    disable_output_buffering();
+}
+
 // Increase memory limits if possible
 raise_memory_limit(MEMORY_STANDARD);
 
index 34cbd6c..e6fa28b 100644 (file)
@@ -267,6 +267,24 @@ class invalid_dataroot_permissions extends moodle_exception {
     }
 }
 
+/**
+ * An exception that indicates that file can not be served
+ *
+ * @package    core
+ * @subpackage lib
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class file_serving_exception extends moodle_exception {
+    /**
+     * Constructor
+     * @param string $debuginfo optional more detailed information
+     */
+    function __construct($debuginfo = NULL) {
+        parent::__construct('cannotservefile', 'error', '', NULL, $debuginfo);
+    }
+}
+
 /**
  * Default exception handler, uncaught exceptions are equivalent to error() in 1.9 and earlier
  *
@@ -529,6 +547,26 @@ function format_backtrace($callers, $plaintext = false) {
     return $from;
 }
 
+/**
+ * This function makes the return value of ini_get consistent if you are
+ * setting server directives through the .htaccess file in apache.
+ *
+ * Current behavior for value set from php.ini On = 1, Off = [blank]
+ * Current behavior for value set from .htaccess On = On, Off = Off
+ * Contributed by jdell @ unr.edu
+ *
+ * @param string $ini_get_arg The argument to get
+ * @return bool True for on false for not
+ */
+function ini_get_bool($ini_get_arg) {
+    $temp = ini_get($ini_get_arg);
+
+    if ($temp == '1' or strtolower($temp) == 'on') {
+        return true;
+    }
+    return false;
+}
+
 /**
  * This function verifies the sanity of PHP configuration
  * and stops execution if anything critical found.
@@ -838,7 +876,7 @@ function raise_memory_limit($newlimit) {
         return false;
     }
 
-    $cur = @ini_get('memory_limit');
+    $cur = ini_get('memory_limit');
     if (empty($cur)) {
         // if php is compiled without --enable-memory-limits
         // apparently memory_limit is set to ''
@@ -872,7 +910,7 @@ function reduce_memory_limit($newlimit) {
     if (empty($newlimit)) {
         return false;
     }
-    $cur = @ini_get('memory_limit');
+    $cur = ini_get('memory_limit');
     if (empty($cur)) {
         // if php is compiled without --enable-memory-limits
         // apparently memory_limit is set to ''
@@ -925,6 +963,34 @@ function get_real_size($size = 0) {
     return $size;
 }
 
+/**
+ * Try to disable all output buffering
+ * @private to be called only from lib/setup.php !
+ * @return void
+ */
+function disable_output_buffering() {
+    $olddebug = error_reporting(0);
+
+    // disable compression, it would prevent closing of buffers
+    if (ini_get_bool('zlib.output_compression')) {
+        ini_set('zlib.output_compression', 'Off');
+    }
+
+    // try to flush everything all the time
+    ob_implicit_flush(true);
+
+    // close all buffers if possible and discard any existing output
+    // this can actually work around some whitespace problems in config.php
+    while(ob_get_level()) {
+        if (!ob_end_clean()) {
+            // prevent infinite loop when buffer can not be closed
+            break;
+        }
+    }
+
+    error_reporting($olddebug);
+}
+
 /**
  * Check whether a major upgrade is needed. That is defined as an upgrade that
  * changes something really fundamental in the database, so nothing can possibly
index 5c8d0c3..9a98185 100644 (file)
@@ -18,6 +18,7 @@
 
 define('CHAT_MAX_CLIENT_UPDATES', 1000);
 define('NO_MOODLE_COOKIES', true); // session not used here
+define('NO_OUTPUT_BUFFERING', true);
 
 require('../../../config.php');
 require('../lib.php');
@@ -119,7 +120,6 @@ $refreshurl = "{$CFG->wwwroot}/mod/chat/gui_header_js/jsupdated.php?chat_sid=$ch
 
     // Ensure the HTML head makes it out there
     echo $CHAT_DUMMY_DATA;
-    @ob_end_flush();
 
     for ($n=0; $n <= CHAT_MAX_CLIENT_UPDATES; $n++) {
 
@@ -135,7 +135,6 @@ $refreshurl = "{$CFG->wwwroot}/mod/chat/gui_header_js/jsupdated.php?chat_sid=$ch
             $chat_newlastid   = 0;
             print " \n";
             print $CHAT_DUMMY_DATA;
-            @ob_end_flush();
             sleep($CFG->chat_refresh_room);
             continue;
         }
@@ -164,7 +163,6 @@ $refreshurl = "{$CFG->wwwroot}/mod/chat/gui_header_js/jsupdated.php?chat_sid=$ch
         } else {
             print " \n";
             print $CHAT_DUMMY_DATA;
-            @ob_end_flush();
             sleep($CFG->chat_refresh_room);
             continue;
             $num = 0;
@@ -229,7 +227,6 @@ EOD;
             print '<embed src="../beep.wav" autostart="true" hidden="true" name="beep" />';
         }
         print $CHAT_DUMMY_DATA;
-        @ob_end_flush();
         sleep($CFG->chat_refresh_room);
     } // here ends the for() loop
 
index 3a33ef4..8f2b9e3 100644 (file)
 /**
 * includes and requires
 */
+
+define('NO_OUTPUT_BUFFERING', true);
+
 require_once('../config.php');
 require_once($CFG->dirroot.'/search/lib.php');
 
 //this'll take some time, set up the environment
 @set_time_limit(0);
-@ob_implicit_flush(true);
-@ob_end_flush();
 
     ini_set('include_path', $CFG->dirroot.DIRECTORY_SEPARATOR.'search'.PATH_SEPARATOR.ini_get('include_path'));
 
 /// only administrators can index the moodle installation, because access to all pages is required
 
     require_login();
-    
+
     if (empty($CFG->enableglobalsearch)) {
         print_error('globalsearchdisabled', 'search');
     }
-    
+
     if (!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
         print_error('beadmin', 'search', get_login_url());
     }
@@ -55,72 +56,72 @@ require_once($CFG->dirroot.'/search/lib.php');
 /// confirmation flag to prevent accidental reindexing (indexersplash.php is the correct entry point)
 
     $sure = strtolower(optional_param('areyousure', '', PARAM_ALPHA));
-    
+
     if ($sure != 'yes') {
         mtrace("<pre>Sorry, you need to confirm indexing via <a href='indexersplash.php'>indexersplash.php</a>"
               .". (<a href='index.php'>Back to query page</a>).</pre>");
-    
+
         exit(0);
     }
-    
+
 /// check for php5 (lib.php)
 
     //php5 found, continue including php5-only files
     //require_once("$CFG->dirroot/search/Zend/Search/Lucene.php");
     require_once($CFG->dirroot.'/search/indexlib.php');
-    
+
     mtrace('<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8" /></head><body>');
     mtrace('<pre>Server Time: '.date('r',time())."\n");
-    
+
     if (isset($CFG->search_indexer_busy) && $CFG->search_indexer_busy == '1') {
         //means indexing was not finished previously
         mtrace("Warning: Indexing was not successfully completed last time, restarting.\n");
     }
-    
+
 /// turn on busy flag
 
     set_config('search_indexer_busy', '1');
-    
+
     //paths
     $index_path = SEARCH_INDEX_PATH;
     $index_db_file = "{$CFG->dirroot}/search/db/$CFG->dbtype.sql";
     $dbcontrol = new IndexDBControl();
-    
+
 /// setup directory in data root
 
     if (!file_exists($index_path)) {
         mtrace("Data directory ($index_path) does not exist, attempting to create.");
         if (!mkdir($index_path, $CFG->directorypermissions)) {
             search_pexit("Error creating data directory at: $index_path. Please correct.");
-        } 
+        }
         else {
             mtrace("Directory successfully created.");
-        } 
-    } 
+        }
+    }
     else {
         mtrace("Using {$index_path} as data directory.");
-    } 
-    
+    }
+
     Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8_CaseInsensitive());
     $index = new Zend_Search_Lucene($index_path, true);
-    
+
 /// New regeneration
 
     mtrace('Deleting old index entries.');
     $DB->delete_records(SEARCH_DATABASE_TABLE);
-    
+
 /// begin timer
 
     search_stopwatch();
     mtrace("Starting activity modules\n");
-    
+
     //the presence of the required search functions -
     // * mod_iterator
     // * mod_get_content_for_index
     //are the sole basis for including a module in the index at the moment.
 
     $searchables = search_collect_searchables();
-    
+
 /// start indexation
 
     if ($searchables){
@@ -131,24 +132,24 @@ require_once($CFG->dirroot.'/search/lib.php');
             set_config($indexdatestring, time());
             $indexdatestring = 'search_indexer_run_date_'.$mod->name;
             set_config($indexdatestring, time());
-        
+
             mtrace("starting indexing {$mod->name}\n");
-        
+
             $key = 'search_in_'.$mod->name;
             if (isset($CFG->$key) && !$CFG->$key) {
                 mtrace("module $key has been administratively disabled. Skipping...\n");
                 continue;
             }
-        
+
             if ($mod->location == 'internal'){
                 $class_file = $CFG->dirroot.'/search/documents/'.$mod->name.'_document.php';
             } else {
                 $class_file = $CFG->dirroot.'/'.$mod->location.'/'.$mod->name.'/search_document.php';
             }
-            
+
             if (file_exists($class_file)) {
                 include_once($class_file);
-    
+
                 //build function names
                 $iter_function = $mod->name.'_iterator';
                 $index_function = $mod->name.'_get_content_for_index';
@@ -159,7 +160,7 @@ require_once($CFG->dirroot.'/search/lib.php');
                     if ($sources){
                         foreach ($sources as $i) {
                             $documents = $index_function($i);
-                  
+
                             //begin transaction
                             if ($documents){
                                 foreach($documents as $document) {
@@ -171,27 +172,27 @@ require_once($CFG->dirroot.'/search/lib.php');
                                     }
                                     //object to insert into db
                                     $dbid = $dbcontrol->addDocument($document);
-                                    
+
                                     //synchronise db with index
                                     $document->addField(Zend_Search_Lucene_Field::Keyword('dbid', $dbid));
-                                    
+
                                     //add document to index
                                     $index->addDocument($document);
-                                    
+
                                     //commit every x new documents, and print a status message
                                     if (($counter % 2000) == 0) {
                                         $index->commit();
                                         mtrace(".. $counter");
-                                    } 
+                                    }
                                 }
                             }
                             //end transaction
                         }
                     }
-            
+
                     //commit left over documents, and finish up
                     $index->commit();
-          
+
                     mtrace("-- $counter documents indexed");
                     mtrace("done.\n");
                 }
@@ -200,19 +201,19 @@ require_once($CFG->dirroot.'/search/lib.php');
             }
         }
     }
-      
+
 /// finished modules
 
     mtrace('Finished activity modules');
     search_stopwatch();
-        
+
     mtrace(".<br/><a href='index.php'>Back to query page</a>.");
     mtrace('</pre>');
-    
+
 /// finished, turn busy flag off
 
     set_config('search_indexer_busy', '0');
-    
+
 /// mark the time we last updated
 
     set_config('search_indexer_run_date', time());
index 0fdedff..594fd63 100644 (file)
     * @version Moodle 2.0
     **/
 
+    define('NO_OUTPUT_BUFFERING', true);
 
     require_once('../../config.php');
 
     @set_time_limit(0);
-    @ob_implicit_flush(true);
-    @ob_end_flush();
 /// makes inclusions of the Zend Engine more reliable
     ini_set('include_path', $CFG->dirroot.DIRECTORY_SEPARATOR.'search'.PATH_SEPARATOR.ini_get('include_path'));
 
     require_once($CFG->dirroot.'/search/lib.php');
 
     require_login();
-    
+
     if (empty($CFG->enableglobalsearch)) {
       print_error('globalsearchdisabled', 'search');
     }
-    
+
     if (!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
       print_error('onlyadmins', 'error', get_login_url());
     }
 
     mtrace('<pre>Server Time: '.date('r',time()));
     mtrace("Testing global search capabilities:\n");
-    
+
     //fix paths for testing
     set_include_path(get_include_path().":../");
     require_once("$CFG->dirroot/search/Zend/Search/Lucene.php");
-    
+
     mtrace("Checking activity modules:\n");
-    
+
     //the presence of the required search functions -
     // * mod_iterator
     // * mod_get_content_for_index
     //are the sole basis for including a module in the index at the moment.
-    
+
 /// get all installed modules
     if ($mods = $DB->get_records('modules', null, 'name', 'id, name')){
 
                 if (file_exists($documentfile)){
                     $searchables[] = $mod;
                 }
-            }        
-        }    
+            }
+        }
         mtrace(count($searchables).' modules to search in / '.count($mods).' modules found.');
     }
-      
+
 /// collects blocks as indexable information may be found in blocks either
     if ($blocks = $DB->get_records('block', null, 'name', 'id,name')) {
         $blocks_searchables = array();
                     $mod->location = 'blocks';
                     $blocks_searchables[] = $block;
                 }
-            }        
-        }    
+            }
+        }
         mtrace(count($blocks_searchables).' blocks to search in / '.count($blocks).' blocks found.');
         $searchables = array_merge($searchables, $blocks_searchables);
     }
-      
+
 /// add virtual modules onto the back of the array
 
     $additional = search_get_additional_modules();
     mtrace(count($additional).' additional to search in.');
     $searchables = array_merge($searchables, $additional);
-    
+
     foreach ($searchables as $mod) {
 
         $key = 'search_in_'.$mod->name;
         } else {
             $class_file = $CFG->dirroot.'/'.$mod->location.'/'.$mod->name.'/search_document.php';
         }
-    
+
         if (file_exists($class_file)) {
             include_once($class_file);
-    
+
             if ($mod->location != 'internal' && !defined('X_SEARCH_TYPE_'.strtoupper($mod->name))) {
                 mtrace("ERROR: Constant 'X_SEARCH_TYPE_".strtoupper($mod->name)."' is not defined in search/searchtypes.php or in module");
                 continue;
             }
-            
+
             $iter_function = $mod->name.'_iterator';
             $index_function = $mod->name.'_get_content_for_index';
-            
+
             if (function_exists($index_function) && function_exists($iter_function)) {
                 $entries = $iter_function();
                 if (!empty($entries)) {
                     $documents = $index_function(array_pop($entries));
-            
+
                     if (is_array($documents)) {
                         mtrace("Success: '$mod->name' module seems to be ready for indexing.");
                     } else {
                         mtrace("ERROR: $index_function() doesn't seem to be returning an array.");
-                    } 
+                    }
                 } else {
                     mtrace("Success : '$mod->name' has nothing to index.");
-                } 
+                }
             } else {
                 mtrace("ERROR: $iter_function() and/or $index_function() does not exist in $class_file");
-            } 
+            }
         } else {
             mtrace("Notice: $class_file does not exist, this module will not be indexed.");
-        } 
-    } 
-    
+        }
+    }
+
     mtrace("\nFinished checking for searcheable items.");
-        
+
     mtrace("<br/><a href='../index.php'>Back to query page</a> or <a href='../indexersplash.php'>Start indexing</a>.");
     mtrace('</pre>');
 ?>
\ No newline at end of file