Esempio n. 1
0
	protected function __construct() {
		$idFile = wfTempDir() . '/mw-' . __CLASS__ . '-UID-nodeid';
		$nodeId = is_file( $idFile ) ? file_get_contents( $idFile ) : '';
		// Try to get some ID that uniquely identifies this machine (RFC 4122)...
		if ( !preg_match( '/^[0-9a-f]{12}$/i', $nodeId ) ) {
			wfSuppressWarnings();
			if ( wfIsWindows() ) {
				// http://technet.microsoft.com/en-us/library/bb490913.aspx
				$csv = trim( wfShellExec( 'getmac /NH /FO CSV' ) );
				$line = substr( $csv, 0, strcspn( $csv, "\n" ) );
				$info = str_getcsv( $line );
				$nodeId = isset( $info[0] ) ? str_replace( '-', '', $info[0] ) : '';
			} elseif ( is_executable( '/sbin/ifconfig' ) ) { // Linux/BSD/Solaris/OS X
				// See http://linux.die.net/man/8/ifconfig
				$m = array();
				preg_match( '/\s([0-9a-f]{2}(:[0-9a-f]{2}){5})\s/',
					wfShellExec( '/sbin/ifconfig -a' ), $m );
				$nodeId = isset( $m[1] ) ? str_replace( ':', '', $m[1] ) : '';
			}
			wfRestoreWarnings();
			if ( !preg_match( '/^[0-9a-f]{12}$/i', $nodeId ) ) {
				$nodeId = MWCryptRand::generateHex( 12, true );
				$nodeId[1] = dechex( hexdec( $nodeId[1] ) | 0x1 ); // set multicast bit
			}
			file_put_contents( $idFile, $nodeId ); // cache
		}
		$this->nodeId32 = wfBaseConvert( substr( sha1( $nodeId ), 0, 8 ), 16, 2, 32 );
		$this->nodeId48 = wfBaseConvert( $nodeId, 16, 2, 48 );
		// If different processes run as different users, they may have different temp dirs.
		// This is dealt with by initializing the clock sequence number and counters randomly.
		$this->lockFile88 = wfTempDir() . '/mw-' . __CLASS__ . '-UID-88';
		$this->lockFile128 = wfTempDir() . '/mw-' . __CLASS__ . '-UID-128';
	}
	function __construct() {
		global $wgWebStoreSettings, $wgUploadDirectory, $wgTmpDirectory, $wgFileStore;

		foreach ( $wgWebStoreSettings as $name => $value ) {
			$this->$name = $value;
		}
		if ( !$this->tmpDir ) {
			$this->tmpDir = $wgTmpDirectory;
		}
		if ( !$this->publicDir ) {
			$this->publicDir = $wgUploadDirectory;
		}
		if ( !$this->deletedDir ) {
			if ( isset( $wgFileStore['deleted']['directory'] ) ) {
				$this->deletedDir = $wgFileStore['deleted']['directory'];
			} else {
				// No deletion
				$this->errors[] = new WebStoreWarning( 'webstore_no_deleted' );
				$this->deletedDir = false;
			}
		}
		$this->windows = wfIsWindows();


	}
 function getLineEnding()
 {
     if (wfIsWindows()) {
         return "\r\n";
     } else {
         return "\n";
     }
 }
 /**
  * @covers MailAddress::toString
  * @dataProvider provideToString
  */
 public function testToString($useRealName, $address, $name, $realName, $expected)
 {
     if (wfIsWindows()) {
         $this->markTestSkipped('This test only works on non-Windows platforms');
     }
     $this->setMwGlobals('wgEnotifUseRealName', $useRealName);
     $ma = new MailAddress($address, $name, $realName);
     $this->assertEquals($expected, $ma->toString());
 }
 public function testBug67870()
 {
     $command = wfIsWindows() ? 'for /l %i in (1, 1, 1001) do @echo ' . str_repeat('*', 331) : 'printf "%-333333s" "*"';
     // Test several times because it involves a race condition that may randomly succeed or fail
     for ($i = 0; $i < 10; $i++) {
         $output = wfShellExec($command);
         $this->assertEquals(333333, strlen($output));
     }
 }
 public function testMultipleArgsAsArray()
 {
     if (wfIsWindows()) {
         $expected = '"foo" "bar" "baz"';
     } else {
         $expected = "'foo' 'bar' 'baz'";
     }
     $actual = wfEscapeShellArg(['foo', 'bar', 'baz']);
     $this->assertEquals($expected, $actual);
 }
Esempio n. 7
0
function getVars($_gv_filename)
{
    require $_gv_filename;
    $vars = get_defined_vars();
    unset($vars['_gv_filename']);
    # Clean up line endings
    if (wfIsWindows()) {
        $vars = unixLineEndings($vars);
    }
    return $vars;
}
Esempio n. 8
0
	/**
	 * Open a /dev/urandom file handle
	 * Returns a Status object
	 */
	function open() {
		if ( $this->urandom ) {
			return Status::newGood();
		}

		if ( wfIsWindows() ) {
			return Status::newFatal( 'securepoll-urandom-not-supported' );
		}
		$this->urandom = fopen( '/dev/urandom', 'rb' );
		if ( !$this->urandom ) {
			return Status::newFatal( 'securepoll-dump-no-urandom' );
		}
		return Status::newGood();
	}
/**
 * Tries to get the system directory for temporary files. The TMPDIR, TMP, and
 * TEMP environment variables are then checked in sequence, and if none are set
 * try sys_get_temp_dir() for PHP >= 5.2.1. All else fails, return /tmp for Unix
 * or C:\Windows\Temp for Windows and hope for the best.
 * It is common to call it with tempnam().
 *
 * NOTE: When possible, use instead the tmpfile() function to create
 * temporary files to avoid race conditions on file creation, etc.
 *
 * This function is from MediaWiki 1.19
 *
 * @return String
 */
function w2lTempDir()
{
    foreach (array('TMPDIR', 'TMP', 'TEMP') as $var) {
        $tmp = getenv($var);
        if ($tmp && file_exists($tmp) && is_dir($tmp) && is_writable($tmp)) {
            return $tmp;
        }
    }
    if (function_exists('sys_get_temp_dir')) {
        return sys_get_temp_dir();
    }
    # Usual defaults
    return wfIsWindows() ? 'C:\\Windows\\Temp' : '/tmp';
}
Esempio n. 10
0
 /**
  * Return formatted and quoted address to insert into SMTP headers
  * @return string
  */
 function toString()
 {
     # PHP's mail() implementation under Windows is somewhat s***e, and
     # can't handle "Joe Bloggs <*****@*****.**>" format email addresses,
     # so don't bother generating them
     if ($this->name != '' && !wfIsWindows()) {
         $quoted = wfQuotedPrintable($this->name);
         if (strpos($quoted, '.') !== false) {
             $quoted = '"' . $quoted . '"';
         }
         return "{$quoted} <{$this->address}>";
     } else {
         return $this->address;
     }
 }
