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); }
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; }
/** * 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'; }
/** * 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; } }
/** * 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); }
/** * 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."); }
/** * 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 ""; } }
/** * 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); }
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; } } } }
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; }
/** * 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; } }
/** * 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; } }
/** * 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(); } } }
/** * @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); }
/** * 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; }
/** * 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; }
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); }
/** * 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'); } }
/** * 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; }
/** 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; } } } }
/** * @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; }