Esempio n. 11
0
 /**
  * Verify all files that appear to be tests have file names ending in
  * Test.  If the file names do not end in Test, they will not be run.
  */
 public function testUnitTestFileNamesEndWithTest()
 {
     if (wfIsWindows()) {
         $this->markTestSkipped('This test does not work on Windows');
     }
     $rootPath = escapeshellarg(__DIR__);
     $testClassRegex = implode('|', array('ApiFormatTestBase', 'ApiTestCase', 'MediaWikiLangTestCase', 'MediaWikiTestCase', 'PHPUnit_Framework_TestCase'));
     $testClassRegex = "^class .* extends ({$testClassRegex})";
     $finder = "find {$rootPath} -name '*.php' '!' -name '*Test.php'" . " | xargs grep -El '{$testClassRegex}|function suite\\('";
     $results = null;
     $exitCode = null;
     exec($finder, $results, $exitCode);
     $this->assertEquals(0, $exitCode, 'Verify find/grep command succeeds.');
     $results = array_filter($results, array($this, 'filterSuites'));
     $this->assertEquals(array(), $results, 'Unit test file names must end with Test.');
 }
 function stream_open($path, $mode, $options, &$opened_path)
 {
     if ($mode[0] == 'r') {
         $options = 'e -bd -so';
     } elseif ($mode[0] == 'w') {
         $options = 'a -bd -si';
     } else {
         return false;
     }
     $arg = wfEscapeShellArg($this->stripPath($path));
     $command = "7za {$options} {$arg}";
     if (!wfIsWindows()) {
         // Suppress the stupid messages on stderr
         $command .= ' 2>/dev/null';
     }
     $this->stream = popen($command, $mode);
     return $this->stream !== false;
 }
 public static function main($exit = true)
 {
     $command = new self();
     if (wfIsWindows()) {
         # Windows does not come anymore with ANSI.SYS loaded by default
         # PHPUnit uses the suite.xml parameters to enable/disable colors
         # which can be then forced to be enabled with --colors.
         # The below code inject a parameter just like if the user called
         # phpunit with a --no-color option (which does not exist). It
         # overrides the suite.xml setting.
         # Probably fix bug 29226
         $command->arguments['colors'] = false;
     }
     # Makes MediaWiki PHPUnit directory includable so the PHPUnit will
     # be able to resolve relative files inclusion such as suites/*
     # PHPUnit uses stream_resolve_include_path() internally
     # See bug 32022
     set_include_path(__DIR__ . PATH_SEPARATOR . get_include_path());
     $command->run($_SERVER['argv'], $exit);
 }
Esempio n. 14
0
 /**
  * Verify all files that appear to be tests have file names ending in
  * Test.  If the file names do not end in Test, they will not be run.
  * @group medium
  */
 public function testUnitTestFileNamesEndWithTest()
 {
     if (wfIsWindows()) {
         $this->markTestSkipped('This test does not work on Windows');
     }
     $rootPath = escapeshellarg(__DIR__ . '/..');
     $testClassRegex = implode('|', ['ApiFormatTestBase', 'ApiTestCase', 'ApiQueryTestBase', 'ApiQueryContinueTestBase', 'MediaWikiLangTestCase', 'MediaWikiMediaTestCase', 'MediaWikiTestCase', 'ResourceLoaderTestCase', 'PHPUnit_Framework_TestCase', 'DumpTestCase']);
     $testClassRegex = "^class .* extends ({$testClassRegex})";
     $finder = "find {$rootPath} -name '*.php' '!' -name '*Test.php'" . " | xargs grep -El '{$testClassRegex}|function suite\\('";
     $results = null;
     $exitCode = null;
     exec($finder, $results, $exitCode);
     $this->assertEquals(0, $exitCode, 'Verify find/grep command succeeds.');
     $results = array_filter($results, [$this, 'filterSuites']);
     $strip = strlen($rootPath) - 1;
     foreach ($results as $k => $v) {
         $results[$k] = substr($v, $strip);
     }
     $this->assertEquals([], $results, "Unit test file in {$rootPath} must end with Test.");
 }
Esempio n. 15
0
 /**
  * Return formatted and quoted address to insert into SMTP headers
  * @return string
  */
 function toString()
 {
     # PHP's mail() implementation under Windows is somewhat s***e, and
     # can't handle "Joe Bloggs <*****@*****.**>" format email addresses,
     # so don't bother generating them
     if ($this->address) {
         if ($this->name != '' && !wfIsWindows()) {
             global $wgEnotifUseRealName;
             $name = $wgEnotifUseRealName && $this->realName !== '' ? $this->realName : $this->name;
             $quoted = UserMailer::quotedPrintable($name);
             if (strpos($quoted, '.') !== false || strpos($quoted, ',') !== false) {
                 $quoted = '"' . $quoted . '"';
             }
             return "{$quoted} <{$this->address}>";
         } else {
             return $this->address;
         }
     } else {
         return "";
     }
 }
Esempio n. 16
0
 /**
  * Get wikitext from a the file passed as argument or STDIN
  * @return string Wikitext
  */
 protected function Wikitext()
 {
     $php_stdin = 'php://stdin';
     $input_file = $this->getArg(0, $php_stdin);
     if ($input_file === $php_stdin && !$this->mQuiet) {
         $ctrl = wfIsWindows() ? 'CTRL+Z' : 'CTRL+D';
         $this->error(basename(__FILE__) . ": warning: reading wikitext from STDIN. Press {$ctrl} to parse.\n");
     }
     return file_get_contents($input_file);
 }
Esempio n. 17
0
 public function execute()
 {
     global $IP;
     // Deregister handler from MWExceptionHandler::installHandle so that PHPUnit's own handler
     // stays in tact.
     // Has to in execute() instead of finalSetup(), because finalSetup() runs before
     // doMaintenance.php includes Setup.php, which calls MWExceptionHandler::installHandle().
     restore_error_handler();
     $this->forceFormatServerArgv();
     # Make sure we have --configuration or PHPUnit might complain
     if (!in_array('--configuration', $_SERVER['argv'])) {
         // Hack to eliminate the need to use the Makefile (which sucks ATM)
         array_splice($_SERVER['argv'], 1, 0, array('--configuration', $IP . '/tests/phpunit/suite.xml'));
     }
     # --with-phpunitdir let us override the default PHPUnit version
     # Can use with either or phpunit.phar in the directory or the
     # full PHPUnit code base.
     if ($this->hasOption('with-phpunitdir')) {
         $phpunitDir = $this->getOption('with-phpunitdir');
         # prepends provided PHPUnit directory or phar
         $this->output("Will attempt loading PHPUnit from `{$phpunitDir}`\n");
         set_include_path($phpunitDir . PATH_SEPARATOR . get_include_path());
         # Cleanup $args array so the option and its value do not
         # pollute PHPUnit
         $key = array_search('--with-phpunitdir', $_SERVER['argv']);
         unset($_SERVER['argv'][$key]);
         // the option
         unset($_SERVER['argv'][$key + 1]);
         // its value
         $_SERVER['argv'] = array_values($_SERVER['argv']);
     }
     if (!wfIsWindows()) {
         # If we are not running on windows then we can enable phpunit colors
         # Windows does not come anymore with ANSI.SYS loaded by default
         # PHPUnit uses the suite.xml parameters to enable/disable colors
         # which can be then forced to be enabled with --colors.
         # The below code injects a parameter just like if the user called
         # Probably fix bug 29226
         $key = array_search('--colors', $_SERVER['argv']);
         if ($key === false) {
             array_splice($_SERVER['argv'], 1, 0, '--colors');
         }
     }
     # Makes MediaWiki PHPUnit directory includable so the PHPUnit will
     # be able to resolve relative files inclusion such as suites/*
     # PHPUnit uses stream_resolve_include_path() internally
     # See bug 32022
     $key = array_search('--include-path', $_SERVER['argv']);
     if ($key === false) {
         array_splice($_SERVER['argv'], 1, 0, __DIR__ . PATH_SEPARATOR . get_include_path());
         array_splice($_SERVER['argv'], 1, 0, '--include-path');
     }
     $key = array_search('--debug-tests', $_SERVER['argv']);
     if ($key !== false && array_search('--printer', $_SERVER['argv']) === false) {
         unset($_SERVER['argv'][$key]);
         array_splice($_SERVER['argv'], 1, 0, 'MediaWikiPHPUnitTestListener');
         array_splice($_SERVER['argv'], 1, 0, '--printer');
     }
     foreach (self::$additionalOptions as $option => $default) {
         $key = array_search('--' . $option, $_SERVER['argv']);
         if ($key !== false) {
             unset($_SERVER['argv'][$key]);
             if ($this->mParams[$option]['withArg']) {
                 self::$additionalOptions[$option] = $_SERVER['argv'][$key + 1];
                 unset($_SERVER['argv'][$key + 1]);
             } else {
                 self::$additionalOptions[$option] = true;
             }
         }
     }
 }
Esempio n. 18
0
 function getLog($path, $startRev = null, $endRev = null)
 {
     $lang = wfIsWindows() ? "" : "LC_ALL=en_US.utf-8 ";
     $command = sprintf("{$lang}svn log -v -r%s:%s %s %s", wfEscapeShellArg($this->_rev($startRev, 'BASE')), wfEscapeShellArg($this->_rev($endRev, 'HEAD')), $this->getExtraArgs(), wfEscapeShellArg($this->mRepoPath . $path));
     $lines = explode("\n", wfShellExec($command));
     $out = array();
     $divider = str_repeat('-', 72);
     $formats = array('rev' => '/^r(\\d+)$/', 'author' => '/^(.*)$/', 'date' => '/^(?:(.*?) )?\\(.*\\)$/', 'lines' => '/^(\\d+) lines?$/');
     $state = "start";
     foreach ($lines as $line) {
         $line = rtrim($line);
         switch ($state) {
             case "start":
                 if ($line == $divider) {
                     $state = "revdata";
                     break;
                 } else {
                     return $out;
                     # throw new MWException( "Unexpected start line: $line" );
                 }
             case "revdata":
                 if ($line == "") {
                     $state = "done";
                     break;
                 }
                 $data = array();
                 $bits = explode(" | ", $line);
                 $i = 0;
                 foreach ($formats as $key => $regex) {
                     $text = $bits[$i++];
                     $matches = array();
                     if (preg_match($regex, $text, $matches)) {
                         $data[$key] = $matches[1];
                     } else {
                         throw new MWException("Unexpected format for {$key} in '{$text}'");
                     }
                 }
                 $data['msg'] = '';
                 $data['paths'] = array();
                 $state = 'changedpaths';
                 break;
             case "changedpaths":
                 if ($line == "Changed paths:") {
                     // broken when svn messages are not in English
                     $state = "path";
                 } elseif ($line == "") {
                     // No changed paths?
                     $state = "msg";
                 } else {
                     throw new MWException("Expected 'Changed paths:' or '', got '{$line}'");
                 }
                 break;
             case "path":
                 if ($line == "") {
                     // Out of paths. Move on to the message...
                     $state = 'msg';
                 } else {
                     $matches = array();
                     if (preg_match('/^   (.) (.*)$/', $line, $matches)) {
                         $data['paths'][] = array('action' => $matches[1], 'path' => $matches[2]);
                     }
                 }
                 break;
             case "msg":
                 $data['msg'] .= $line;
                 if (--$data['lines']) {
                     $data['msg'] .= "\n";
                 } else {
                     unset($data['lines']);
                     $out[] = $data;
                     $state = "start";
                 }
                 break;
             case "done":
                 throw new MWException("Unexpected input after end: {$line}");
             default:
                 throw new MWException("Invalid state '{$state}'");
         }
     }
     return $out;
 }
Esempio n. 19
0
/**
 * Get a platform-independent path to the null file, e.g. /dev/null
 *
 * @return string
 */
function wfGetNull()
{
    return wfIsWindows() ? 'NUL' : '/dev/null';
}
	/**
	 * Generic wrapper function for a virus scanner program.
	 * This relies on the $wgAntivirus and $wgAntivirusSetup variables.
	 * $wgAntivirusRequired may be used to deny upload if the scan fails.
	 *
	 * @param string $file Pathname to the temporary upload file
	 * @return mixed false if not virus is found, NULL if the scan fails or is disabled,
	 * or a string containing feedback from the virus scanner if a virus was found.
	 * If textual feedback is missing but a virus was found, this function returns true.
	 */
	function detectVirus( $file ) {
		global $wgAntivirus, $wgAntivirusSetup, $wgAntivirusRequired;

		if ( !$wgAntivirus ) { # disabled?
			wfDebug( __METHOD__ . ": virus scanner disabled\n" );
			return null;
		}

		if ( !$wgAntivirusSetup[$wgAntivirus] ) {
			wfDebug( __METHOD__ . ": unknown virus scanner: $wgAntivirus\n" );

			$wgOut->addHTML( '<div class="error">' . wfMsg( 'virus-badscanner', $wgAntivirus ) . "\n" );

			return wfMsg( 'virus-unknownscanner' ) . $wgAntivirus;
		}

		# look up scanner configuration
		$virus_scanner = $wgAntivirusSetup[$wgAntivirus]['command']; # command pattern
		$virus_scanner_codes = $wgAntivirusSetup[$wgAntivirus]['codemap']; # exit-code map
		$msg_pattern = $wgAntivirusSetup[$wgAntivirus]['messagepattern']; # message pattern

		$scanner = $virus_scanner; # copy, so we can resolve the pattern

		if ( strpos( $scanner, "%f" ) === false ) {
			$scanner .= ' ' . wfEscapeShellArg( $file ); # simple pattern: append file to scan
		} else {
			$scanner = str_replace( "%f", wfEscapeShellArg( $file ), $scanner ); # complex pattern: replace "%f" with file to scan
		}

		wfDebug( __METHOD__ . ": running virus scan: $scanner \n" );

		# execute virus scanner
		$code = false;

		# NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
		# that does not seem to be worth the pain.
		# Ask me (Duesentrieb) about it if it's ever needed.
		if ( wfIsWindows() ) {
			exec( "$scanner", $output, $code );
		} else {
			exec( "$scanner 2>&1", $output, $code );
		}

		$exit_code = $code; # remeber for user feedback

		if ( $virus_scanner_codes ) { # map exit code to AV_xxx constants.
			if ( isset( $virus_scanner_codes[$code] ) ) {
				$code = $virus_scanner_codes[$code]; # explicite mapping
			} elseif ( isset( $virus_scanner_codes['*'] ) ) {
				$code = $virus_scanner_codes['*']; # fallback mapping
			}
		}

		if ( $code === AV_SCAN_FAILED ) { # scan failed (code was mapped to false by $virus_scanner_codes)
			wfDebug( __METHOD__ . ": failed to scan $file (code $exit_code).\n" );
			if ( $wgAntivirusRequired ) {
				return wfMsg( 'virus-scanfailed', $exit_code );
			} else {
				return null;
			}
		} elseif ( $code === AV_SCAN_ABORTED ) { # scan failed because filetype is unknown (probably immune)
			wfDebug( __METHOD__ . ": unsupported file type $file (code $exit_code).\n" );
			return null;
		} elseif ( $code === AV_NO_VIRUS ) {
			wfDebug( __METHOD__ . ": file passed virus scan.\n" );
			return false; # no virus found
		} else {
			$output = join( "\n", $output );
			$output = trim( $output );

			if ( !$output ) {
				$output = true; # if there's no output, return true
			} elseif ( $msg_pattern ) {
				$groups = array();
				if ( preg_match( $msg_pattern, $output, $groups ) ) {
					if ( $groups[1] ) {
						$output = $groups[1];
					}
				}
			}

			wfDebug( __METHOD__ . ": FOUND VIRUS! scanner feedback: $output" );
			return $output;
		}
	}
Esempio n. 21
0
 /**
  * Determine if a relative path is valid, i.e. not blank or involving directory traveral
  *
  * @param $filename string
  * @return bool
  */
 public function validateFilename($filename)
 {
     if (strval($filename) == '') {
         return false;
     }
     if (wfIsWindows()) {
         $filename = strtr($filename, '\\', '/');
     }
     /**
      * Use the same traversal protection as Title::secureAndSplit()
      */
     if (strpos($filename, '.') !== false && ($filename === '.' || $filename === '..' || strpos($filename, './') === 0 || strpos($filename, '../') === 0 || strpos($filename, '/./') !== false || strpos($filename, '/../') !== false)) {
         return false;
     } else {
         return true;
     }
 }
Esempio n. 22
0
 /**
  * This function will perform a direct (authenticated) login to
  * a SMTP Server to use for mail relaying if 'wgSMTP' specifies an
  * array of parameters. It requires PEAR:Mail to do that.
  * Otherwise it just uses the standard PHP 'mail' function.
  *
  * @param MailAddress|MailAddress[] $to Recipient's email (or an array of them)
  * @param MailAddress $from Sender's email
  * @param string $subject Email's subject.
  * @param string $body Email's text or Array of two strings to be the text and html bodies
  * @param MailAddress $replyto Optional reply-to email (default: null).
  * @param string $contentType Optional custom Content-Type (default: text/plain; charset=UTF-8)
  * @throws MWException
  * @throws Exception
  * @return Status
  */
 public static function send($to, $from, $subject, $body, $replyto = null, $contentType = 'text/plain; charset=UTF-8')
 {
     global $wgSMTP, $wgEnotifMaxRecips, $wgAdditionalMailParams, $wgAllowHTMLEmail;
     $mime = null;
     if (!is_array($to)) {
         $to = array($to);
     }
     // mail body must have some content
     $minBodyLen = 10;
     // arbitrary but longer than Array or Object to detect casting error
     // body must either be a string or an array with text and body
     if (!(!is_array($body) && strlen($body) >= $minBodyLen) && !(is_array($body) && isset($body['text']) && isset($body['html']) && strlen($body['text']) >= $minBodyLen && strlen($body['html']) >= $minBodyLen)) {
         // if it is neither we have a problem
         return Status::newFatal('user-mail-no-body');
     }
     if (!$wgAllowHTMLEmail && is_array($body)) {
         // HTML not wanted.  Dump it.
         $body = $body['text'];
     }
     wfDebug(__METHOD__ . ': sending mail to ' . implode(', ', $to) . "\n");
     # Make sure we have at least one address
     $has_address = false;
     foreach ($to as $u) {
         if ($u->address) {
             $has_address = true;
             break;
         }
     }
     if (!$has_address) {
         return Status::newFatal('user-mail-no-addy');
     }
     # Forge email headers
     # -------------------
     #
     # WARNING
     #
     # DO NOT add To: or Subject: headers at this step. They need to be
     # handled differently depending upon the mailer we are going to use.
     #
     # To:
     #  PHP mail() first argument is the mail receiver. The argument is
     #  used as a recipient destination and as a To header.
     #
     #  PEAR mailer has a recipient argument which is only used to
     #  send the mail. If no To header is given, PEAR will set it to
     #  to 'undisclosed-recipients:'.
     #
     #  NOTE: To: is for presentation, the actual recipient is specified
     #  by the mailer using the Rcpt-To: header.
     #
     # Subject:
     #  PHP mail() second argument to pass the subject, passing a Subject
     #  as an additional header will result in a duplicate header.
     #
     #  PEAR mailer should be passed a Subject header.
     #
     # -- hashar 20120218
     $headers['From'] = $from->toString();
     $returnPath = $from->address;
     $extraParams = $wgAdditionalMailParams;
     // Hook to generate custom VERP address for 'Return-Path'
     Hooks::run('UserMailerChangeReturnPath', array($to, &$returnPath));
     # Add the envelope sender address using the -f command line option when PHP mail() is used.
     # Will default to the $from->address when the UserMailerChangeReturnPath hook fails and the
     # generated VERP address when the hook runs effectively.
     $extraParams .= ' -f ' . $returnPath;
     $headers['Return-Path'] = $returnPath;
     if ($replyto) {
         $headers['Reply-To'] = $replyto->toString();
     }
     $headers['Date'] = MWTimestamp::getLocalInstance()->format('r');
     $headers['Message-ID'] = self::makeMsgId();
     $headers['X-Mailer'] = 'MediaWiki mailer';
     # Line endings need to be different on Unix and Windows due to
     # the bug described at http://trac.wordpress.org/ticket/2603
     if (wfIsWindows()) {
         $endl = "\r\n";
     } else {
         $endl = "\n";
     }
     if (is_array($body)) {
         // we are sending a multipart message
         wfDebug("Assembling multipart mime email\n");
         if (!stream_resolve_include_path('Mail/mime.php')) {
             wfDebug("PEAR Mail_Mime package is not installed. Falling back to text email.\n");
             // remove the html body for text email fall back
             $body = $body['text'];
         } else {
             require_once 'Mail/mime.php';
             if (wfIsWindows()) {
                 $body['text'] = str_replace("\n", "\r\n", $body['text']);
                 $body['html'] = str_replace("\n", "\r\n", $body['html']);
             }
             $mime = new Mail_mime(array('eol' => $endl, 'text_charset' => 'UTF-8', 'html_charset' => 'UTF-8'));
             $mime->setTXTBody($body['text']);
             $mime->setHTMLBody($body['html']);
             $body = $mime->get();
             // must call get() before headers()
             $headers = $mime->headers($headers);
         }
     }
     if ($mime === null) {
         // sending text only, either deliberately or as a fallback
         if (wfIsWindows()) {
             $body = str_replace("\n", "\r\n", $body);
         }
         $headers['MIME-Version'] = '1.0';
         $headers['Content-type'] = is_null($contentType) ? 'text/plain; charset=UTF-8' : $contentType;
         $headers['Content-transfer-encoding'] = '8bit';
     }
     $ret = Hooks::run('AlternateUserMailer', array($headers, $to, $from, $subject, $body));
     if ($ret === false) {
         // the hook implementation will return false to skip regular mail sending
         return Status::newGood();
     } elseif ($ret !== true) {
         // the hook implementation will return a string to pass an error message
         return Status::newFatal('php-mail-error', $ret);
     }
     if (is_array($wgSMTP)) {
         #
         # PEAR MAILER
         #
         //start Eli Agbayani Nov 26, 2015
         $path = '/usr/local/Cellar/php53/5.3.29_2/lib/php';
         set_include_path(get_include_path() . PATH_SEPARATOR . $path);
         //end Eli Agbayani
         if (!stream_resolve_include_path('Mail.php')) {
             throw new MWException('PEAR mail package is not installed');
         }
         require_once 'Mail.php';
         wfSuppressWarnings();
         // Create the mail object using the Mail::factory method
         $mail_object =& Mail::factory('smtp', $wgSMTP);
         if (PEAR::isError($mail_object)) {
             wfDebug("PEAR::Mail factory failed: " . $mail_object->getMessage() . "\n");
             wfRestoreWarnings();
             return Status::newFatal('pear-mail-error', $mail_object->getMessage());
         }
         wfDebug("Sending mail via PEAR::Mail\n");
         $headers['Subject'] = self::quotedPrintable($subject);
         # When sending only to one recipient, shows it its email using To:
         if (count($to) == 1) {
             $headers['To'] = $to[0]->toString();
         }
         # Split jobs since SMTP servers tends to limit the maximum
         # number of possible recipients.
         $chunks = array_chunk($to, $wgEnotifMaxRecips);
         foreach ($chunks as $chunk) {
             $status = self::sendWithPear($mail_object, $chunk, $headers, $body);
             # FIXME : some chunks might be sent while others are not!
             if (!$status->isOK()) {
                 wfRestoreWarnings();
                 return $status;
             }
         }
         wfRestoreWarnings();
         return Status::newGood();
     } else {
         #
         # PHP mail()
         #
         if (count($to) > 1) {
             $headers['To'] = 'undisclosed-recipients:;';
         }
         $headers = self::arrayToHeaderString($headers, $endl);
         wfDebug("Sending mail via internal mail() function\n");
         self::$mErrorString = '';
         $html_errors = ini_get('html_errors');
         ini_set('html_errors', '0');
         set_error_handler('UserMailer::errorHandler');
         try {
             $safeMode = wfIniGetBool('safe_mode');
             foreach ($to as $recip) {
                 if ($safeMode) {
                     $sent = mail($recip, self::quotedPrintable($subject), $body, $headers);
                 } else {
                     $sent = mail($recip, self::quotedPrintable($subject), $body, $headers, $extraParams);
                 }
             }
         } catch (Exception $e) {
             restore_error_handler();
             throw $e;
         }
         restore_error_handler();
         ini_set('html_errors', $html_errors);
         if (self::$mErrorString) {
             wfDebug("Error sending mail: " . self::$mErrorString . "\n");
             return Status::newFatal('php-mail-error', self::$mErrorString);
         } elseif (!$sent) {
             // mail function only tells if there's an error
             wfDebug("Unknown error sending mail\n");
             return Status::newFatal('php-mail-error-unknown');
         } else {
             return Status::newGood();
         }
     }
 }
Esempio n. 23
0
 /**
  * @dataProvider provideWfShellWikiCmdList
  * @covers ::wfShellWikiCmd
  */
 public function testWfShellWikiCmd($script, $parameters, $options, $expected, $description)
 {
     if (wfIsWindows()) {
         // Approximation that's good enough for our purposes just now
         $expected = str_replace("'", '"', $expected);
     }
     $actual = wfShellWikiCmd($script, $parameters, $options);
     $this->assertEquals($expected, $actual, $description);
 }
Esempio n. 24
0
 /**
  * Store a batch of files
  *
  * @param array $triplets (src,zone,dest) triplets as per store()
  * @param integer $flags Bitwise combination of the following flags:
  *     self::DELETE_SOURCE     Delete the source file after upload
  *     self::OVERWRITE         Overwrite an existing destination file instead of failing
  *     self::OVERWRITE_SAME    Overwrite the file if the destination exists and has the
  *                             same contents as the source
  */
 function storeBatch($triplets, $flags = 0)
 {
     if (!wfMkdirParents($this->directory)) {
         return $this->newFatal('upload_directory_missing', $this->directory);
     }
     if (!is_writable($this->directory)) {
         return $this->newFatal('upload_directory_read_only', $this->directory);
     }
     $status = $this->newGood();
     foreach ($triplets as $i => $triplet) {
         list($srcPath, $dstZone, $dstRel) = $triplet;
         $root = $this->getZonePath($dstZone);
         if (!$root) {
             throw new MWException("Invalid zone: {$dstZone}");
         }
         if (!$this->validateFilename($dstRel)) {
             throw new MWException('Validation error in $dstRel');
         }
         $dstPath = "{$root}/{$dstRel}";
         $dstDir = dirname($dstPath);
         if (!is_dir($dstDir)) {
             if (!wfMkdirParents($dstDir)) {
                 return $this->newFatal('directorycreateerror', $dstDir);
             }
             if ($dstZone == 'deleted') {
                 $this->initDeletedDir($dstDir);
             }
         }
         if (self::isVirtualUrl($srcPath)) {
             $srcPath = $triplets[$i][0] = $this->resolveVirtualUrl($srcPath);
         }
         if (!is_file($srcPath)) {
             // Make a list of files that don't exist for return to the caller
             $status->fatal('filenotfound', $srcPath);
             continue;
         }
         if (!($flags & self::OVERWRITE) && file_exists($dstPath)) {
             if ($flags & self::OVERWRITE_SAME) {
                 $hashSource = sha1_file($srcPath);
                 $hashDest = sha1_file($dstPath);
                 if ($hashSource != $hashDest) {
                     $status->fatal('fileexistserror', $dstPath);
                 }
             } else {
                 $status->fatal('fileexistserror', $dstPath);
             }
         }
     }
     $deleteDest = wfIsWindows() && $flags & self::OVERWRITE;
     // Abort now on failure
     if (!$status->ok) {
         return $status;
     }
     foreach ($triplets as $triplet) {
         list($srcPath, $dstZone, $dstRel) = $triplet;
         $root = $this->getZonePath($dstZone);
         $dstPath = "{$root}/{$dstRel}";
         $good = true;
         if ($flags & self::DELETE_SOURCE) {
             if ($deleteDest) {
                 unlink($dstPath);
             }
             if (!rename($srcPath, $dstPath)) {
                 $status->error('filerenameerror', $srcPath, $dstPath);
                 $good = false;
             }
         } else {
             if (!copy($srcPath, $dstPath)) {
                 $status->error('filecopyerror', $srcPath, $dstPath);
                 $good = false;
             }
         }
         if ($good) {
             chmod($dstPath, 0644);
             $status->successCount++;
         } else {
             $status->failCount++;
         }
     }
     return $status;
 }
Esempio n. 25
0
 /**
  * Armour a string against ImageMagick's GetPathComponent(). This is a
  * helper function for escapeMagickInput() and escapeMagickOutput().
  *
  * @param $path string The file path
  * @param $scene string The scene specification, or false if there is none
  */
 protected function escapeMagickPath($path, $scene = false)
 {
     # Die on format specifiers (other than drive letters). The regex is
     # meant to match all the formats you get from "convert -list format"
     if (preg_match('/^([a-zA-Z0-9-]+):/', $path, $m)) {
         if (wfIsWindows() && is_dir($m[0])) {
             // OK, it's a drive letter
             // ImageMagick has a similar exception, see IsMagickConflict()
         } else {
             throw new MWException(__METHOD__ . ': unexpected colon character in path name');
         }
     }
     # If there are square brackets, add a do-nothing scene specification
     # to force a literal interpretation
     if ($scene === false) {
         if (strpos($path, '[') !== false) {
             $path .= '[0--1]';
         }
     } else {
         $path .= "[{$scene}]";
     }
     return $path;
 }
Esempio n. 26
0
 function close()
 {
     if (isset($this->handle)) {
         dba_close($this->handle);
     }
     if (wfIsWindows()) {
         unlink($this->realFileName);
     }
     if (!rename($this->tmpFileName, $this->realFileName)) {
         throw new MWException('Unable to move the new CDB file into place.');
     }
     chmod($this->realFileName, 0664);
     unset($this->handle);
 }
Esempio n. 27
0
 /**
  * Environment check for ImageMagick and GD.
  */
 protected function envCheckGraphics()
 {
     $names = array(wfIsWindows() ? 'convert.exe' : 'convert');
     $convert = self::locateExecutableInDefaultPaths($names, array('$1 -version', 'ImageMagick'));
     $this->setVar('wgImageMagickConvertCommand', '');
     if ($convert) {
         $this->setVar('wgImageMagickConvertCommand', $convert);
         $this->showMessage('config-imagemagick', $convert);
         return true;
     } elseif (function_exists('imagejpeg')) {
         $this->showMessage('config-gd');
         return true;
     } else {
         $this->showMessage('config-no-scaling');
     }
 }
Esempio n. 28
0
 /**
  * Returns the title of the file to be uploaded. Sets mTitleError in case
  * the name was illegal.
  *
  * @return Title The title of the file or null in case the name was illegal
  */
 public function getTitle()
 {
     if ($this->mTitle !== false) {
         return $this->mTitle;
     }
     /* Assume that if a user specified File:Something.jpg, this is an error
      * and that the namespace prefix needs to be stripped of.
      */
     $title = Title::newFromText($this->mDesiredDestName);
     if ($title && $title->getNamespace() == NS_FILE) {
         $this->mFilteredName = $title->getDBkey();
     } else {
         $this->mFilteredName = $this->mDesiredDestName;
     }
     # oi_archive_name is max 255 bytes, which include a timestamp and an
     # exclamation mark, so restrict file name to 240 bytes.
     if (strlen($this->mFilteredName) > 240) {
         $this->mTitleError = self::FILENAME_TOO_LONG;
         $this->mTitle = null;
         return $this->mTitle;
     }
     /**
      * Chop off any directories in the given filename. Then
      * filter out illegal characters, and try to make a legible name
      * out of it. We'll strip some silently that Title would die on.
      */
     $this->mFilteredName = wfStripIllegalFilenameChars($this->mFilteredName);
     /* Normalize to title form before we do any further processing */
     $nt = Title::makeTitleSafe(NS_FILE, $this->mFilteredName);
     if (is_null($nt)) {
         $this->mTitleError = self::ILLEGAL_FILENAME;
         $this->mTitle = null;
         return $this->mTitle;
     }
     $this->mFilteredName = $nt->getDBkey();
     /**
      * We'll want to blacklist against *any* 'extension', and use
      * only the final one for the whitelist.
      */
     list($partname, $ext) = $this->splitExtensions($this->mFilteredName);
     if (count($ext)) {
         $this->mFinalExtension = trim($ext[count($ext) - 1]);
     } else {
         $this->mFinalExtension = '';
         # No extension, try guessing one
         $magic = MimeMagic::singleton();
         $mime = $magic->guessMimeType($this->mTempPath);
         if ($mime !== 'unknown/unknown') {
             # Get a space separated list of extensions
             $extList = $magic->getExtensionsForType($mime);
             if ($extList) {
                 # Set the extension to the canonical extension
                 $this->mFinalExtension = strtok($extList, ' ');
                 # Fix up the other variables
                 $this->mFilteredName .= ".{$this->mFinalExtension}";
                 $nt = Title::makeTitleSafe(NS_FILE, $this->mFilteredName);
                 $ext = array($this->mFinalExtension);
             }
         }
     }
     /* Don't allow users to override the blacklist (check file extension) */
     global $wgCheckFileExtensions, $wgStrictFileExtensions;
     global $wgFileExtensions, $wgFileBlacklist;
     $blackListedExtensions = $this->checkFileExtensionList($ext, $wgFileBlacklist);
     if ($this->mFinalExtension == '') {
         $this->mTitleError = self::FILETYPE_MISSING;
         $this->mTitle = null;
         return $this->mTitle;
     } elseif ($blackListedExtensions || $wgCheckFileExtensions && $wgStrictFileExtensions && !$this->checkFileExtension($this->mFinalExtension, $wgFileExtensions)) {
         $this->mBlackListedExtensions = $blackListedExtensions;
         $this->mTitleError = self::FILETYPE_BADTYPE;
         $this->mTitle = null;
         return $this->mTitle;
     }
     // Windows may be broken with special characters, see bug XXX
     if (wfIsWindows() && !preg_match('/^[\\x0-\\x7f]*$/', $nt->getText())) {
         $this->mTitleError = self::WINDOWS_NONASCII_FILENAME;
         $this->mTitle = null;
         return $this->mTitle;
     }
     # If there was more than one "extension", reassemble the base
     # filename to prevent bogus complaints about length
     if (count($ext) > 1) {
         for ($i = 0; $i < count($ext) - 1; $i++) {
             $partname .= '.' . $ext[$i];
         }
     }
     if (strlen($partname) < 1) {
         $this->mTitleError = self::MIN_LENGTH_PARTNAME;
         $this->mTitle = null;
         return $this->mTitle;
     }
     $this->mTitle = $nt;
     return $this->mTitle;
 }
Esempio n. 29
0
 /** Generic wrapper function for a virus scanner program.
  * This relies on the $wgAntivirus and $wgAntivirusSetup variables.
  * $wgAntivirusRequired may be used to deny upload if the scan fails.
  *
  * @param string $file Pathname to the temporary upload file
  * @return mixed false if not virus is found, NULL if the scan fails or is disabled,
  *         or a string containing feedback from the virus scanner if a virus was found.
  *         If textual feedback is missing but a virus was found, this function returns true.
  */
 function detectVirus($file)
 {
     global $wgAntivirus, $wgAntivirusSetup, $wgAntivirusRequired, $wgOut;
     $fname = "SpecialUpload::detectVirus";
     if (!$wgAntivirus) {
         #disabled?
         wfDebug("{$fname}: virus scanner disabled\n");
         return NULL;
     }
     if (!$wgAntivirusSetup[$wgAntivirus]) {
         wfDebug("{$fname}: unknown virus scanner: {$wgAntivirus}\n");
         $wgOut->addHTML("<div class='error'>Bad configuration: unknown virus scanner: <i>{$wgAntivirus}</i></div>\n");
         #LOCALIZE
         return "unknown antivirus: {$wgAntivirus}";
     }
     #look up scanner configuration
     $virus_scanner = $wgAntivirusSetup[$wgAntivirus]["command"];
     #command pattern
     $virus_scanner_codes = $wgAntivirusSetup[$wgAntivirus]["codemap"];
     #exit-code map
     $msg_pattern = $wgAntivirusSetup[$wgAntivirus]["messagepattern"];
     #message pattern
     $scanner = $virus_scanner;
     #copy, so we can resolve the pattern
     if (strpos($scanner, "%f") === false) {
         $scanner .= " " . wfEscapeShellArg($file);
     } else {
         $scanner = str_replace("%f", wfEscapeShellArg($file), $scanner);
     }
     #complex pattern: replace "%f" with file to scan
     wfDebug("{$fname}: running virus scan: {$scanner} \n");
     #execute virus scanner
     $code = false;
     #NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
     #      that does not seem to be worth the pain.
     #      Ask me (Duesentrieb) about it if it's ever needed.
     if (wfIsWindows()) {
         exec("{$scanner}", $output, $code);
     } else {
         exec("{$scanner} 2>&1", $output, $code);
     }
     $exit_code = $code;
     #remeber for user feedback
     if ($virus_scanner_codes) {
         #map exit code to AV_xxx constants.
         if (isset($virus_scanner_codes[$code])) {
             $code = $virus_scanner_codes[$code];
         } else {
             if (isset($virus_scanner_codes["*"])) {
                 $code = $virus_scanner_codes["*"];
             }
         }
         #fallback mapping
     }
     if ($code === AV_SCAN_FAILED) {
         #scan failed (code was mapped to false by $virus_scanner_codes)
         wfDebug("{$fname}: failed to scan {$file} (code {$exit_code}).\n");
         if ($wgAntivirusRequired) {
             return "scan failed (code {$exit_code})";
         } else {
             return NULL;
         }
     } else {
         if ($code === AV_SCAN_ABORTED) {
             #scan failed because filetype is unknown (probably imune)
             wfDebug("{$fname}: unsupported file type {$file} (code {$exit_code}).\n");
             return NULL;
         } else {
             if ($code === AV_NO_VIRUS) {
                 wfDebug("{$fname}: file passed virus scan.\n");
                 return false;
                 #no virus found
             } else {
                 $output = join("\n", $output);
                 $output = trim($output);
                 if (!$output) {
                     $output = true;
                 } else {
                     if ($msg_pattern) {
                         $groups = array();
                         if (preg_match($msg_pattern, $output, $groups)) {
                             if ($groups[1]) {
                                 $output = $groups[1];
                             }
                         }
                     }
                 }
                 wfDebug("{$fname}: FOUND VIRUS! scanner feedback: {$output}");
                 return $output;
             }
         }
     }
 }
Esempio n. 30
0
 /**
  * @see self::generate()
  */
 public function realGenerate($bytes, $forceStrong = false)
 {
     wfDebug(__METHOD__ . ": Generating cryptographic random bytes for " . wfGetAllCallers(5) . "\n");
     $bytes = floor($bytes);
     static $buffer = '';
     if (is_null($this->strong)) {
         // Set strength to false initially until we know what source data is coming from
         $this->strong = true;
     }
     if (strlen($buffer) < $bytes) {
         // If available make use of mcrypt_create_iv URANDOM source to generate randomness
         // On unix-like systems this reads from /dev/urandom but does it without any buffering
         // and bypasses openbasedir restrictions, so it's preferable to reading directly
         // On Windows starting in PHP 5.3.0 Windows' native CryptGenRandom is used to generate
         // entropy so this is also preferable to just trying to read urandom because it may work
         // on Windows systems as well.
         if (function_exists('mcrypt_create_iv')) {
             $rem = $bytes - strlen($buffer);
             $iv = mcrypt_create_iv($rem, MCRYPT_DEV_URANDOM);
             if ($iv === false) {
                 wfDebug(__METHOD__ . ": mcrypt_create_iv returned false.\n");
             } else {
                 $buffer .= $iv;
                 wfDebug(__METHOD__ . ": mcrypt_create_iv generated " . strlen($iv) . " bytes of randomness.\n");
             }
         }
     }
     if (strlen($buffer) < $bytes) {
         // If available make use of openssl's random_pseudo_bytes method to
         // attempt to generate randomness. However don't do this on Windows
         // with PHP < 5.3.4 due to a bug:
         // http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
         // http://git.php.net/?p=php-src.git;a=commitdiff;h=cd62a70863c261b07f6dadedad9464f7e213cad5
         if (function_exists('openssl_random_pseudo_bytes') && (!wfIsWindows() || version_compare(PHP_VERSION, '5.3.4', '>='))) {
             $rem = $bytes - strlen($buffer);
             $openssl_bytes = openssl_random_pseudo_bytes($rem, $openssl_strong);
             if ($openssl_bytes === false) {
                 wfDebug(__METHOD__ . ": openssl_random_pseudo_bytes returned false.\n");
             } else {
                 $buffer .= $openssl_bytes;
                 wfDebug(__METHOD__ . ": openssl_random_pseudo_bytes generated " . strlen($openssl_bytes) . " bytes of " . ($openssl_strong ? "strong" : "weak") . " randomness.\n");
             }
             if (strlen($buffer) >= $bytes) {
                 // openssl tells us if the random source was strong, if some of our data was generated
                 // using it use it's say on whether the randomness is strong
                 $this->strong = !!$openssl_strong;
             }
         }
     }
     // Only read from urandom if we can control the buffer size or were passed forceStrong
     if (strlen($buffer) < $bytes && (function_exists('stream_set_read_buffer') || $forceStrong)) {
         $rem = $bytes - strlen($buffer);
         if (!function_exists('stream_set_read_buffer') && $forceStrong) {
             wfDebug(__METHOD__ . ": Was forced to read from /dev/urandom " . "without control over the buffer size.\n");
         }
         // /dev/urandom is generally considered the best possible commonly
         // available random source, and is available on most *nix systems.
         MediaWiki\suppressWarnings();
         $urandom = fopen("/dev/urandom", "rb");
         MediaWiki\restoreWarnings();
         // Attempt to read all our random data from urandom
         // php's fread always does buffered reads based on the stream's chunk_size
         // so in reality it will usually read more than the amount of data we're
         // asked for and not storing that risks depleting the system's random pool.
         // If stream_set_read_buffer is available set the chunk_size to the amount
         // of data we need. Otherwise read 8k, php's default chunk_size.
         if ($urandom) {
             // php's default chunk_size is 8k
             $chunk_size = 1024 * 8;
             if (function_exists('stream_set_read_buffer')) {
                 // If possible set the chunk_size to the amount of data we need
                 stream_set_read_buffer($urandom, $rem);
                 $chunk_size = $rem;
             }
             $random_bytes = fread($urandom, max($chunk_size, $rem));
             $buffer .= $random_bytes;
             fclose($urandom);
             wfDebug(__METHOD__ . ": /dev/urandom generated " . strlen($random_bytes) . " bytes of randomness.\n");
             if (strlen($buffer) >= $bytes) {
                 // urandom is always strong, set to true if all our data was generated using it
                 $this->strong = true;
             }
         } else {
             wfDebug(__METHOD__ . ": /dev/urandom could not be opened.\n");
         }
     }
     // If we cannot use or generate enough data from a secure source
     // use this loop to generate a good set of pseudo random data.
     // This works by initializing a random state using a pile of unstable data
     // and continually shoving it through a hash along with a variable salt.
     // We hash the random state with more salt to avoid the state from leaking
     // out and being used to predict the /randomness/ that follows.
     if (strlen($buffer) < $bytes) {
         wfDebug(__METHOD__ . ": Falling back to using a pseudo random state to generate randomness.\n");
     }
     while (strlen($buffer) < $bytes) {
         $buffer .= MWCryptHash::hmac($this->randomState(), mt_rand());
         // This code is never really cryptographically strong, if we use it
         // at all, then set strong to false.
         $this->strong = false;
     }
     // Once the buffer has been filled up with enough random data to fulfill
     // the request shift off enough data to handle the request and leave the
     // unused portion left inside the buffer for the next request for random data
     $generated = substr($buffer, 0, $bytes);
     $buffer = substr($buffer, $bytes);
     wfDebug(__METHOD__ . ": " . strlen($buffer) . " bytes of randomness leftover in the buffer.\n");
     return $generated;
 }