Index: trunk/extensions/NSFileRepo/REL1_13_0/phase3/img_auth.i18n.php |
— | — | @@ -0,0 +1,32 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Internationalisation file for img_auth script (see see http://www.mediawiki.org/wiki/Manual:Image_Authorization). |
| 5 | +*/ |
| 6 | + |
| 7 | +$messages = array(); |
| 8 | + |
| 9 | +/** English |
| 10 | + * @author Jack D. Pond |
| 11 | + */ |
| 12 | +$messages['en'] = array( |
| 13 | + 'image_auth-desc' => 'Image authorisation script', |
| 14 | + 'image_auth-nopathinfo' => "Missing PATH_INFO. Your server is not set up to pass this information - |
| 15 | +may be CGI-based and can't support img_auth. See `Image Authorization` on MediaWiki.", |
| 16 | + 'image_auth-notindir' => "Requested path not in upload directory.", |
| 17 | + 'image_auth-badtitle' => "Unable to construct a valid Title from `$1`.", |
| 18 | + 'image_auth-nologinnWL' => "Not logged in and `$1` not in whitelist.", |
| 19 | + 'image_auth-nofile' => "`$1` does not exist.", |
| 20 | + 'image_auth-isdir' => "`$1` is a directory.", |
| 21 | + 'image_auth-streaming' => "Streaming `$1`.", |
| 22 | + 'image_auth-public' => "The function of img_auth.php is to output files from a private wiki. This wiki |
| 23 | +is configured as a public wiki. For optimal security, img_auth.php is disabled for this case.", |
| 24 | + 'image_auth-noread' => "User does not have access to read `$1`." |
| 25 | +); |
| 26 | + |
| 27 | +/** Message documentation (Message documentation) |
| 28 | + * @author Jack D. Pond |
| 29 | + */ |
| 30 | +$messages['qqq'] = array( |
| 31 | + 'image_auth-desc' => 'Image authorisation script' |
| 32 | +); |
| 33 | + |
Property changes on: trunk/extensions/NSFileRepo/REL1_13_0/phase3/img_auth.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 34 | + native |
Index: trunk/extensions/NSFileRepo/REL1_13_0/phase3/img_auth.php |
— | — | @@ -0,0 +1,136 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Image authorisation script |
| 6 | + * |
| 7 | + * To use this, see http://www.mediawiki.org/wiki/Manual:Image_Authorization |
| 8 | + * |
| 9 | + * - Set $wgUploadDirectory to a non-public directory (not web accessible) |
| 10 | + * - Set $wgUploadPath to point to this file |
| 11 | + * |
| 12 | + * Your server needs to support PATH_INFO; CGI-based configurations usually don't. |
| 13 | + * |
| 14 | + * @file |
| 15 | + */ |
| 16 | + |
| 17 | + |
| 18 | +/** |
| 19 | + For security reasons, you usually don't want your user to know access was denied, just that it was. |
| 20 | + If you want to change this, you can set $wgImgAuthDetails to 'true' in localsettings.php and it will give the user the reason |
| 21 | + why access was denied. |
| 22 | +**/ |
| 23 | + |
| 24 | +global $wgImgAuthDetails; |
| 25 | +$wgImgAuthDetails = false; |
| 26 | + |
| 27 | +define( 'MW_NO_OUTPUT_COMPRESSION', 1 ); |
| 28 | +require_once( dirname( __FILE__ ) . '/includes/WebStart.php' ); |
| 29 | +wfProfileIn( 'img_auth.php' ); |
| 30 | +require_once( dirname( __FILE__ ) . '/includes/StreamFile.php' ); |
| 31 | + |
| 32 | +global $wgMessageCache, $messages; |
| 33 | +require_once( dirname( __FILE__ ) . '/img_auth.i18n.php' ); |
| 34 | +foreach( $messages as $lang => $LangMsg ) |
| 35 | + $wgMessageCache->addMessages( $LangMsg, $lang ); |
| 36 | + |
| 37 | +$perms = User::getGroupPermissions( array( '*' ) ); |
| 38 | + |
| 39 | +// See if this is a public Wiki (no protections) |
| 40 | +if ( in_array( 'read', $perms, true ) ) |
| 41 | + wfPublicError(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-public')); |
| 42 | + |
| 43 | +// Extract path and image information |
| 44 | +if( !isset( $_SERVER['PATH_INFO'] ) ) |
| 45 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-nopathinfo')); |
| 46 | + |
| 47 | +$path = $_SERVER['PATH_INFO']; |
| 48 | +$filename = realpath( $wgUploadDirectory . $_SERVER['PATH_INFO'] ); |
| 49 | +$realUpload = realpath( $wgUploadDirectory ); |
| 50 | + |
| 51 | +// Basic directory traversal check |
| 52 | +if( substr( $filename, 0, strlen( $realUpload ) ) != $realUpload ) |
| 53 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-notindir')); |
| 54 | + |
| 55 | +// Extract the file name and chop off the size specifier |
| 56 | +// (e.g. 120px-Foo.png => Foo.png) |
| 57 | +$name = wfBaseName( $path ); |
| 58 | +if( preg_match( '!\d+px-(.*)!i', $name, $m ) ) |
| 59 | + $name = $m[1]; |
| 60 | + |
| 61 | +// Check to see if the file exists |
| 62 | +if( !file_exists( $filename ) ) |
| 63 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-nofile',$filename)); |
| 64 | + |
| 65 | +// Check to see if tried to access a directory |
| 66 | +if( is_dir( $filename ) ) |
| 67 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-isdir',$filename)); |
| 68 | + |
| 69 | + |
| 70 | +$title = Title::makeTitleSafe( NS_FILE, $name ); |
| 71 | + |
| 72 | +// See if could create the title object |
| 73 | +if( !$title instanceof Title ) |
| 74 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-badtitle',$name)); |
| 75 | + |
| 76 | +// Run hook |
| 77 | +if (!wfRunHooks( 'ImgAuthBeforeStream', array( &$title, &$path, &$name, &$result ) ) ) |
| 78 | + wfForbidden($result[0],$result[1]); |
| 79 | + |
| 80 | +// Check the whitelist if needed, deprecated since usercan added |
| 81 | +// $pTitle = $title->getPrefixedText(); |
| 82 | +// if( !$wgUser->getId() && ( !is_array( $wgWhitelistRead ) || !in_array( $pTitle, $wgWhitelistRead ) ) ) |
| 83 | +// wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-nologinnWL',$pTitle)); |
| 84 | + |
| 85 | + |
| 86 | +// Check user authorization for this title |
| 87 | +if( !$title->userCanRead() ) |
| 88 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-noread',$name)); |
| 89 | + |
| 90 | + |
| 91 | +// Stream the requested file |
| 92 | +wfDebugLog( 'img_auth', "Streaming `{$filename}`" ); |
| 93 | +wfStreamFile( $filename, array( 'Cache-Control: private', 'Vary: Cookie' ) ); |
| 94 | +wfLogProfilingData(); |
| 95 | + |
| 96 | +/** |
| 97 | + * Issue a standard HTTP 403 Forbidden header ($msg1) and an |
| 98 | + * error message ($msg2), then end the script |
| 99 | + */ |
| 100 | +function wfForbidden($msg1,$msg2) { |
| 101 | + global $wgImgAuthDetails; |
| 102 | + $detailMsg = $wgImgAuthDetails ? $msg2 : wfMsgHTML('badaccess-group0'); |
| 103 | + wfDebugLog( 'img_auth', "wfForbidden Msg: ".$msg2 ); |
| 104 | + header( 'HTTP/1.0 403 Forbidden' ); |
| 105 | + header( 'Vary: Cookie' ); |
| 106 | + header( 'Content-Type: text/html; charset=utf-8' ); |
| 107 | + echo <<<ENDS |
| 108 | +<html> |
| 109 | +<body> |
| 110 | +<h1>$msg1</h1> |
| 111 | +<p>$detailMsg</p> |
| 112 | +</body> |
| 113 | +</html> |
| 114 | +ENDS; |
| 115 | + wfLogProfilingData(); |
| 116 | + exit(); |
| 117 | +} |
| 118 | + |
| 119 | +/** |
| 120 | + * Show a 403 error for use when the wiki is public |
| 121 | + */ |
| 122 | +function wfPublicError($msg1,$msg2) { |
| 123 | + header( 'HTTP/1.0 403 Forbidden' ); |
| 124 | + header( 'Content-Type: text/html; charset=utf-8' ); |
| 125 | + wfDebugLog( 'img_auth', "wfPublicError Msg: ".$msg2 ); |
| 126 | + echo <<<ENDS |
| 127 | +<html> |
| 128 | +<body> |
| 129 | +<h1>$msg1</h1> |
| 130 | +<p>$msg2</p> |
| 131 | +</body> |
| 132 | +</html> |
| 133 | +ENDS; |
| 134 | + wfLogProfilingData(); |
| 135 | + exit; |
| 136 | +} |
| 137 | + |
Property changes on: trunk/extensions/NSFileRepo/REL1_13_0/phase3/img_auth.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 138 | + native |
Index: trunk/extensions/NSFileRepo/REL1_13_0/phase3/includes/GlobalFunctions.php |
— | — | @@ -0,0 +1,2769 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 5 | + die( "This file is part of MediaWiki, it is not a valid entry point" ); |
| 6 | +} |
| 7 | + |
| 8 | +/** |
| 9 | + * Global functions used everywhere |
| 10 | + */ |
| 11 | + |
| 12 | +require_once dirname(__FILE__) . '/LogPage.php'; |
| 13 | +require_once dirname(__FILE__) . '/normal/UtfNormalUtil.php'; |
| 14 | +require_once dirname(__FILE__) . '/XmlFunctions.php'; |
| 15 | + |
| 16 | +/** |
| 17 | + * Compatibility functions |
| 18 | + * |
| 19 | + * We more or less support PHP 5.0.x and up. |
| 20 | + * Re-implementations of newer functions or functions in non-standard |
| 21 | + * PHP extensions may be included here. |
| 22 | + */ |
| 23 | +if( !function_exists('iconv') ) { |
| 24 | + # iconv support is not in the default configuration and so may not be present. |
| 25 | + # Assume will only ever use utf-8 and iso-8859-1. |
| 26 | + # This will *not* work in all circumstances. |
| 27 | + function iconv( $from, $to, $string ) { |
| 28 | + if(strcasecmp( $from, $to ) == 0) return $string; |
| 29 | + if(strcasecmp( $from, 'utf-8' ) == 0) return utf8_decode( $string ); |
| 30 | + if(strcasecmp( $to, 'utf-8' ) == 0) return utf8_encode( $string ); |
| 31 | + return $string; |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +# UTF-8 substr function based on a PHP manual comment |
| 36 | +if ( !function_exists( 'mb_substr' ) ) { |
| 37 | + function mb_substr( $str, $start ) { |
| 38 | + $ar = array(); |
| 39 | + preg_match_all( '/./us', $str, $ar ); |
| 40 | + |
| 41 | + if( func_num_args() >= 3 ) { |
| 42 | + $end = func_get_arg( 2 ); |
| 43 | + return join( '', array_slice( $ar[0], $start, $end ) ); |
| 44 | + } else { |
| 45 | + return join( '', array_slice( $ar[0], $start ) ); |
| 46 | + } |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +if ( !function_exists( 'mb_strlen' ) ) { |
| 51 | + /** |
| 52 | + * Fallback implementation of mb_strlen, hardcoded to UTF-8. |
| 53 | + * @param string $str |
| 54 | + * @param string $enc optional encoding; ignored |
| 55 | + * @return int |
| 56 | + */ |
| 57 | + function mb_strlen( $str, $enc="" ) { |
| 58 | + $counts = count_chars( $str ); |
| 59 | + $total = 0; |
| 60 | + |
| 61 | + // Count ASCII bytes |
| 62 | + for( $i = 0; $i < 0x80; $i++ ) { |
| 63 | + $total += $counts[$i]; |
| 64 | + } |
| 65 | + |
| 66 | + // Count multibyte sequence heads |
| 67 | + for( $i = 0xc0; $i < 0xff; $i++ ) { |
| 68 | + $total += $counts[$i]; |
| 69 | + } |
| 70 | + return $total; |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +if ( !function_exists( 'array_diff_key' ) ) { |
| 75 | + /** |
| 76 | + * Exists in PHP 5.1.0+ |
| 77 | + * Not quite compatible, two-argument version only |
| 78 | + * Null values will cause problems due to this use of isset() |
| 79 | + */ |
| 80 | + function array_diff_key( $left, $right ) { |
| 81 | + $result = $left; |
| 82 | + foreach ( $left as $key => $unused ) { |
| 83 | + if ( isset( $right[$key] ) ) { |
| 84 | + unset( $result[$key] ); |
| 85 | + } |
| 86 | + } |
| 87 | + return $result; |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +/** |
| 92 | + * Like array_diff( $a, $b ) except that it works with two-dimensional arrays. |
| 93 | + */ |
| 94 | +function wfArrayDiff2( $a, $b ) { |
| 95 | + return array_udiff( $a, $b, 'wfArrayDiff2_cmp' ); |
| 96 | +} |
| 97 | +function wfArrayDiff2_cmp( $a, $b ) { |
| 98 | + if ( !is_array( $a ) ) { |
| 99 | + return strcmp( $a, $b ); |
| 100 | + } elseif ( count( $a ) !== count( $b ) ) { |
| 101 | + return count( $a ) < count( $b ) ? -1 : 1; |
| 102 | + } else { |
| 103 | + reset( $a ); |
| 104 | + reset( $b ); |
| 105 | + while( ( list( $keyA, $valueA ) = each( $a ) ) && ( list( $keyB, $valueB ) = each( $b ) ) ) { |
| 106 | + $cmp = strcmp( $valueA, $valueB ); |
| 107 | + if ( $cmp !== 0 ) { |
| 108 | + return $cmp; |
| 109 | + } |
| 110 | + } |
| 111 | + return 0; |
| 112 | + } |
| 113 | +} |
| 114 | + |
| 115 | +/** |
| 116 | + * Wrapper for clone(), for compatibility with PHP4-friendly extensions. |
| 117 | + * PHP 5 won't let you declare a 'clone' function, even conditionally, |
| 118 | + * so it has to be a wrapper with a different name. |
| 119 | + */ |
| 120 | +function wfClone( $object ) { |
| 121 | + return clone( $object ); |
| 122 | +} |
| 123 | + |
| 124 | +/** |
| 125 | + * Seed Mersenne Twister |
| 126 | + * No-op for compatibility; only necessary in PHP < 4.2.0 |
| 127 | + */ |
| 128 | +function wfSeedRandom() { |
| 129 | + /* No-op */ |
| 130 | +} |
| 131 | + |
| 132 | +/** |
| 133 | + * Get a random decimal value between 0 and 1, in a way |
| 134 | + * not likely to give duplicate values for any realistic |
| 135 | + * number of articles. |
| 136 | + * |
| 137 | + * @return string |
| 138 | + */ |
| 139 | +function wfRandom() { |
| 140 | + # The maximum random value is "only" 2^31-1, so get two random |
| 141 | + # values to reduce the chance of dupes |
| 142 | + $max = mt_getrandmax() + 1; |
| 143 | + $rand = number_format( (mt_rand() * $max + mt_rand()) |
| 144 | + / $max / $max, 12, '.', '' ); |
| 145 | + return $rand; |
| 146 | +} |
| 147 | + |
| 148 | +/** |
| 149 | + * We want / and : to be included as literal characters in our title URLs. |
| 150 | + * %2F in the page titles seems to fatally break for some reason. |
| 151 | + * |
| 152 | + * @param $s String: |
| 153 | + * @return string |
| 154 | +*/ |
| 155 | +function wfUrlencode ( $s ) { |
| 156 | + $s = urlencode( $s ); |
| 157 | + $s = preg_replace( '/%3[Aa]/', ':', $s ); |
| 158 | + $s = preg_replace( '/%2[Ff]/', '/', $s ); |
| 159 | + |
| 160 | + return $s; |
| 161 | +} |
| 162 | + |
| 163 | +/** |
| 164 | + * Sends a line to the debug log if enabled or, optionally, to a comment in output. |
| 165 | + * In normal operation this is a NOP. |
| 166 | + * |
| 167 | + * Controlling globals: |
| 168 | + * $wgDebugLogFile - points to the log file |
| 169 | + * $wgProfileOnly - if set, normal debug messages will not be recorded. |
| 170 | + * $wgDebugRawPage - if false, 'action=raw' hits will not result in debug output. |
| 171 | + * $wgDebugComments - if on, some debug items may appear in comments in the HTML output. |
| 172 | + * |
| 173 | + * @param $text String |
| 174 | + * @param $logonly Bool: set true to avoid appearing in HTML when $wgDebugComments is set |
| 175 | + */ |
| 176 | +function wfDebug( $text, $logonly = false ) { |
| 177 | + global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage; |
| 178 | + static $recursion = 0; |
| 179 | + |
| 180 | + static $cache = array(); // Cache of unoutputted messages |
| 181 | + |
| 182 | + # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet |
| 183 | + if ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' && !$wgDebugRawPage ) { |
| 184 | + return; |
| 185 | + } |
| 186 | + |
| 187 | + if ( $wgDebugComments && !$logonly ) { |
| 188 | + $cache[] = $text; |
| 189 | + |
| 190 | + if ( !isset( $wgOut ) ) { |
| 191 | + return; |
| 192 | + } |
| 193 | + if ( !StubObject::isRealObject( $wgOut ) ) { |
| 194 | + if ( $recursion ) { |
| 195 | + return; |
| 196 | + } |
| 197 | + $recursion++; |
| 198 | + $wgOut->_unstub(); |
| 199 | + $recursion--; |
| 200 | + } |
| 201 | + |
| 202 | + // add the message and possible cached ones to the output |
| 203 | + array_map( array( $wgOut, 'debug' ), $cache ); |
| 204 | + $cache = array(); |
| 205 | + } |
| 206 | + if ( '' != $wgDebugLogFile && !$wgProfileOnly ) { |
| 207 | + # Strip unprintables; they can switch terminal modes when binary data |
| 208 | + # gets dumped, which is pretty annoying. |
| 209 | + $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text ); |
| 210 | + wfErrorLog( $text, $wgDebugLogFile ); |
| 211 | + } |
| 212 | +} |
| 213 | + |
| 214 | +/** |
| 215 | + * Send a line to a supplementary debug log file, if configured, or main debug log if not. |
| 216 | + * $wgDebugLogGroups[$logGroup] should be set to a filename to send to a separate log. |
| 217 | + * |
| 218 | + * @param $logGroup String |
| 219 | + * @param $text String |
| 220 | + * @param $public Bool: whether to log the event in the public log if no private |
| 221 | + * log file is specified, (default true) |
| 222 | + */ |
| 223 | +function wfDebugLog( $logGroup, $text, $public = true ) { |
| 224 | + global $wgDebugLogGroups; |
| 225 | + if( $text{strlen( $text ) - 1} != "\n" ) $text .= "\n"; |
| 226 | + if( isset( $wgDebugLogGroups[$logGroup] ) ) { |
| 227 | + $time = wfTimestamp( TS_DB ); |
| 228 | + $wiki = wfWikiID(); |
| 229 | + wfErrorLog( "$time $wiki: $text", $wgDebugLogGroups[$logGroup] ); |
| 230 | + } else if ( $public === true ) { |
| 231 | + wfDebug( $text, true ); |
| 232 | + } |
| 233 | +} |
| 234 | + |
| 235 | +/** |
| 236 | + * Log for database errors |
| 237 | + * @param $text String: database error message. |
| 238 | + */ |
| 239 | +function wfLogDBError( $text ) { |
| 240 | + global $wgDBerrorLog, $wgDBname; |
| 241 | + if ( $wgDBerrorLog ) { |
| 242 | + $host = trim(`hostname`); |
| 243 | + $text = date('D M j G:i:s T Y') . "\t$host\t$wgDBname\t$text"; |
| 244 | + wfErrorLog( $text, $wgDBerrorLog ); |
| 245 | + } |
| 246 | +} |
| 247 | + |
| 248 | +/** |
| 249 | + * Log to a file without getting "file size exceeded" signals |
| 250 | + */ |
| 251 | +function wfErrorLog( $text, $file ) { |
| 252 | + wfSuppressWarnings(); |
| 253 | + $exists = file_exists( $file ); |
| 254 | + $size = $exists ? filesize( $file ) : false; |
| 255 | + if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) { |
| 256 | + error_log( $text, 3, $file ); |
| 257 | + } |
| 258 | + wfRestoreWarnings(); |
| 259 | +} |
| 260 | + |
| 261 | +/** |
| 262 | + * @todo document |
| 263 | + */ |
| 264 | +function wfLogProfilingData() { |
| 265 | + global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest; |
| 266 | + global $wgProfiler, $wgUser; |
| 267 | + if ( !isset( $wgProfiler ) ) |
| 268 | + return; |
| 269 | + |
| 270 | + $now = wfTime(); |
| 271 | + $elapsed = $now - $wgRequestTime; |
| 272 | + $prof = wfGetProfilingOutput( $wgRequestTime, $elapsed ); |
| 273 | + $forward = ''; |
| 274 | + if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) |
| 275 | + $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR']; |
| 276 | + if( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) |
| 277 | + $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP']; |
| 278 | + if( !empty( $_SERVER['HTTP_FROM'] ) ) |
| 279 | + $forward .= ' from ' . $_SERVER['HTTP_FROM']; |
| 280 | + if( $forward ) |
| 281 | + $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})"; |
| 282 | + // Don't unstub $wgUser at this late stage just for statistics purposes |
| 283 | + if( StubObject::isRealObject($wgUser) && $wgUser->isAnon() ) |
| 284 | + $forward .= ' anon'; |
| 285 | + $log = sprintf( "%s\t%04.3f\t%s\n", |
| 286 | + gmdate( 'YmdHis' ), $elapsed, |
| 287 | + urldecode( $wgRequest->getRequestURL() . $forward ) ); |
| 288 | + if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) { |
| 289 | + wfErrorLog( $log . $prof, $wgDebugLogFile ); |
| 290 | + } |
| 291 | +} |
| 292 | + |
| 293 | +/** |
| 294 | + * Check if the wiki read-only lock file is present. This can be used to lock |
| 295 | + * off editing functions, but doesn't guarantee that the database will not be |
| 296 | + * modified. |
| 297 | + * @return bool |
| 298 | + */ |
| 299 | +function wfReadOnly() { |
| 300 | + global $wgReadOnlyFile, $wgReadOnly; |
| 301 | + |
| 302 | + if ( !is_null( $wgReadOnly ) ) { |
| 303 | + return (bool)$wgReadOnly; |
| 304 | + } |
| 305 | + if ( '' == $wgReadOnlyFile ) { |
| 306 | + return false; |
| 307 | + } |
| 308 | + // Set $wgReadOnly for faster access next time |
| 309 | + if ( is_file( $wgReadOnlyFile ) ) { |
| 310 | + $wgReadOnly = file_get_contents( $wgReadOnlyFile ); |
| 311 | + } else { |
| 312 | + $wgReadOnly = false; |
| 313 | + } |
| 314 | + return (bool)$wgReadOnly; |
| 315 | +} |
| 316 | + |
| 317 | +function wfReadOnlyReason() { |
| 318 | + global $wgReadOnly; |
| 319 | + wfReadOnly(); |
| 320 | + return $wgReadOnly; |
| 321 | +} |
| 322 | + |
| 323 | +/** |
| 324 | + * Get a message from anywhere, for the current user language. |
| 325 | + * |
| 326 | + * Use wfMsgForContent() instead if the message should NOT |
| 327 | + * change depending on the user preferences. |
| 328 | + * |
| 329 | + * @param $key String: lookup key for the message, usually |
| 330 | + * defined in languages/Language.php |
| 331 | + * |
| 332 | + * This function also takes extra optional parameters (not |
| 333 | + * shown in the function definition), which can by used to |
| 334 | + * insert variable text into the predefined message. |
| 335 | + */ |
| 336 | +function wfMsg( $key ) { |
| 337 | + $args = func_get_args(); |
| 338 | + array_shift( $args ); |
| 339 | + return wfMsgReal( $key, $args, true ); |
| 340 | +} |
| 341 | + |
| 342 | +/** |
| 343 | + * Same as above except doesn't transform the message |
| 344 | + */ |
| 345 | +function wfMsgNoTrans( $key ) { |
| 346 | + $args = func_get_args(); |
| 347 | + array_shift( $args ); |
| 348 | + return wfMsgReal( $key, $args, true, false, false ); |
| 349 | +} |
| 350 | + |
| 351 | +/** |
| 352 | + * Get a message from anywhere, for the current global language |
| 353 | + * set with $wgLanguageCode. |
| 354 | + * |
| 355 | + * Use this if the message should NOT change dependent on the |
| 356 | + * language set in the user's preferences. This is the case for |
| 357 | + * most text written into logs, as well as link targets (such as |
| 358 | + * the name of the copyright policy page). Link titles, on the |
| 359 | + * other hand, should be shown in the UI language. |
| 360 | + * |
| 361 | + * Note that MediaWiki allows users to change the user interface |
| 362 | + * language in their preferences, but a single installation |
| 363 | + * typically only contains content in one language. |
| 364 | + * |
| 365 | + * Be wary of this distinction: If you use wfMsg() where you should |
| 366 | + * use wfMsgForContent(), a user of the software may have to |
| 367 | + * customize over 70 messages in order to, e.g., fix a link in every |
| 368 | + * possible language. |
| 369 | + * |
| 370 | + * @param $key String: lookup key for the message, usually |
| 371 | + * defined in languages/Language.php |
| 372 | + */ |
| 373 | +function wfMsgForContent( $key ) { |
| 374 | + global $wgForceUIMsgAsContentMsg; |
| 375 | + $args = func_get_args(); |
| 376 | + array_shift( $args ); |
| 377 | + $forcontent = true; |
| 378 | + if( is_array( $wgForceUIMsgAsContentMsg ) && |
| 379 | + in_array( $key, $wgForceUIMsgAsContentMsg ) ) |
| 380 | + $forcontent = false; |
| 381 | + return wfMsgReal( $key, $args, true, $forcontent ); |
| 382 | +} |
| 383 | + |
| 384 | +/** |
| 385 | + * Same as above except doesn't transform the message |
| 386 | + */ |
| 387 | +function wfMsgForContentNoTrans( $key ) { |
| 388 | + global $wgForceUIMsgAsContentMsg; |
| 389 | + $args = func_get_args(); |
| 390 | + array_shift( $args ); |
| 391 | + $forcontent = true; |
| 392 | + if( is_array( $wgForceUIMsgAsContentMsg ) && |
| 393 | + in_array( $key, $wgForceUIMsgAsContentMsg ) ) |
| 394 | + $forcontent = false; |
| 395 | + return wfMsgReal( $key, $args, true, $forcontent, false ); |
| 396 | +} |
| 397 | + |
| 398 | +/** |
| 399 | + * Get a message from the language file, for the UI elements |
| 400 | + */ |
| 401 | +function wfMsgNoDB( $key ) { |
| 402 | + $args = func_get_args(); |
| 403 | + array_shift( $args ); |
| 404 | + return wfMsgReal( $key, $args, false ); |
| 405 | +} |
| 406 | + |
| 407 | +/** |
| 408 | + * Get a message from the language file, for the content |
| 409 | + */ |
| 410 | +function wfMsgNoDBForContent( $key ) { |
| 411 | + global $wgForceUIMsgAsContentMsg; |
| 412 | + $args = func_get_args(); |
| 413 | + array_shift( $args ); |
| 414 | + $forcontent = true; |
| 415 | + if( is_array( $wgForceUIMsgAsContentMsg ) && |
| 416 | + in_array( $key, $wgForceUIMsgAsContentMsg ) ) |
| 417 | + $forcontent = false; |
| 418 | + return wfMsgReal( $key, $args, false, $forcontent ); |
| 419 | +} |
| 420 | + |
| 421 | + |
| 422 | +/** |
| 423 | + * Really get a message |
| 424 | + * @param $key String: key to get. |
| 425 | + * @param $args |
| 426 | + * @param $useDB Boolean |
| 427 | + * @param $transform Boolean: Whether or not to transform the message. |
| 428 | + * @param $forContent Boolean |
| 429 | + * @return String: the requested message. |
| 430 | + */ |
| 431 | +function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform = true ) { |
| 432 | + wfProfileIn( __METHOD__ ); |
| 433 | + $message = wfMsgGetKey( $key, $useDB, $forContent, $transform ); |
| 434 | + $message = wfMsgReplaceArgs( $message, $args ); |
| 435 | + wfProfileOut( __METHOD__ ); |
| 436 | + return $message; |
| 437 | +} |
| 438 | + |
| 439 | +/** |
| 440 | + * This function provides the message source for messages to be edited which are *not* stored in the database. |
| 441 | + * @param $key String: |
| 442 | + */ |
| 443 | +function wfMsgWeirdKey ( $key ) { |
| 444 | + $source = wfMsgGetKey( $key, false, true, false ); |
| 445 | + if ( wfEmptyMsg( $key, $source ) ) |
| 446 | + return ""; |
| 447 | + else |
| 448 | + return $source; |
| 449 | +} |
| 450 | + |
| 451 | +/** |
| 452 | + * Fetch a message string value, but don't replace any keys yet. |
| 453 | + * @param string $key |
| 454 | + * @param bool $useDB |
| 455 | + * @param string $langcode Code of the language to get the message for, or |
| 456 | + * behaves as a content language switch if it is a |
| 457 | + * boolean. |
| 458 | + * @return string |
| 459 | + * @private |
| 460 | + */ |
| 461 | +function wfMsgGetKey( $key, $useDB, $langCode = false, $transform = true ) { |
| 462 | + global $wgParser, $wgContLang, $wgMessageCache, $wgLang; |
| 463 | + |
| 464 | + wfRunHooks('NormalizeMessageKey', array(&$key, &$useDB, &$langCode, &$transform)); |
| 465 | + |
| 466 | + # If $wgMessageCache isn't initialised yet, try to return something sensible. |
| 467 | + if( is_object( $wgMessageCache ) ) { |
| 468 | + $message = $wgMessageCache->get( $key, $useDB, $langCode ); |
| 469 | + if ( $transform ) { |
| 470 | + $message = $wgMessageCache->transform( $message ); |
| 471 | + } |
| 472 | + } else { |
| 473 | + if( $langCode === true ) { |
| 474 | + $lang = &$wgContLang; |
| 475 | + } elseif( $langCode === false ) { |
| 476 | + $lang = &$wgLang; |
| 477 | + } else { |
| 478 | + $validCodes = array_keys( Language::getLanguageNames() ); |
| 479 | + if( in_array( $langCode, $validCodes ) ) { |
| 480 | + # $langcode corresponds to a valid language. |
| 481 | + $lang = Language::factory( $langCode ); |
| 482 | + } else { |
| 483 | + # $langcode is a string, but not a valid language code; use content language. |
| 484 | + $lang =& $wgContLang; |
| 485 | + wfDebug( 'Invalid language code passed to wfMsgGetKey, falling back to content language.' ); |
| 486 | + } |
| 487 | + } |
| 488 | + |
| 489 | + # MessageCache::get() does this already, Language::getMessage() doesn't |
| 490 | + # ISSUE: Should we try to handle "message/lang" here too? |
| 491 | + $key = str_replace( ' ' , '_' , $wgContLang->lcfirst( $key ) ); |
| 492 | + |
| 493 | + if( is_object( $lang ) ) { |
| 494 | + $message = $lang->getMessage( $key ); |
| 495 | + } else { |
| 496 | + $message = false; |
| 497 | + } |
| 498 | + } |
| 499 | + |
| 500 | + return $message; |
| 501 | +} |
| 502 | + |
| 503 | +/** |
| 504 | + * Replace message parameter keys on the given formatted output. |
| 505 | + * |
| 506 | + * @param string $message |
| 507 | + * @param array $args |
| 508 | + * @return string |
| 509 | + * @private |
| 510 | + */ |
| 511 | +function wfMsgReplaceArgs( $message, $args ) { |
| 512 | + # Fix windows line-endings |
| 513 | + # Some messages are split with explode("\n", $msg) |
| 514 | + $message = str_replace( "\r", '', $message ); |
| 515 | + |
| 516 | + // Replace arguments |
| 517 | + if ( count( $args ) ) { |
| 518 | + if ( is_array( $args[0] ) ) { |
| 519 | + $args = array_values( $args[0] ); |
| 520 | + } |
| 521 | + $replacementKeys = array(); |
| 522 | + foreach( $args as $n => $param ) { |
| 523 | + $replacementKeys['$' . ($n + 1)] = $param; |
| 524 | + } |
| 525 | + $message = strtr( $message, $replacementKeys ); |
| 526 | + } |
| 527 | + |
| 528 | + return $message; |
| 529 | +} |
| 530 | + |
| 531 | +/** |
| 532 | + * Return an HTML-escaped version of a message. |
| 533 | + * Parameter replacements, if any, are done *after* the HTML-escaping, |
| 534 | + * so parameters may contain HTML (eg links or form controls). Be sure |
| 535 | + * to pre-escape them if you really do want plaintext, or just wrap |
| 536 | + * the whole thing in htmlspecialchars(). |
| 537 | + * |
| 538 | + * @param string $key |
| 539 | + * @param string ... parameters |
| 540 | + * @return string |
| 541 | + */ |
| 542 | +function wfMsgHtml( $key ) { |
| 543 | + $args = func_get_args(); |
| 544 | + array_shift( $args ); |
| 545 | + return wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $key, true ) ), $args ); |
| 546 | +} |
| 547 | + |
| 548 | +/** |
| 549 | + * Return an HTML version of message |
| 550 | + * Parameter replacements, if any, are done *after* parsing the wiki-text message, |
| 551 | + * so parameters may contain HTML (eg links or form controls). Be sure |
| 552 | + * to pre-escape them if you really do want plaintext, or just wrap |
| 553 | + * the whole thing in htmlspecialchars(). |
| 554 | + * |
| 555 | + * @param string $key |
| 556 | + * @param string ... parameters |
| 557 | + * @return string |
| 558 | + */ |
| 559 | +function wfMsgWikiHtml( $key ) { |
| 560 | + global $wgOut; |
| 561 | + $args = func_get_args(); |
| 562 | + array_shift( $args ); |
| 563 | + return wfMsgReplaceArgs( $wgOut->parse( wfMsgGetKey( $key, true ), /* can't be set to false */ true ), $args ); |
| 564 | +} |
| 565 | + |
| 566 | +/** |
| 567 | + * Returns message in the requested format |
| 568 | + * @param string $key Key of the message |
| 569 | + * @param array $options Processing rules: |
| 570 | + * <i>parse</i>: parses wikitext to html |
| 571 | + * <i>parseinline</i>: parses wikitext to html and removes the surrounding p's added by parser or tidy |
| 572 | + * <i>escape</i>: filters message through htmlspecialchars |
| 573 | + * <i>escapenoentities</i>: same, but allows entity references like through |
| 574 | + * <i>replaceafter</i>: parameters are substituted after parsing or escaping |
| 575 | + * <i>parsemag</i>: transform the message using magic phrases |
| 576 | + * <i>content</i>: fetch message for content language instead of interface |
| 577 | + * <i>language</i>: language code to fetch message for (overriden by <i>content</i>), its behaviour |
| 578 | + * with parser, parseinline and parsemag is undefined. |
| 579 | + * Behavior for conflicting options (e.g., parse+parseinline) is undefined. |
| 580 | + */ |
| 581 | +function wfMsgExt( $key, $options ) { |
| 582 | + global $wgOut, $wgParser; |
| 583 | + |
| 584 | + $args = func_get_args(); |
| 585 | + array_shift( $args ); |
| 586 | + array_shift( $args ); |
| 587 | + |
| 588 | + if( !is_array($options) ) { |
| 589 | + $options = array($options); |
| 590 | + } |
| 591 | + |
| 592 | + if( in_array('content', $options) ) { |
| 593 | + $forContent = true; |
| 594 | + $langCode = true; |
| 595 | + } elseif( array_key_exists('language', $options) ) { |
| 596 | + $forContent = false; |
| 597 | + $langCode = $options['language']; |
| 598 | + $validCodes = array_keys( Language::getLanguageNames() ); |
| 599 | + if( !in_array($options['language'], $validCodes) ) { |
| 600 | + # Fallback to en, instead of whatever interface language we might have |
| 601 | + $langCode = 'en'; |
| 602 | + } |
| 603 | + } else { |
| 604 | + $forContent = false; |
| 605 | + $langCode = false; |
| 606 | + } |
| 607 | + |
| 608 | + $string = wfMsgGetKey( $key, /*DB*/true, $langCode, /*Transform*/false ); |
| 609 | + |
| 610 | + if( !in_array('replaceafter', $options) ) { |
| 611 | + $string = wfMsgReplaceArgs( $string, $args ); |
| 612 | + } |
| 613 | + |
| 614 | + if( in_array('parse', $options) ) { |
| 615 | + $string = $wgOut->parse( $string, true, !$forContent ); |
| 616 | + } elseif ( in_array('parseinline', $options) ) { |
| 617 | + $string = $wgOut->parse( $string, true, !$forContent ); |
| 618 | + $m = array(); |
| 619 | + if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) { |
| 620 | + $string = $m[1]; |
| 621 | + } |
| 622 | + } elseif ( in_array('parsemag', $options) ) { |
| 623 | + global $wgMessageCache; |
| 624 | + if ( isset( $wgMessageCache ) ) { |
| 625 | + $string = $wgMessageCache->transform( $string, !$forContent ); |
| 626 | + } |
| 627 | + } |
| 628 | + |
| 629 | + if ( in_array('escape', $options) ) { |
| 630 | + $string = htmlspecialchars ( $string ); |
| 631 | + } elseif ( in_array( 'escapenoentities', $options ) ) { |
| 632 | + $string = htmlspecialchars( $string ); |
| 633 | + $string = str_replace( '&', '&', $string ); |
| 634 | + $string = Sanitizer::normalizeCharReferences( $string ); |
| 635 | + } |
| 636 | + |
| 637 | + if( in_array('replaceafter', $options) ) { |
| 638 | + $string = wfMsgReplaceArgs( $string, $args ); |
| 639 | + } |
| 640 | + |
| 641 | + return $string; |
| 642 | +} |
| 643 | + |
| 644 | + |
| 645 | +/** |
| 646 | + * Just like exit() but makes a note of it. |
| 647 | + * Commits open transactions except if the error parameter is set |
| 648 | + * |
| 649 | + * @deprecated Please return control to the caller or throw an exception |
| 650 | + */ |
| 651 | +function wfAbruptExit( $error = false ){ |
| 652 | + static $called = false; |
| 653 | + if ( $called ){ |
| 654 | + exit( -1 ); |
| 655 | + } |
| 656 | + $called = true; |
| 657 | + |
| 658 | + $bt = wfDebugBacktrace(); |
| 659 | + if( $bt ) { |
| 660 | + for($i = 0; $i < count($bt) ; $i++){ |
| 661 | + $file = isset($bt[$i]['file']) ? $bt[$i]['file'] : "unknown"; |
| 662 | + $line = isset($bt[$i]['line']) ? $bt[$i]['line'] : "unknown"; |
| 663 | + wfDebug("WARNING: Abrupt exit in $file at line $line\n"); |
| 664 | + } |
| 665 | + } else { |
| 666 | + wfDebug('WARNING: Abrupt exit\n'); |
| 667 | + } |
| 668 | + |
| 669 | + wfLogProfilingData(); |
| 670 | + |
| 671 | + if ( !$error ) { |
| 672 | + wfGetLB()->closeAll(); |
| 673 | + } |
| 674 | + exit( -1 ); |
| 675 | +} |
| 676 | + |
| 677 | +/** |
| 678 | + * @deprecated Please return control the caller or throw an exception |
| 679 | + */ |
| 680 | +function wfErrorExit() { |
| 681 | + wfAbruptExit( true ); |
| 682 | +} |
| 683 | + |
| 684 | +/** |
| 685 | + * Print a simple message and die, returning nonzero to the shell if any. |
| 686 | + * Plain die() fails to return nonzero to the shell if you pass a string. |
| 687 | + * @param string $msg |
| 688 | + */ |
| 689 | +function wfDie( $msg='' ) { |
| 690 | + echo $msg; |
| 691 | + die( 1 ); |
| 692 | +} |
| 693 | + |
| 694 | +/** |
| 695 | + * Throw a debugging exception. This function previously once exited the process, |
| 696 | + * but now throws an exception instead, with similar results. |
| 697 | + * |
| 698 | + * @param string $msg Message shown when dieing. |
| 699 | + */ |
| 700 | +function wfDebugDieBacktrace( $msg = '' ) { |
| 701 | + throw new MWException( $msg ); |
| 702 | +} |
| 703 | + |
| 704 | +/** |
| 705 | + * Fetch server name for use in error reporting etc. |
| 706 | + * Use real server name if available, so we know which machine |
| 707 | + * in a server farm generated the current page. |
| 708 | + * @return string |
| 709 | + */ |
| 710 | +function wfHostname() { |
| 711 | + if ( function_exists( 'posix_uname' ) ) { |
| 712 | + // This function not present on Windows |
| 713 | + $uname = @posix_uname(); |
| 714 | + } else { |
| 715 | + $uname = false; |
| 716 | + } |
| 717 | + if( is_array( $uname ) && isset( $uname['nodename'] ) ) { |
| 718 | + return $uname['nodename']; |
| 719 | + } else { |
| 720 | + # This may be a virtual server. |
| 721 | + return $_SERVER['SERVER_NAME']; |
| 722 | + } |
| 723 | +} |
| 724 | + |
| 725 | + /** |
| 726 | + * Returns a HTML comment with the elapsed time since request. |
| 727 | + * This method has no side effects. |
| 728 | + * @return string |
| 729 | + */ |
| 730 | + function wfReportTime() { |
| 731 | + global $wgRequestTime, $wgShowHostnames; |
| 732 | + |
| 733 | + $now = wfTime(); |
| 734 | + $elapsed = $now - $wgRequestTime; |
| 735 | + |
| 736 | + return $wgShowHostnames |
| 737 | + ? sprintf( "<!-- Served by %s in %01.3f secs. -->", wfHostname(), $elapsed ) |
| 738 | + : sprintf( "<!-- Served in %01.3f secs. -->", $elapsed ); |
| 739 | + } |
| 740 | + |
| 741 | +/** |
| 742 | + * Safety wrapper for debug_backtrace(). |
| 743 | + * |
| 744 | + * With Zend Optimizer 3.2.0 loaded, this causes segfaults under somewhat |
| 745 | + * murky circumstances, which may be triggered in part by stub objects |
| 746 | + * or other fancy talkin'. |
| 747 | + * |
| 748 | + * Will return an empty array if Zend Optimizer is detected, otherwise |
| 749 | + * the output from debug_backtrace() (trimmed). |
| 750 | + * |
| 751 | + * @return array of backtrace information |
| 752 | + */ |
| 753 | +function wfDebugBacktrace() { |
| 754 | + if( extension_loaded( 'Zend Optimizer' ) ) { |
| 755 | + wfDebug( "Zend Optimizer detected; skipping debug_backtrace for safety.\n" ); |
| 756 | + return array(); |
| 757 | + } else { |
| 758 | + return array_slice( debug_backtrace(), 1 ); |
| 759 | + } |
| 760 | +} |
| 761 | + |
| 762 | +function wfBacktrace() { |
| 763 | + global $wgCommandLineMode; |
| 764 | + |
| 765 | + if ( $wgCommandLineMode ) { |
| 766 | + $msg = ''; |
| 767 | + } else { |
| 768 | + $msg = "<ul>\n"; |
| 769 | + } |
| 770 | + $backtrace = wfDebugBacktrace(); |
| 771 | + foreach( $backtrace as $call ) { |
| 772 | + if( isset( $call['file'] ) ) { |
| 773 | + $f = explode( DIRECTORY_SEPARATOR, $call['file'] ); |
| 774 | + $file = $f[count($f)-1]; |
| 775 | + } else { |
| 776 | + $file = '-'; |
| 777 | + } |
| 778 | + if( isset( $call['line'] ) ) { |
| 779 | + $line = $call['line']; |
| 780 | + } else { |
| 781 | + $line = '-'; |
| 782 | + } |
| 783 | + if ( $wgCommandLineMode ) { |
| 784 | + $msg .= "$file line $line calls "; |
| 785 | + } else { |
| 786 | + $msg .= '<li>' . $file . ' line ' . $line . ' calls '; |
| 787 | + } |
| 788 | + if( !empty( $call['class'] ) ) $msg .= $call['class'] . '::'; |
| 789 | + $msg .= $call['function'] . '()'; |
| 790 | + |
| 791 | + if ( $wgCommandLineMode ) { |
| 792 | + $msg .= "\n"; |
| 793 | + } else { |
| 794 | + $msg .= "</li>\n"; |
| 795 | + } |
| 796 | + } |
| 797 | + if ( $wgCommandLineMode ) { |
| 798 | + $msg .= "\n"; |
| 799 | + } else { |
| 800 | + $msg .= "</ul>\n"; |
| 801 | + } |
| 802 | + |
| 803 | + return $msg; |
| 804 | +} |
| 805 | + |
| 806 | + |
| 807 | +/* Some generic result counters, pulled out of SearchEngine */ |
| 808 | + |
| 809 | + |
| 810 | +/** |
| 811 | + * @todo document |
| 812 | + */ |
| 813 | +function wfShowingResults( $offset, $limit ) { |
| 814 | + global $wgLang; |
| 815 | + return wfMsgExt( 'showingresults', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) ); |
| 816 | +} |
| 817 | + |
| 818 | +/** |
| 819 | + * @todo document |
| 820 | + */ |
| 821 | +function wfShowingResultsNum( $offset, $limit, $num ) { |
| 822 | + global $wgLang; |
| 823 | + return wfMsgExt( 'showingresultsnum', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ), $wgLang->formatNum( $num ) ); |
| 824 | +} |
| 825 | + |
| 826 | +/** |
| 827 | + * @todo document |
| 828 | + */ |
| 829 | +function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { |
| 830 | + global $wgLang; |
| 831 | + $fmtLimit = $wgLang->formatNum( $limit ); |
| 832 | + $prev = wfMsg( 'prevn', $fmtLimit ); |
| 833 | + $next = wfMsg( 'nextn', $fmtLimit ); |
| 834 | + |
| 835 | + if( is_object( $link ) ) { |
| 836 | + $title =& $link; |
| 837 | + } else { |
| 838 | + $title = Title::newFromText( $link ); |
| 839 | + if( is_null( $title ) ) { |
| 840 | + return false; |
| 841 | + } |
| 842 | + } |
| 843 | + |
| 844 | + if ( 0 != $offset ) { |
| 845 | + $po = $offset - $limit; |
| 846 | + if ( $po < 0 ) { $po = 0; } |
| 847 | + $q = "limit={$limit}&offset={$po}"; |
| 848 | + if ( '' != $query ) { $q .= '&'.$query; } |
| 849 | + $plink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-prevlink\">{$prev}</a>"; |
| 850 | + } else { $plink = $prev; } |
| 851 | + |
| 852 | + $no = $offset + $limit; |
| 853 | + $q = 'limit='.$limit.'&offset='.$no; |
| 854 | + if ( '' != $query ) { $q .= '&'.$query; } |
| 855 | + |
| 856 | + if ( $atend ) { |
| 857 | + $nlink = $next; |
| 858 | + } else { |
| 859 | + $nlink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-nextlink\">{$next}</a>"; |
| 860 | + } |
| 861 | + $nums = wfNumLink( $offset, 20, $title, $query ) . ' | ' . |
| 862 | + wfNumLink( $offset, 50, $title, $query ) . ' | ' . |
| 863 | + wfNumLink( $offset, 100, $title, $query ) . ' | ' . |
| 864 | + wfNumLink( $offset, 250, $title, $query ) . ' | ' . |
| 865 | + wfNumLink( $offset, 500, $title, $query ); |
| 866 | + |
| 867 | + return wfMsg( 'viewprevnext', $plink, $nlink, $nums ); |
| 868 | +} |
| 869 | + |
| 870 | +/** |
| 871 | + * @todo document |
| 872 | + */ |
| 873 | +function wfNumLink( $offset, $limit, &$title, $query = '' ) { |
| 874 | + global $wgLang; |
| 875 | + if ( '' == $query ) { $q = ''; } |
| 876 | + else { $q = $query.'&'; } |
| 877 | + $q .= 'limit='.$limit.'&offset='.$offset; |
| 878 | + |
| 879 | + $fmtLimit = $wgLang->formatNum( $limit ); |
| 880 | + $s = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-numlink\">{$fmtLimit}</a>"; |
| 881 | + return $s; |
| 882 | +} |
| 883 | + |
| 884 | +/** |
| 885 | + * @todo document |
| 886 | + * @todo FIXME: we may want to blacklist some broken browsers |
| 887 | + * |
| 888 | + * @return bool Whereas client accept gzip compression |
| 889 | + */ |
| 890 | +function wfClientAcceptsGzip() { |
| 891 | + global $wgUseGzip; |
| 892 | + if( $wgUseGzip ) { |
| 893 | + # FIXME: we may want to blacklist some broken browsers |
| 894 | + $m = array(); |
| 895 | + if( preg_match( |
| 896 | + '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/', |
| 897 | + $_SERVER['HTTP_ACCEPT_ENCODING'], |
| 898 | + $m ) ) { |
| 899 | + if( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) return false; |
| 900 | + wfDebug( " accepts gzip\n" ); |
| 901 | + return true; |
| 902 | + } |
| 903 | + } |
| 904 | + return false; |
| 905 | +} |
| 906 | + |
| 907 | +/** |
| 908 | + * Obtain the offset and limit values from the request string; |
| 909 | + * used in special pages |
| 910 | + * |
| 911 | + * @param $deflimit Default limit if none supplied |
| 912 | + * @param $optionname Name of a user preference to check against |
| 913 | + * @return array |
| 914 | + * |
| 915 | + */ |
| 916 | +function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) { |
| 917 | + global $wgRequest; |
| 918 | + return $wgRequest->getLimitOffset( $deflimit, $optionname ); |
| 919 | +} |
| 920 | + |
| 921 | +/** |
| 922 | + * Escapes the given text so that it may be output using addWikiText() |
| 923 | + * without any linking, formatting, etc. making its way through. This |
| 924 | + * is achieved by substituting certain characters with HTML entities. |
| 925 | + * As required by the callers, <nowiki> is not used. It currently does |
| 926 | + * not filter out characters which have special meaning only at the |
| 927 | + * start of a line, such as "*". |
| 928 | + * |
| 929 | + * @param string $text Text to be escaped |
| 930 | + */ |
| 931 | +function wfEscapeWikiText( $text ) { |
| 932 | + $text = str_replace( |
| 933 | + array( '[', '|', ']', '\'', 'ISBN ', 'RFC ', '://', "\n=", '{{' ), |
| 934 | + array( '[', '|', ']', ''', 'ISBN ', 'RFC ', '://', "\n=", '{{' ), |
| 935 | + htmlspecialchars($text) ); |
| 936 | + return $text; |
| 937 | +} |
| 938 | + |
| 939 | +/** |
| 940 | + * @todo document |
| 941 | + */ |
| 942 | +function wfQuotedPrintable( $string, $charset = '' ) { |
| 943 | + # Probably incomplete; see RFC 2045 |
| 944 | + if( empty( $charset ) ) { |
| 945 | + global $wgInputEncoding; |
| 946 | + $charset = $wgInputEncoding; |
| 947 | + } |
| 948 | + $charset = strtoupper( $charset ); |
| 949 | + $charset = str_replace( 'ISO-8859', 'ISO8859', $charset ); // ? |
| 950 | + |
| 951 | + $illegal = '\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff='; |
| 952 | + $replace = $illegal . '\t ?_'; |
| 953 | + if( !preg_match( "/[$illegal]/", $string ) ) return $string; |
| 954 | + $out = "=?$charset?Q?"; |
| 955 | + $out .= preg_replace( "/([$replace])/e", 'sprintf("=%02X",ord("$1"))', $string ); |
| 956 | + $out .= '?='; |
| 957 | + return $out; |
| 958 | +} |
| 959 | + |
| 960 | + |
| 961 | +/** |
| 962 | + * @todo document |
| 963 | + * @return float |
| 964 | + */ |
| 965 | +function wfTime() { |
| 966 | + return microtime(true); |
| 967 | +} |
| 968 | + |
| 969 | +/** |
| 970 | + * Sets dest to source and returns the original value of dest |
| 971 | + * If source is NULL, it just returns the value, it doesn't set the variable |
| 972 | + */ |
| 973 | +function wfSetVar( &$dest, $source ) { |
| 974 | + $temp = $dest; |
| 975 | + if ( !is_null( $source ) ) { |
| 976 | + $dest = $source; |
| 977 | + } |
| 978 | + return $temp; |
| 979 | +} |
| 980 | + |
| 981 | +/** |
| 982 | + * As for wfSetVar except setting a bit |
| 983 | + */ |
| 984 | +function wfSetBit( &$dest, $bit, $state = true ) { |
| 985 | + $temp = (bool)($dest & $bit ); |
| 986 | + if ( !is_null( $state ) ) { |
| 987 | + if ( $state ) { |
| 988 | + $dest |= $bit; |
| 989 | + } else { |
| 990 | + $dest &= ~$bit; |
| 991 | + } |
| 992 | + } |
| 993 | + return $temp; |
| 994 | +} |
| 995 | + |
| 996 | +/** |
| 997 | + * This function takes two arrays as input, and returns a CGI-style string, e.g. |
| 998 | + * "days=7&limit=100". Options in the first array override options in the second. |
| 999 | + * Options set to "" will not be output. |
| 1000 | + */ |
| 1001 | +function wfArrayToCGI( $array1, $array2 = NULL ) |
| 1002 | +{ |
| 1003 | + if ( !is_null( $array2 ) ) { |
| 1004 | + $array1 = $array1 + $array2; |
| 1005 | + } |
| 1006 | + |
| 1007 | + $cgi = ''; |
| 1008 | + foreach ( $array1 as $key => $value ) { |
| 1009 | + if ( '' !== $value ) { |
| 1010 | + if ( '' != $cgi ) { |
| 1011 | + $cgi .= '&'; |
| 1012 | + } |
| 1013 | + if(is_array($value)) |
| 1014 | + { |
| 1015 | + $firstTime = true; |
| 1016 | + foreach($value as $v) |
| 1017 | + { |
| 1018 | + $cgi .= ($firstTime ? '' : '&') . |
| 1019 | + urlencode( $key . '[]' ) . '=' . |
| 1020 | + urlencode( $v ); |
| 1021 | + $firstTime = false; |
| 1022 | + } |
| 1023 | + } |
| 1024 | + else |
| 1025 | + $cgi .= urlencode( $key ) . '=' . |
| 1026 | + urlencode( $value ); |
| 1027 | + } |
| 1028 | + } |
| 1029 | + return $cgi; |
| 1030 | +} |
| 1031 | + |
| 1032 | +/** |
| 1033 | + * Append a query string to an existing URL, which may or may not already |
| 1034 | + * have query string parameters already. If so, they will be combined. |
| 1035 | + * |
| 1036 | + * @param string $url |
| 1037 | + * @param string $query |
| 1038 | + * @return string |
| 1039 | + */ |
| 1040 | +function wfAppendQuery( $url, $query ) { |
| 1041 | + if( $query != '' ) { |
| 1042 | + if( false === strpos( $url, '?' ) ) { |
| 1043 | + $url .= '?'; |
| 1044 | + } else { |
| 1045 | + $url .= '&'; |
| 1046 | + } |
| 1047 | + $url .= $query; |
| 1048 | + } |
| 1049 | + return $url; |
| 1050 | +} |
| 1051 | + |
| 1052 | +/** |
| 1053 | + * Expand a potentially local URL to a fully-qualified URL. |
| 1054 | + * Assumes $wgServer is correct. :) |
| 1055 | + * @param string $url, either fully-qualified or a local path + query |
| 1056 | + * @return string Fully-qualified URL |
| 1057 | + */ |
| 1058 | +function wfExpandUrl( $url ) { |
| 1059 | + if( substr( $url, 0, 1 ) == '/' ) { |
| 1060 | + global $wgServer; |
| 1061 | + return $wgServer . $url; |
| 1062 | + } else { |
| 1063 | + return $url; |
| 1064 | + } |
| 1065 | +} |
| 1066 | + |
| 1067 | +/** |
| 1068 | + * This is obsolete, use SquidUpdate::purge() |
| 1069 | + * @deprecated |
| 1070 | + */ |
| 1071 | +function wfPurgeSquidServers ($urlArr) { |
| 1072 | + SquidUpdate::purge( $urlArr ); |
| 1073 | +} |
| 1074 | + |
| 1075 | +/** |
| 1076 | + * Windows-compatible version of escapeshellarg() |
| 1077 | + * Windows doesn't recognise single-quotes in the shell, but the escapeshellarg() |
| 1078 | + * function puts single quotes in regardless of OS |
| 1079 | + */ |
| 1080 | +function wfEscapeShellArg( ) { |
| 1081 | + $args = func_get_args(); |
| 1082 | + $first = true; |
| 1083 | + $retVal = ''; |
| 1084 | + foreach ( $args as $arg ) { |
| 1085 | + if ( !$first ) { |
| 1086 | + $retVal .= ' '; |
| 1087 | + } else { |
| 1088 | + $first = false; |
| 1089 | + } |
| 1090 | + |
| 1091 | + if ( wfIsWindows() ) { |
| 1092 | + // Escaping for an MSVC-style command line parser |
| 1093 | + // Ref: http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html |
| 1094 | + // Double the backslashes before any double quotes. Escape the double quotes. |
| 1095 | + $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE ); |
| 1096 | + $arg = ''; |
| 1097 | + $delim = false; |
| 1098 | + foreach ( $tokens as $token ) { |
| 1099 | + if ( $delim ) { |
| 1100 | + $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"'; |
| 1101 | + } else { |
| 1102 | + $arg .= $token; |
| 1103 | + } |
| 1104 | + $delim = !$delim; |
| 1105 | + } |
| 1106 | + // Double the backslashes before the end of the string, because |
| 1107 | + // we will soon add a quote |
| 1108 | + $m = array(); |
| 1109 | + if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) { |
| 1110 | + $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] ); |
| 1111 | + } |
| 1112 | + |
| 1113 | + // Add surrounding quotes |
| 1114 | + $retVal .= '"' . $arg . '"'; |
| 1115 | + } else { |
| 1116 | + $retVal .= escapeshellarg( $arg ); |
| 1117 | + } |
| 1118 | + } |
| 1119 | + return $retVal; |
| 1120 | +} |
| 1121 | + |
| 1122 | +/** |
| 1123 | + * wfMerge attempts to merge differences between three texts. |
| 1124 | + * Returns true for a clean merge and false for failure or a conflict. |
| 1125 | + */ |
| 1126 | +function wfMerge( $old, $mine, $yours, &$result ){ |
| 1127 | + global $wgDiff3; |
| 1128 | + |
| 1129 | + # This check may also protect against code injection in |
| 1130 | + # case of broken installations. |
| 1131 | + if(! file_exists( $wgDiff3 ) ){ |
| 1132 | + wfDebug( "diff3 not found\n" ); |
| 1133 | + return false; |
| 1134 | + } |
| 1135 | + |
| 1136 | + # Make temporary files |
| 1137 | + $td = wfTempDir(); |
| 1138 | + $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' ); |
| 1139 | + $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' ); |
| 1140 | + $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' ); |
| 1141 | + |
| 1142 | + fwrite( $oldtextFile, $old ); fclose( $oldtextFile ); |
| 1143 | + fwrite( $mytextFile, $mine ); fclose( $mytextFile ); |
| 1144 | + fwrite( $yourtextFile, $yours ); fclose( $yourtextFile ); |
| 1145 | + |
| 1146 | + # Check for a conflict |
| 1147 | + $cmd = $wgDiff3 . ' -a --overlap-only ' . |
| 1148 | + wfEscapeShellArg( $mytextName ) . ' ' . |
| 1149 | + wfEscapeShellArg( $oldtextName ) . ' ' . |
| 1150 | + wfEscapeShellArg( $yourtextName ); |
| 1151 | + $handle = popen( $cmd, 'r' ); |
| 1152 | + |
| 1153 | + if( fgets( $handle, 1024 ) ){ |
| 1154 | + $conflict = true; |
| 1155 | + } else { |
| 1156 | + $conflict = false; |
| 1157 | + } |
| 1158 | + pclose( $handle ); |
| 1159 | + |
| 1160 | + # Merge differences |
| 1161 | + $cmd = $wgDiff3 . ' -a -e --merge ' . |
| 1162 | + wfEscapeShellArg( $mytextName, $oldtextName, $yourtextName ); |
| 1163 | + $handle = popen( $cmd, 'r' ); |
| 1164 | + $result = ''; |
| 1165 | + do { |
| 1166 | + $data = fread( $handle, 8192 ); |
| 1167 | + if ( strlen( $data ) == 0 ) { |
| 1168 | + break; |
| 1169 | + } |
| 1170 | + $result .= $data; |
| 1171 | + } while ( true ); |
| 1172 | + pclose( $handle ); |
| 1173 | + unlink( $mytextName ); unlink( $oldtextName ); unlink( $yourtextName ); |
| 1174 | + |
| 1175 | + if ( $result === '' && $old !== '' && $conflict == false ) { |
| 1176 | + wfDebug( "Unexpected null result from diff3. Command: $cmd\n" ); |
| 1177 | + $conflict = true; |
| 1178 | + } |
| 1179 | + return ! $conflict; |
| 1180 | +} |
| 1181 | + |
| 1182 | +/** |
| 1183 | + * Returns unified plain-text diff of two texts. |
| 1184 | + * Useful for machine processing of diffs. |
| 1185 | + * @param $before string The text before the changes. |
| 1186 | + * @param $after string The text after the changes. |
| 1187 | + * @param $params string Command-line options for the diff command. |
| 1188 | + * @return string Unified diff of $before and $after |
| 1189 | + */ |
| 1190 | +function wfDiff( $before, $after, $params = '-u' ) { |
| 1191 | + global $wgDiff; |
| 1192 | + |
| 1193 | + # This check may also protect against code injection in |
| 1194 | + # case of broken installations. |
| 1195 | + if( !file_exists( $wgDiff ) ){ |
| 1196 | + wfDebug( "diff executable not found\n" ); |
| 1197 | + $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) ); |
| 1198 | + $format = new UnifiedDiffFormatter(); |
| 1199 | + return $format->format( $diffs ); |
| 1200 | + } |
| 1201 | + |
| 1202 | + # Make temporary files |
| 1203 | + $td = wfTempDir(); |
| 1204 | + $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' ); |
| 1205 | + $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' ); |
| 1206 | + |
| 1207 | + fwrite( $oldtextFile, $before ); fclose( $oldtextFile ); |
| 1208 | + fwrite( $newtextFile, $after ); fclose( $newtextFile ); |
| 1209 | + |
| 1210 | + // Get the diff of the two files |
| 1211 | + $cmd = "$wgDiff " . $params . ' ' .wfEscapeShellArg( $oldtextName, $newtextName ); |
| 1212 | + |
| 1213 | + $h = popen( $cmd, 'r' ); |
| 1214 | + |
| 1215 | + $diff = ''; |
| 1216 | + |
| 1217 | + do { |
| 1218 | + $data = fread( $h, 8192 ); |
| 1219 | + if ( strlen( $data ) == 0 ) { |
| 1220 | + break; |
| 1221 | + } |
| 1222 | + $diff .= $data; |
| 1223 | + } while ( true ); |
| 1224 | + |
| 1225 | + // Clean up |
| 1226 | + pclose( $h ); |
| 1227 | + unlink( $oldtextName ); |
| 1228 | + unlink( $newtextName ); |
| 1229 | + |
| 1230 | + // Kill the --- and +++ lines. They're not useful. |
| 1231 | + $diff_lines = explode( "\n", $diff ); |
| 1232 | + if (strpos( $diff_lines[0], '---' ) === 0) { |
| 1233 | + unset($diff_lines[0]); |
| 1234 | + } |
| 1235 | + if (strpos( $diff_lines[1], '+++' ) === 0) { |
| 1236 | + unset($diff_lines[1]); |
| 1237 | + } |
| 1238 | + |
| 1239 | + $diff = implode( "\n", $diff_lines ); |
| 1240 | + |
| 1241 | + return $diff; |
| 1242 | +} |
| 1243 | + |
| 1244 | +/** |
| 1245 | + * @todo document |
| 1246 | + */ |
| 1247 | +function wfVarDump( $var ) { |
| 1248 | + global $wgOut; |
| 1249 | + $s = str_replace("\n","<br />\n", var_export( $var, true ) . "\n"); |
| 1250 | + if ( headers_sent() || !@is_object( $wgOut ) ) { |
| 1251 | + print $s; |
| 1252 | + } else { |
| 1253 | + $wgOut->addHTML( $s ); |
| 1254 | + } |
| 1255 | +} |
| 1256 | + |
| 1257 | +/** |
| 1258 | + * Provide a simple HTTP error. |
| 1259 | + */ |
| 1260 | +function wfHttpError( $code, $label, $desc ) { |
| 1261 | + global $wgOut; |
| 1262 | + $wgOut->disable(); |
| 1263 | + header( "HTTP/1.0 $code $label" ); |
| 1264 | + header( "Status: $code $label" ); |
| 1265 | + $wgOut->sendCacheControl(); |
| 1266 | + |
| 1267 | + header( 'Content-type: text/html; charset=utf-8' ); |
| 1268 | + print "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">". |
| 1269 | + "<html><head><title>" . |
| 1270 | + htmlspecialchars( $label ) . |
| 1271 | + "</title></head><body><h1>" . |
| 1272 | + htmlspecialchars( $label ) . |
| 1273 | + "</h1><p>" . |
| 1274 | + nl2br( htmlspecialchars( $desc ) ) . |
| 1275 | + "</p></body></html>\n"; |
| 1276 | +} |
| 1277 | + |
| 1278 | +/** |
| 1279 | + * Clear away any user-level output buffers, discarding contents. |
| 1280 | + * |
| 1281 | + * Suitable for 'starting afresh', for instance when streaming |
| 1282 | + * relatively large amounts of data without buffering, or wanting to |
| 1283 | + * output image files without ob_gzhandler's compression. |
| 1284 | + * |
| 1285 | + * The optional $resetGzipEncoding parameter controls suppression of |
| 1286 | + * the Content-Encoding header sent by ob_gzhandler; by default it |
| 1287 | + * is left. See comments for wfClearOutputBuffers() for why it would |
| 1288 | + * be used. |
| 1289 | + * |
| 1290 | + * Note that some PHP configuration options may add output buffer |
| 1291 | + * layers which cannot be removed; these are left in place. |
| 1292 | + * |
| 1293 | + * @param bool $resetGzipEncoding |
| 1294 | + */ |
| 1295 | +function wfResetOutputBuffers( $resetGzipEncoding=true ) { |
| 1296 | + if( $resetGzipEncoding ) { |
| 1297 | + // Suppress Content-Encoding and Content-Length |
| 1298 | + // headers from 1.10+s wfOutputHandler |
| 1299 | + global $wgDisableOutputCompression; |
| 1300 | + $wgDisableOutputCompression = true; |
| 1301 | + } |
| 1302 | + while( $status = ob_get_status() ) { |
| 1303 | + if( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) { |
| 1304 | + // Probably from zlib.output_compression or other |
| 1305 | + // PHP-internal setting which can't be removed. |
| 1306 | + // |
| 1307 | + // Give up, and hope the result doesn't break |
| 1308 | + // output behavior. |
| 1309 | + break; |
| 1310 | + } |
| 1311 | + if( !ob_end_clean() ) { |
| 1312 | + // Could not remove output buffer handler; abort now |
| 1313 | + // to avoid getting in some kind of infinite loop. |
| 1314 | + break; |
| 1315 | + } |
| 1316 | + if( $resetGzipEncoding ) { |
| 1317 | + if( $status['name'] == 'ob_gzhandler' ) { |
| 1318 | + // Reset the 'Content-Encoding' field set by this handler |
| 1319 | + // so we can start fresh. |
| 1320 | + header( 'Content-Encoding:' ); |
| 1321 | + } |
| 1322 | + } |
| 1323 | + } |
| 1324 | +} |
| 1325 | + |
| 1326 | +/** |
| 1327 | + * More legible than passing a 'false' parameter to wfResetOutputBuffers(): |
| 1328 | + * |
| 1329 | + * Clear away output buffers, but keep the Content-Encoding header |
| 1330 | + * produced by ob_gzhandler, if any. |
| 1331 | + * |
| 1332 | + * This should be used for HTTP 304 responses, where you need to |
| 1333 | + * preserve the Content-Encoding header of the real result, but |
| 1334 | + * also need to suppress the output of ob_gzhandler to keep to spec |
| 1335 | + * and avoid breaking Firefox in rare cases where the headers and |
| 1336 | + * body are broken over two packets. |
| 1337 | + */ |
| 1338 | +function wfClearOutputBuffers() { |
| 1339 | + wfResetOutputBuffers( false ); |
| 1340 | +} |
| 1341 | + |
| 1342 | +/** |
| 1343 | + * Converts an Accept-* header into an array mapping string values to quality |
| 1344 | + * factors |
| 1345 | + */ |
| 1346 | +function wfAcceptToPrefs( $accept, $def = '*/*' ) { |
| 1347 | + # No arg means accept anything (per HTTP spec) |
| 1348 | + if( !$accept ) { |
| 1349 | + return array( $def => 1.0 ); |
| 1350 | + } |
| 1351 | + |
| 1352 | + $prefs = array(); |
| 1353 | + |
| 1354 | + $parts = explode( ',', $accept ); |
| 1355 | + |
| 1356 | + foreach( $parts as $part ) { |
| 1357 | + # FIXME: doesn't deal with params like 'text/html; level=1' |
| 1358 | + @list( $value, $qpart ) = explode( ';', trim( $part ) ); |
| 1359 | + $match = array(); |
| 1360 | + if( !isset( $qpart ) ) { |
| 1361 | + $prefs[$value] = 1.0; |
| 1362 | + } elseif( preg_match( '/q\s*=\s*(\d*\.\d+)/', $qpart, $match ) ) { |
| 1363 | + $prefs[$value] = floatval($match[1]); |
| 1364 | + } |
| 1365 | + } |
| 1366 | + |
| 1367 | + return $prefs; |
| 1368 | +} |
| 1369 | + |
| 1370 | +/** |
| 1371 | + * Checks if a given MIME type matches any of the keys in the given |
| 1372 | + * array. Basic wildcards are accepted in the array keys. |
| 1373 | + * |
| 1374 | + * Returns the matching MIME type (or wildcard) if a match, otherwise |
| 1375 | + * NULL if no match. |
| 1376 | + * |
| 1377 | + * @param string $type |
| 1378 | + * @param array $avail |
| 1379 | + * @return string |
| 1380 | + * @private |
| 1381 | + */ |
| 1382 | +function mimeTypeMatch( $type, $avail ) { |
| 1383 | + if( array_key_exists($type, $avail) ) { |
| 1384 | + return $type; |
| 1385 | + } else { |
| 1386 | + $parts = explode( '/', $type ); |
| 1387 | + if( array_key_exists( $parts[0] . '/*', $avail ) ) { |
| 1388 | + return $parts[0] . '/*'; |
| 1389 | + } elseif( array_key_exists( '*/*', $avail ) ) { |
| 1390 | + return '*/*'; |
| 1391 | + } else { |
| 1392 | + return NULL; |
| 1393 | + } |
| 1394 | + } |
| 1395 | +} |
| 1396 | + |
| 1397 | +/** |
| 1398 | + * Returns the 'best' match between a client's requested internet media types |
| 1399 | + * and the server's list of available types. Each list should be an associative |
| 1400 | + * array of type to preference (preference is a float between 0.0 and 1.0). |
| 1401 | + * Wildcards in the types are acceptable. |
| 1402 | + * |
| 1403 | + * @param array $cprefs Client's acceptable type list |
| 1404 | + * @param array $sprefs Server's offered types |
| 1405 | + * @return string |
| 1406 | + * |
| 1407 | + * @todo FIXME: doesn't handle params like 'text/plain; charset=UTF-8' |
| 1408 | + * XXX: generalize to negotiate other stuff |
| 1409 | + */ |
| 1410 | +function wfNegotiateType( $cprefs, $sprefs ) { |
| 1411 | + $combine = array(); |
| 1412 | + |
| 1413 | + foreach( array_keys($sprefs) as $type ) { |
| 1414 | + $parts = explode( '/', $type ); |
| 1415 | + if( $parts[1] != '*' ) { |
| 1416 | + $ckey = mimeTypeMatch( $type, $cprefs ); |
| 1417 | + if( $ckey ) { |
| 1418 | + $combine[$type] = $sprefs[$type] * $cprefs[$ckey]; |
| 1419 | + } |
| 1420 | + } |
| 1421 | + } |
| 1422 | + |
| 1423 | + foreach( array_keys( $cprefs ) as $type ) { |
| 1424 | + $parts = explode( '/', $type ); |
| 1425 | + if( $parts[1] != '*' && !array_key_exists( $type, $sprefs ) ) { |
| 1426 | + $skey = mimeTypeMatch( $type, $sprefs ); |
| 1427 | + if( $skey ) { |
| 1428 | + $combine[$type] = $sprefs[$skey] * $cprefs[$type]; |
| 1429 | + } |
| 1430 | + } |
| 1431 | + } |
| 1432 | + |
| 1433 | + $bestq = 0; |
| 1434 | + $besttype = NULL; |
| 1435 | + |
| 1436 | + foreach( array_keys( $combine ) as $type ) { |
| 1437 | + if( $combine[$type] > $bestq ) { |
| 1438 | + $besttype = $type; |
| 1439 | + $bestq = $combine[$type]; |
| 1440 | + } |
| 1441 | + } |
| 1442 | + |
| 1443 | + return $besttype; |
| 1444 | +} |
| 1445 | + |
| 1446 | +/** |
| 1447 | + * Array lookup |
| 1448 | + * Returns an array where the values in the first array are replaced by the |
| 1449 | + * values in the second array with the corresponding keys |
| 1450 | + * |
| 1451 | + * @return array |
| 1452 | + */ |
| 1453 | +function wfArrayLookup( $a, $b ) { |
| 1454 | + return array_flip( array_intersect( array_flip( $a ), array_keys( $b ) ) ); |
| 1455 | +} |
| 1456 | + |
| 1457 | +/** |
| 1458 | + * Convenience function; returns MediaWiki timestamp for the present time. |
| 1459 | + * @return string |
| 1460 | + */ |
| 1461 | +function wfTimestampNow() { |
| 1462 | + # return NOW |
| 1463 | + return wfTimestamp( TS_MW, time() ); |
| 1464 | +} |
| 1465 | + |
| 1466 | +/** |
| 1467 | + * Reference-counted warning suppression |
| 1468 | + */ |
| 1469 | +function wfSuppressWarnings( $end = false ) { |
| 1470 | + static $suppressCount = 0; |
| 1471 | + static $originalLevel = false; |
| 1472 | + |
| 1473 | + if ( $end ) { |
| 1474 | + if ( $suppressCount ) { |
| 1475 | + --$suppressCount; |
| 1476 | + if ( !$suppressCount ) { |
| 1477 | + error_reporting( $originalLevel ); |
| 1478 | + } |
| 1479 | + } |
| 1480 | + } else { |
| 1481 | + if ( !$suppressCount ) { |
| 1482 | + $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE ) ); |
| 1483 | + } |
| 1484 | + ++$suppressCount; |
| 1485 | + } |
| 1486 | +} |
| 1487 | + |
| 1488 | +/** |
| 1489 | + * Restore error level to previous value |
| 1490 | + */ |
| 1491 | +function wfRestoreWarnings() { |
| 1492 | + wfSuppressWarnings( true ); |
| 1493 | +} |
| 1494 | + |
| 1495 | +# Autodetect, convert and provide timestamps of various types |
| 1496 | + |
| 1497 | +/** |
| 1498 | + * Unix time - the number of seconds since 1970-01-01 00:00:00 UTC |
| 1499 | + */ |
| 1500 | +define('TS_UNIX', 0); |
| 1501 | + |
| 1502 | +/** |
| 1503 | + * MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS) |
| 1504 | + */ |
| 1505 | +define('TS_MW', 1); |
| 1506 | + |
| 1507 | +/** |
| 1508 | + * MySQL DATETIME (YYYY-MM-DD HH:MM:SS) |
| 1509 | + */ |
| 1510 | +define('TS_DB', 2); |
| 1511 | + |
| 1512 | +/** |
| 1513 | + * RFC 2822 format, for E-mail and HTTP headers |
| 1514 | + */ |
| 1515 | +define('TS_RFC2822', 3); |
| 1516 | + |
| 1517 | +/** |
| 1518 | + * ISO 8601 format with no timezone: 1986-02-09T20:00:00Z |
| 1519 | + * |
| 1520 | + * This is used by Special:Export |
| 1521 | + */ |
| 1522 | +define('TS_ISO_8601', 4); |
| 1523 | + |
| 1524 | +/** |
| 1525 | + * An Exif timestamp (YYYY:MM:DD HH:MM:SS) |
| 1526 | + * |
| 1527 | + * @see http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the |
| 1528 | + * DateTime tag and page 36 for the DateTimeOriginal and |
| 1529 | + * DateTimeDigitized tags. |
| 1530 | + */ |
| 1531 | +define('TS_EXIF', 5); |
| 1532 | + |
| 1533 | +/** |
| 1534 | + * Oracle format time. |
| 1535 | + */ |
| 1536 | +define('TS_ORACLE', 6); |
| 1537 | + |
| 1538 | +/** |
| 1539 | + * Postgres format time. |
| 1540 | + */ |
| 1541 | +define('TS_POSTGRES', 7); |
| 1542 | + |
| 1543 | +/** |
| 1544 | + * @param mixed $outputtype A timestamp in one of the supported formats, the |
| 1545 | + * function will autodetect which format is supplied |
| 1546 | + * and act accordingly. |
| 1547 | + * @return string Time in the format specified in $outputtype |
| 1548 | + */ |
| 1549 | +function wfTimestamp($outputtype=TS_UNIX,$ts=0) { |
| 1550 | + $uts = 0; |
| 1551 | + $da = array(); |
| 1552 | + if ($ts==0) { |
| 1553 | + $uts=time(); |
| 1554 | + } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D',$ts,$da)) { |
| 1555 | + # TS_DB |
| 1556 | + } elseif (preg_match('/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D',$ts,$da)) { |
| 1557 | + # TS_EXIF |
| 1558 | + } elseif (preg_match('/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/D',$ts,$da)) { |
| 1559 | + # TS_MW |
| 1560 | + } elseif (preg_match('/^\d{1,13}$/D',$ts)) { |
| 1561 | + # TS_UNIX |
| 1562 | + $uts = $ts; |
| 1563 | + } elseif (preg_match('/^\d{1,2}-...-\d\d(?:\d\d)? \d\d\.\d\d\.\d\d/', $ts)) { |
| 1564 | + # TS_ORACLE |
| 1565 | + $uts = strtotime(preg_replace('/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3", |
| 1566 | + str_replace("+00:00", "UTC", $ts))); |
| 1567 | + } elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/', $ts, $da)) { |
| 1568 | + # TS_ISO_8601 |
| 1569 | + } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)[\+\- ](\d\d)$/',$ts,$da)) { |
| 1570 | + # TS_POSTGRES |
| 1571 | + } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/',$ts,$da)) { |
| 1572 | + # TS_POSTGRES |
| 1573 | + } else { |
| 1574 | + # Bogus value; fall back to the epoch... |
| 1575 | + wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n"); |
| 1576 | + $uts = 0; |
| 1577 | + } |
| 1578 | + |
| 1579 | + if (count( $da ) ) { |
| 1580 | + // Warning! gmmktime() acts oddly if the month or day is set to 0 |
| 1581 | + // We may want to handle that explicitly at some point |
| 1582 | + $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6], |
| 1583 | + (int)$da[2],(int)$da[3],(int)$da[1]); |
| 1584 | + } |
| 1585 | + |
| 1586 | + switch($outputtype) { |
| 1587 | + case TS_UNIX: |
| 1588 | + return $uts; |
| 1589 | + case TS_MW: |
| 1590 | + return gmdate( 'YmdHis', $uts ); |
| 1591 | + case TS_DB: |
| 1592 | + return gmdate( 'Y-m-d H:i:s', $uts ); |
| 1593 | + case TS_ISO_8601: |
| 1594 | + return gmdate( 'Y-m-d\TH:i:s\Z', $uts ); |
| 1595 | + // This shouldn't ever be used, but is included for completeness |
| 1596 | + case TS_EXIF: |
| 1597 | + return gmdate( 'Y:m:d H:i:s', $uts ); |
| 1598 | + case TS_RFC2822: |
| 1599 | + return gmdate( 'D, d M Y H:i:s', $uts ) . ' GMT'; |
| 1600 | + case TS_ORACLE: |
| 1601 | + return gmdate( 'd-M-y h.i.s A', $uts) . ' +00:00'; |
| 1602 | + case TS_POSTGRES: |
| 1603 | + return gmdate( 'Y-m-d H:i:s', $uts) . ' GMT'; |
| 1604 | + default: |
| 1605 | + throw new MWException( 'wfTimestamp() called with illegal output type.'); |
| 1606 | + } |
| 1607 | +} |
| 1608 | + |
| 1609 | +/** |
| 1610 | + * Return a formatted timestamp, or null if input is null. |
| 1611 | + * For dealing with nullable timestamp columns in the database. |
| 1612 | + * @param int $outputtype |
| 1613 | + * @param string $ts |
| 1614 | + * @return string |
| 1615 | + */ |
| 1616 | +function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) { |
| 1617 | + if( is_null( $ts ) ) { |
| 1618 | + return null; |
| 1619 | + } else { |
| 1620 | + return wfTimestamp( $outputtype, $ts ); |
| 1621 | + } |
| 1622 | +} |
| 1623 | + |
| 1624 | +/** |
| 1625 | + * Check if the operating system is Windows |
| 1626 | + * |
| 1627 | + * @return bool True if it's Windows, False otherwise. |
| 1628 | + */ |
| 1629 | +function wfIsWindows() { |
| 1630 | + if (substr(php_uname(), 0, 7) == 'Windows') { |
| 1631 | + return true; |
| 1632 | + } else { |
| 1633 | + return false; |
| 1634 | + } |
| 1635 | +} |
| 1636 | + |
| 1637 | +/** |
| 1638 | + * Swap two variables |
| 1639 | + */ |
| 1640 | +function swap( &$x, &$y ) { |
| 1641 | + $z = $x; |
| 1642 | + $x = $y; |
| 1643 | + $y = $z; |
| 1644 | +} |
| 1645 | + |
| 1646 | +function wfGetCachedNotice( $name ) { |
| 1647 | + global $wgOut, $parserMemc; |
| 1648 | + $fname = 'wfGetCachedNotice'; |
| 1649 | + wfProfileIn( $fname ); |
| 1650 | + |
| 1651 | + $needParse = false; |
| 1652 | + |
| 1653 | + if( $name === 'default' ) { |
| 1654 | + // special case |
| 1655 | + global $wgSiteNotice; |
| 1656 | + $notice = $wgSiteNotice; |
| 1657 | + if( empty( $notice ) ) { |
| 1658 | + wfProfileOut( $fname ); |
| 1659 | + return false; |
| 1660 | + } |
| 1661 | + } else { |
| 1662 | + $notice = wfMsgForContentNoTrans( $name ); |
| 1663 | + if( wfEmptyMsg( $name, $notice ) || $notice == '-' ) { |
| 1664 | + wfProfileOut( $fname ); |
| 1665 | + return( false ); |
| 1666 | + } |
| 1667 | + } |
| 1668 | + |
| 1669 | + $cachedNotice = $parserMemc->get( wfMemcKey( $name ) ); |
| 1670 | + if( is_array( $cachedNotice ) ) { |
| 1671 | + if( md5( $notice ) == $cachedNotice['hash'] ) { |
| 1672 | + $notice = $cachedNotice['html']; |
| 1673 | + } else { |
| 1674 | + $needParse = true; |
| 1675 | + } |
| 1676 | + } else { |
| 1677 | + $needParse = true; |
| 1678 | + } |
| 1679 | + |
| 1680 | + if( $needParse ) { |
| 1681 | + if( is_object( $wgOut ) ) { |
| 1682 | + $parsed = $wgOut->parse( $notice ); |
| 1683 | + $parserMemc->set( wfMemcKey( $name ), array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 ); |
| 1684 | + $notice = $parsed; |
| 1685 | + } else { |
| 1686 | + wfDebug( 'wfGetCachedNotice called for ' . $name . ' with no $wgOut available' ); |
| 1687 | + $notice = ''; |
| 1688 | + } |
| 1689 | + } |
| 1690 | + |
| 1691 | + wfProfileOut( $fname ); |
| 1692 | + return $notice; |
| 1693 | +} |
| 1694 | + |
| 1695 | +function wfGetNamespaceNotice() { |
| 1696 | + global $wgTitle; |
| 1697 | + |
| 1698 | + # Paranoia |
| 1699 | + if ( !isset( $wgTitle ) || !is_object( $wgTitle ) ) |
| 1700 | + return ""; |
| 1701 | + |
| 1702 | + $fname = 'wfGetNamespaceNotice'; |
| 1703 | + wfProfileIn( $fname ); |
| 1704 | + |
| 1705 | + $key = "namespacenotice-" . $wgTitle->getNsText(); |
| 1706 | + $namespaceNotice = wfGetCachedNotice( $key ); |
| 1707 | + if ( $namespaceNotice && substr ( $namespaceNotice , 0 ,7 ) != "<p><" ) { |
| 1708 | + $namespaceNotice = '<div id="namespacebanner">' . $namespaceNotice . "</div>"; |
| 1709 | + } else { |
| 1710 | + $namespaceNotice = ""; |
| 1711 | + } |
| 1712 | + |
| 1713 | + wfProfileOut( $fname ); |
| 1714 | + return $namespaceNotice; |
| 1715 | +} |
| 1716 | + |
| 1717 | +function wfGetSiteNotice() { |
| 1718 | + global $wgUser, $wgSiteNotice; |
| 1719 | + $fname = 'wfGetSiteNotice'; |
| 1720 | + wfProfileIn( $fname ); |
| 1721 | + $siteNotice = ''; |
| 1722 | + |
| 1723 | + if( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice ) ) ) { |
| 1724 | + if( is_object( $wgUser ) && $wgUser->isLoggedIn() ) { |
| 1725 | + $siteNotice = wfGetCachedNotice( 'sitenotice' ); |
| 1726 | + } else { |
| 1727 | + $anonNotice = wfGetCachedNotice( 'anonnotice' ); |
| 1728 | + if( !$anonNotice ) { |
| 1729 | + $siteNotice = wfGetCachedNotice( 'sitenotice' ); |
| 1730 | + } else { |
| 1731 | + $siteNotice = $anonNotice; |
| 1732 | + } |
| 1733 | + } |
| 1734 | + if( !$siteNotice ) { |
| 1735 | + $siteNotice = wfGetCachedNotice( 'default' ); |
| 1736 | + } |
| 1737 | + } |
| 1738 | + |
| 1739 | + wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice ) ); |
| 1740 | + wfProfileOut( $fname ); |
| 1741 | + return $siteNotice; |
| 1742 | +} |
| 1743 | + |
| 1744 | +/** |
| 1745 | + * BC wrapper for MimeMagic::singleton() |
| 1746 | + * @deprecated |
| 1747 | + */ |
| 1748 | +function &wfGetMimeMagic() { |
| 1749 | + return MimeMagic::singleton(); |
| 1750 | +} |
| 1751 | + |
| 1752 | +/** |
| 1753 | + * Tries to get the system directory for temporary files. |
| 1754 | + * The TMPDIR, TMP, and TEMP environment variables are checked in sequence, |
| 1755 | + * and if none are set /tmp is returned as the generic Unix default. |
| 1756 | + * |
| 1757 | + * NOTE: When possible, use the tempfile() function to create temporary |
| 1758 | + * files to avoid race conditions on file creation, etc. |
| 1759 | + * |
| 1760 | + * @return string |
| 1761 | + */ |
| 1762 | +function wfTempDir() { |
| 1763 | + foreach( array( 'TMPDIR', 'TMP', 'TEMP' ) as $var ) { |
| 1764 | + $tmp = getenv( $var ); |
| 1765 | + if( $tmp && file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) { |
| 1766 | + return $tmp; |
| 1767 | + } |
| 1768 | + } |
| 1769 | + # Hope this is Unix of some kind! |
| 1770 | + return '/tmp'; |
| 1771 | +} |
| 1772 | + |
| 1773 | +/** |
| 1774 | + * Make directory, and make all parent directories if they don't exist |
| 1775 | + * |
| 1776 | + * @param string $fullDir Full path to directory to create |
| 1777 | + * @param int $mode Chmod value to use, default is $wgDirectoryMode |
| 1778 | + * @return bool |
| 1779 | + */ |
| 1780 | +function wfMkdirParents( $fullDir, $mode = null ) { |
| 1781 | + global $wgDirectoryMode; |
| 1782 | + if( strval( $fullDir ) === '' ) |
| 1783 | + return true; |
| 1784 | + if( file_exists( $fullDir ) ) |
| 1785 | + return true; |
| 1786 | + // If not defined or isn't an int, set to default |
| 1787 | + if ( is_null( $mode ) ) { |
| 1788 | + $mode = $wgDirectoryMode; |
| 1789 | + } |
| 1790 | + |
| 1791 | + |
| 1792 | + # Go back through the paths to find the first directory that exists |
| 1793 | + $currentDir = $fullDir; |
| 1794 | + $createList = array(); |
| 1795 | + while ( strval( $currentDir ) !== '' && !file_exists( $currentDir ) ) { |
| 1796 | + # Strip trailing slashes |
| 1797 | + $currentDir = rtrim( $currentDir, '/\\' ); |
| 1798 | + |
| 1799 | + # Add to create list |
| 1800 | + $createList[] = $currentDir; |
| 1801 | + |
| 1802 | + # Find next delimiter searching from the end |
| 1803 | + $p = max( strrpos( $currentDir, '/' ), strrpos( $currentDir, '\\' ) ); |
| 1804 | + if ( $p === false ) { |
| 1805 | + $currentDir = false; |
| 1806 | + } else { |
| 1807 | + $currentDir = substr( $currentDir, 0, $p ); |
| 1808 | + } |
| 1809 | + } |
| 1810 | + |
| 1811 | + if ( count( $createList ) == 0 ) { |
| 1812 | + # Directory specified already exists |
| 1813 | + return true; |
| 1814 | + } elseif ( $currentDir === false ) { |
| 1815 | + # Went all the way back to root and it apparently doesn't exist |
| 1816 | + wfDebugLog( 'mkdir', "Root doesn't exist?\n" ); |
| 1817 | + return false; |
| 1818 | + } |
| 1819 | + # Now go forward creating directories |
| 1820 | + $createList = array_reverse( $createList ); |
| 1821 | + |
| 1822 | + # Is the parent directory writable? |
| 1823 | + if ( $currentDir === '' ) { |
| 1824 | + $currentDir = '/'; |
| 1825 | + } |
| 1826 | + if ( !is_writable( $currentDir ) ) { |
| 1827 | + wfDebugLog( 'mkdir', "Not writable: $currentDir\n" ); |
| 1828 | + return false; |
| 1829 | + } |
| 1830 | + |
| 1831 | + foreach ( $createList as $dir ) { |
| 1832 | + # use chmod to override the umask, as suggested by the PHP manual |
| 1833 | + if ( !mkdir( $dir, $mode ) || !chmod( $dir, $mode ) ) { |
| 1834 | + wfDebugLog( 'mkdir', "Unable to create directory $dir\n" ); |
| 1835 | + return false; |
| 1836 | + } |
| 1837 | + } |
| 1838 | + return true; |
| 1839 | +} |
| 1840 | + |
| 1841 | +/** |
| 1842 | + * Increment a statistics counter |
| 1843 | + */ |
| 1844 | +function wfIncrStats( $key ) { |
| 1845 | + global $wgStatsMethod; |
| 1846 | + |
| 1847 | + if( $wgStatsMethod == 'udp' ) { |
| 1848 | + global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgDBname; |
| 1849 | + static $socket; |
| 1850 | + if (!$socket) { |
| 1851 | + $socket=socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); |
| 1852 | + $statline="stats/{$wgDBname} - 1 1 1 1 1 -total\n"; |
| 1853 | + socket_sendto($socket,$statline,strlen($statline),0,$wgUDPProfilerHost,$wgUDPProfilerPort); |
| 1854 | + } |
| 1855 | + $statline="stats/{$wgDBname} - 1 1 1 1 1 {$key}\n"; |
| 1856 | + @socket_sendto($socket,$statline,strlen($statline),0,$wgUDPProfilerHost,$wgUDPProfilerPort); |
| 1857 | + } elseif( $wgStatsMethod == 'cache' ) { |
| 1858 | + global $wgMemc; |
| 1859 | + $key = wfMemcKey( 'stats', $key ); |
| 1860 | + if ( is_null( $wgMemc->incr( $key ) ) ) { |
| 1861 | + $wgMemc->add( $key, 1 ); |
| 1862 | + } |
| 1863 | + } else { |
| 1864 | + // Disabled |
| 1865 | + } |
| 1866 | +} |
| 1867 | + |
| 1868 | +/** |
| 1869 | + * @param mixed $nr The number to format |
| 1870 | + * @param int $acc The number of digits after the decimal point, default 2 |
| 1871 | + * @param bool $round Whether or not to round the value, default true |
| 1872 | + * @return float |
| 1873 | + */ |
| 1874 | +function wfPercent( $nr, $acc = 2, $round = true ) { |
| 1875 | + $ret = sprintf( "%.${acc}f", $nr ); |
| 1876 | + return $round ? round( $ret, $acc ) . '%' : "$ret%"; |
| 1877 | +} |
| 1878 | + |
| 1879 | +/** |
| 1880 | + * Encrypt a username/password. |
| 1881 | + * |
| 1882 | + * @param string $userid ID of the user |
| 1883 | + * @param string $password Password of the user |
| 1884 | + * @return string Hashed password |
| 1885 | + * @deprecated Use User::crypt() or User::oldCrypt() instead |
| 1886 | + */ |
| 1887 | +function wfEncryptPassword( $userid, $password ) { |
| 1888 | + wfDeprecated(__FUNCTION__); |
| 1889 | + # Just wrap around User::oldCrypt() |
| 1890 | + return User::oldCrypt($password, $userid); |
| 1891 | +} |
| 1892 | + |
| 1893 | +/** |
| 1894 | + * Appends to second array if $value differs from that in $default |
| 1895 | + */ |
| 1896 | +function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) { |
| 1897 | + if ( is_null( $changed ) ) { |
| 1898 | + throw new MWException('GlobalFunctions::wfAppendToArrayIfNotDefault got null'); |
| 1899 | + } |
| 1900 | + if ( $default[$key] !== $value ) { |
| 1901 | + $changed[$key] = $value; |
| 1902 | + } |
| 1903 | +} |
| 1904 | + |
| 1905 | +/** |
| 1906 | + * Since wfMsg() and co suck, they don't return false if the message key they |
| 1907 | + * looked up didn't exist but a XHTML string, this function checks for the |
| 1908 | + * nonexistance of messages by looking at wfMsg() output |
| 1909 | + * |
| 1910 | + * @param $msg The message key looked up |
| 1911 | + * @param $wfMsgOut The output of wfMsg*() |
| 1912 | + * @return bool |
| 1913 | + */ |
| 1914 | +function wfEmptyMsg( $msg, $wfMsgOut ) { |
| 1915 | + return $wfMsgOut === htmlspecialchars( "<$msg>" ); |
| 1916 | +} |
| 1917 | + |
| 1918 | +/** |
| 1919 | + * Find out whether or not a mixed variable exists in a string |
| 1920 | + * |
| 1921 | + * @param mixed needle |
| 1922 | + * @param string haystack |
| 1923 | + * @return bool |
| 1924 | + */ |
| 1925 | +function in_string( $needle, $str ) { |
| 1926 | + return strpos( $str, $needle ) !== false; |
| 1927 | +} |
| 1928 | + |
| 1929 | +function wfSpecialList( $page, $details ) { |
| 1930 | + global $wgContLang; |
| 1931 | + $details = $details ? ' ' . $wgContLang->getDirMark() . "($details)" : ""; |
| 1932 | + return $page . $details; |
| 1933 | +} |
| 1934 | + |
| 1935 | +/** |
| 1936 | + * Returns a regular expression of url protocols |
| 1937 | + * |
| 1938 | + * @return string |
| 1939 | + */ |
| 1940 | +function wfUrlProtocols() { |
| 1941 | + global $wgUrlProtocols; |
| 1942 | + |
| 1943 | + // Support old-style $wgUrlProtocols strings, for backwards compatibility |
| 1944 | + // with LocalSettings files from 1.5 |
| 1945 | + if ( is_array( $wgUrlProtocols ) ) { |
| 1946 | + $protocols = array(); |
| 1947 | + foreach ($wgUrlProtocols as $protocol) |
| 1948 | + $protocols[] = preg_quote( $protocol, '/' ); |
| 1949 | + |
| 1950 | + return implode( '|', $protocols ); |
| 1951 | + } else { |
| 1952 | + return $wgUrlProtocols; |
| 1953 | + } |
| 1954 | +} |
| 1955 | + |
| 1956 | +/** |
| 1957 | + * Safety wrapper around ini_get() for boolean settings. |
| 1958 | + * The values returned from ini_get() are pre-normalized for settings |
| 1959 | + * set via php.ini or php_flag/php_admin_flag... but *not* |
| 1960 | + * for those set via php_value/php_admin_value. |
| 1961 | + * |
| 1962 | + * It's fairly common for people to use php_value instead of php_flag, |
| 1963 | + * which can leave you with an 'off' setting giving a false positive |
| 1964 | + * for code that just takes the ini_get() return value as a boolean. |
| 1965 | + * |
| 1966 | + * To make things extra interesting, setting via php_value accepts |
| 1967 | + * "true" and "yes" as true, but php.ini and php_flag consider them false. :) |
| 1968 | + * Unrecognized values go false... again opposite PHP's own coercion |
| 1969 | + * from string to bool. |
| 1970 | + * |
| 1971 | + * Luckily, 'properly' set settings will always come back as '0' or '1', |
| 1972 | + * so we only have to worry about them and the 'improper' settings. |
| 1973 | + * |
| 1974 | + * I frickin' hate PHP... :P |
| 1975 | + * |
| 1976 | + * @param string $setting |
| 1977 | + * @return bool |
| 1978 | + */ |
| 1979 | +function wfIniGetBool( $setting ) { |
| 1980 | + $val = ini_get( $setting ); |
| 1981 | + // 'on' and 'true' can't have whitespace around them, but '1' can. |
| 1982 | + return strtolower( $val ) == 'on' |
| 1983 | + || strtolower( $val ) == 'true' |
| 1984 | + || strtolower( $val ) == 'yes' |
| 1985 | + || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function |
| 1986 | +} |
| 1987 | + |
| 1988 | +/** |
| 1989 | + * Execute a shell command, with time and memory limits mirrored from the PHP |
| 1990 | + * configuration if supported. |
| 1991 | + * @param $cmd Command line, properly escaped for shell. |
| 1992 | + * @param &$retval optional, will receive the program's exit code. |
| 1993 | + * (non-zero is usually failure) |
| 1994 | + * @return collected stdout as a string (trailing newlines stripped) |
| 1995 | + */ |
| 1996 | +function wfShellExec( $cmd, &$retval=null ) { |
| 1997 | + global $IP, $wgMaxShellMemory, $wgMaxShellFileSize; |
| 1998 | + |
| 1999 | + if( wfIniGetBool( 'safe_mode' ) ) { |
| 2000 | + wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" ); |
| 2001 | + $retval = 1; |
| 2002 | + return "Unable to run external programs in safe mode."; |
| 2003 | + } |
| 2004 | + |
| 2005 | + if ( php_uname( 's' ) == 'Linux' ) { |
| 2006 | + $time = intval( ini_get( 'max_execution_time' ) ); |
| 2007 | + $mem = intval( $wgMaxShellMemory ); |
| 2008 | + $filesize = intval( $wgMaxShellFileSize ); |
| 2009 | + |
| 2010 | + if ( $time > 0 && $mem > 0 ) { |
| 2011 | + $script = "$IP/bin/ulimit4.sh"; |
| 2012 | + if ( is_executable( $script ) ) { |
| 2013 | + $cmd = escapeshellarg( $script ) . " $time $mem $filesize " . escapeshellarg( $cmd ); |
| 2014 | + } |
| 2015 | + } |
| 2016 | + } elseif ( php_uname( 's' ) == 'Windows NT' ) { |
| 2017 | + # This is a hack to work around PHP's flawed invocation of cmd.exe |
| 2018 | + # http://news.php.net/php.internals/21796 |
| 2019 | + $cmd = '"' . $cmd . '"'; |
| 2020 | + } |
| 2021 | + wfDebug( "wfShellExec: $cmd\n" ); |
| 2022 | + |
| 2023 | + $retval = 1; // error by default? |
| 2024 | + ob_start(); |
| 2025 | + passthru( $cmd, $retval ); |
| 2026 | + $output = ob_get_contents(); |
| 2027 | + ob_end_clean(); |
| 2028 | + return $output; |
| 2029 | + |
| 2030 | +} |
| 2031 | + |
| 2032 | +/** |
| 2033 | + * This function works like "use VERSION" in Perl, the program will die with a |
| 2034 | + * backtrace if the current version of PHP is less than the version provided |
| 2035 | + * |
| 2036 | + * This is useful for extensions which due to their nature are not kept in sync |
| 2037 | + * with releases, and might depend on other versions of PHP than the main code |
| 2038 | + * |
| 2039 | + * Note: PHP might die due to parsing errors in some cases before it ever |
| 2040 | + * manages to call this function, such is life |
| 2041 | + * |
| 2042 | + * @see perldoc -f use |
| 2043 | + * |
| 2044 | + * @param mixed $version The version to check, can be a string, an integer, or |
| 2045 | + * a float |
| 2046 | + */ |
| 2047 | +function wfUsePHP( $req_ver ) { |
| 2048 | + $php_ver = PHP_VERSION; |
| 2049 | + |
| 2050 | + if ( version_compare( $php_ver, (string)$req_ver, '<' ) ) |
| 2051 | + throw new MWException( "PHP $req_ver required--this is only $php_ver" ); |
| 2052 | +} |
| 2053 | + |
| 2054 | +/** |
| 2055 | + * This function works like "use VERSION" in Perl except it checks the version |
| 2056 | + * of MediaWiki, the program will die with a backtrace if the current version |
| 2057 | + * of MediaWiki is less than the version provided. |
| 2058 | + * |
| 2059 | + * This is useful for extensions which due to their nature are not kept in sync |
| 2060 | + * with releases |
| 2061 | + * |
| 2062 | + * @see perldoc -f use |
| 2063 | + * |
| 2064 | + * @param mixed $version The version to check, can be a string, an integer, or |
| 2065 | + * a float |
| 2066 | + */ |
| 2067 | +function wfUseMW( $req_ver ) { |
| 2068 | + global $wgVersion; |
| 2069 | + |
| 2070 | + if ( version_compare( $wgVersion, (string)$req_ver, '<' ) ) |
| 2071 | + throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" ); |
| 2072 | +} |
| 2073 | + |
| 2074 | +/** |
| 2075 | + * @deprecated use StringUtils::escapeRegexReplacement |
| 2076 | + */ |
| 2077 | +function wfRegexReplacement( $string ) { |
| 2078 | + return StringUtils::escapeRegexReplacement( $string ); |
| 2079 | +} |
| 2080 | + |
| 2081 | +/** |
| 2082 | + * Return the final portion of a pathname. |
| 2083 | + * Reimplemented because PHP5's basename() is buggy with multibyte text. |
| 2084 | + * http://bugs.php.net/bug.php?id=33898 |
| 2085 | + * |
| 2086 | + * PHP's basename() only considers '\' a pathchar on Windows and Netware. |
| 2087 | + * We'll consider it so always, as we don't want \s in our Unix paths either. |
| 2088 | + * |
| 2089 | + * @param string $path |
| 2090 | + * @param string $suffix to remove if present |
| 2091 | + * @return string |
| 2092 | + */ |
| 2093 | +function wfBaseName( $path, $suffix='' ) { |
| 2094 | + $encSuffix = ($suffix == '') |
| 2095 | + ? '' |
| 2096 | + : ( '(?:' . preg_quote( $suffix, '#' ) . ')?' ); |
| 2097 | + $matches = array(); |
| 2098 | + if( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) { |
| 2099 | + return $matches[1]; |
| 2100 | + } else { |
| 2101 | + return ''; |
| 2102 | + } |
| 2103 | +} |
| 2104 | + |
| 2105 | +/** |
| 2106 | + * Generate a relative path name to the given file. |
| 2107 | + * May explode on non-matching case-insensitive paths, |
| 2108 | + * funky symlinks, etc. |
| 2109 | + * |
| 2110 | + * @param string $path Absolute destination path including target filename |
| 2111 | + * @param string $from Absolute source path, directory only |
| 2112 | + * @return string |
| 2113 | + */ |
| 2114 | +function wfRelativePath( $path, $from ) { |
| 2115 | + // Normalize mixed input on Windows... |
| 2116 | + $path = str_replace( '/', DIRECTORY_SEPARATOR, $path ); |
| 2117 | + $from = str_replace( '/', DIRECTORY_SEPARATOR, $from ); |
| 2118 | + |
| 2119 | + // Trim trailing slashes -- fix for drive root |
| 2120 | + $path = rtrim( $path, DIRECTORY_SEPARATOR ); |
| 2121 | + $from = rtrim( $from, DIRECTORY_SEPARATOR ); |
| 2122 | + |
| 2123 | + $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) ); |
| 2124 | + $against = explode( DIRECTORY_SEPARATOR, $from ); |
| 2125 | + |
| 2126 | + if( $pieces[0] !== $against[0] ) { |
| 2127 | + // Non-matching Windows drive letters? |
| 2128 | + // Return a full path. |
| 2129 | + return $path; |
| 2130 | + } |
| 2131 | + |
| 2132 | + // Trim off common prefix |
| 2133 | + while( count( $pieces ) && count( $against ) |
| 2134 | + && $pieces[0] == $against[0] ) { |
| 2135 | + array_shift( $pieces ); |
| 2136 | + array_shift( $against ); |
| 2137 | + } |
| 2138 | + |
| 2139 | + // relative dots to bump us to the parent |
| 2140 | + while( count( $against ) ) { |
| 2141 | + array_unshift( $pieces, '..' ); |
| 2142 | + array_shift( $against ); |
| 2143 | + } |
| 2144 | + |
| 2145 | + array_push( $pieces, wfBaseName( $path ) ); |
| 2146 | + |
| 2147 | + return implode( DIRECTORY_SEPARATOR, $pieces ); |
| 2148 | +} |
| 2149 | + |
| 2150 | +/** |
| 2151 | + * array_merge() does awful things with "numeric" indexes, including |
| 2152 | + * string indexes when happen to look like integers. When we want |
| 2153 | + * to merge arrays with arbitrary string indexes, we don't want our |
| 2154 | + * arrays to be randomly corrupted just because some of them consist |
| 2155 | + * of numbers. |
| 2156 | + * |
| 2157 | + * Fuck you, PHP. Fuck you in the ear! |
| 2158 | + * |
| 2159 | + * @param array $array1, [$array2, [...]] |
| 2160 | + * @return array |
| 2161 | + */ |
| 2162 | +function wfArrayMerge( $array1/* ... */ ) { |
| 2163 | + $out = $array1; |
| 2164 | + for( $i = 1; $i < func_num_args(); $i++ ) { |
| 2165 | + foreach( func_get_arg( $i ) as $key => $value ) { |
| 2166 | + $out[$key] = $value; |
| 2167 | + } |
| 2168 | + } |
| 2169 | + return $out; |
| 2170 | +} |
| 2171 | + |
| 2172 | +/** |
| 2173 | + * Make a URL index, appropriate for the el_index field of externallinks. |
| 2174 | + */ |
| 2175 | +function wfMakeUrlIndex( $url ) { |
| 2176 | + global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php |
| 2177 | + wfSuppressWarnings(); |
| 2178 | + $bits = parse_url( $url ); |
| 2179 | + wfRestoreWarnings(); |
| 2180 | + if ( !$bits ) { |
| 2181 | + return false; |
| 2182 | + } |
| 2183 | + // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it |
| 2184 | + $delimiter = ''; |
| 2185 | + if ( in_array( $bits['scheme'] . '://' , $wgUrlProtocols ) ) { |
| 2186 | + $delimiter = '://'; |
| 2187 | + } elseif ( in_array( $bits['scheme'] .':' , $wgUrlProtocols ) ) { |
| 2188 | + $delimiter = ':'; |
| 2189 | + // parse_url detects for news: and mailto: the host part of an url as path |
| 2190 | + // We have to correct this wrong detection |
| 2191 | + if ( isset ( $bits['path'] ) ) { |
| 2192 | + $bits['host'] = $bits['path']; |
| 2193 | + $bits['path'] = ''; |
| 2194 | + } |
| 2195 | + } else { |
| 2196 | + return false; |
| 2197 | + } |
| 2198 | + |
| 2199 | + // Reverse the labels in the hostname, convert to lower case |
| 2200 | + // For emails reverse domainpart only |
| 2201 | + if ( $bits['scheme'] == 'mailto' ) { |
| 2202 | + $mailparts = explode( '@', $bits['host'], 2 ); |
| 2203 | + if ( count($mailparts) === 2 ) { |
| 2204 | + $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) ); |
| 2205 | + } else { |
| 2206 | + // No domain specified, don't mangle it |
| 2207 | + $domainpart = ''; |
| 2208 | + } |
| 2209 | + $reversedHost = $domainpart . '@' . $mailparts[0]; |
| 2210 | + } else { |
| 2211 | + $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) ); |
| 2212 | + } |
| 2213 | + // Add an extra dot to the end |
| 2214 | + // Why? Is it in wrong place in mailto links? |
| 2215 | + if ( substr( $reversedHost, -1, 1 ) !== '.' ) { |
| 2216 | + $reversedHost .= '.'; |
| 2217 | + } |
| 2218 | + // Reconstruct the pseudo-URL |
| 2219 | + $prot = $bits['scheme']; |
| 2220 | + $index = "$prot$delimiter$reversedHost"; |
| 2221 | + // Leave out user and password. Add the port, path, query and fragment |
| 2222 | + if ( isset( $bits['port'] ) ) $index .= ':' . $bits['port']; |
| 2223 | + if ( isset( $bits['path'] ) ) { |
| 2224 | + $index .= $bits['path']; |
| 2225 | + } else { |
| 2226 | + $index .= '/'; |
| 2227 | + } |
| 2228 | + if ( isset( $bits['query'] ) ) $index .= '?' . $bits['query']; |
| 2229 | + if ( isset( $bits['fragment'] ) ) $index .= '#' . $bits['fragment']; |
| 2230 | + return $index; |
| 2231 | +} |
| 2232 | + |
| 2233 | +/** |
| 2234 | + * Do any deferred updates and clear the list |
| 2235 | + * TODO: This could be in Wiki.php if that class made any sense at all |
| 2236 | + */ |
| 2237 | +function wfDoUpdates() |
| 2238 | +{ |
| 2239 | + global $wgPostCommitUpdateList, $wgDeferredUpdateList; |
| 2240 | + foreach ( $wgDeferredUpdateList as $update ) { |
| 2241 | + $update->doUpdate(); |
| 2242 | + } |
| 2243 | + foreach ( $wgPostCommitUpdateList as $update ) { |
| 2244 | + $update->doUpdate(); |
| 2245 | + } |
| 2246 | + $wgDeferredUpdateList = array(); |
| 2247 | + $wgPostCommitUpdateList = array(); |
| 2248 | +} |
| 2249 | + |
| 2250 | +/** |
| 2251 | + * @deprecated use StringUtils::explodeMarkup |
| 2252 | + */ |
| 2253 | +function wfExplodeMarkup( $separator, $text ) { |
| 2254 | + return StringUtils::explodeMarkup( $separator, $text ); |
| 2255 | +} |
| 2256 | + |
| 2257 | +/** |
| 2258 | + * Convert an arbitrarily-long digit string from one numeric base |
| 2259 | + * to another, optionally zero-padding to a minimum column width. |
| 2260 | + * |
| 2261 | + * Supports base 2 through 36; digit values 10-36 are represented |
| 2262 | + * as lowercase letters a-z. Input is case-insensitive. |
| 2263 | + * |
| 2264 | + * @param $input string of digits |
| 2265 | + * @param $sourceBase int 2-36 |
| 2266 | + * @param $destBase int 2-36 |
| 2267 | + * @param $pad int 1 or greater |
| 2268 | + * @param $lowercase bool |
| 2269 | + * @return string or false on invalid input |
| 2270 | + */ |
| 2271 | +function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true ) { |
| 2272 | + $input = strval( $input ); |
| 2273 | + if( $sourceBase < 2 || |
| 2274 | + $sourceBase > 36 || |
| 2275 | + $destBase < 2 || |
| 2276 | + $destBase > 36 || |
| 2277 | + $pad < 1 || |
| 2278 | + $sourceBase != intval( $sourceBase ) || |
| 2279 | + $destBase != intval( $destBase ) || |
| 2280 | + $pad != intval( $pad ) || |
| 2281 | + !is_string( $input ) || |
| 2282 | + $input == '' ) { |
| 2283 | + return false; |
| 2284 | + } |
| 2285 | + $digitChars = ( $lowercase ) ? '0123456789abcdefghijklmnopqrstuvwxyz' : '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
| 2286 | + $inDigits = array(); |
| 2287 | + $outChars = ''; |
| 2288 | + |
| 2289 | + // Decode and validate input string |
| 2290 | + $input = strtolower( $input ); |
| 2291 | + for( $i = 0; $i < strlen( $input ); $i++ ) { |
| 2292 | + $n = strpos( $digitChars, $input{$i} ); |
| 2293 | + if( $n === false || $n > $sourceBase ) { |
| 2294 | + return false; |
| 2295 | + } |
| 2296 | + $inDigits[] = $n; |
| 2297 | + } |
| 2298 | + |
| 2299 | + // Iterate over the input, modulo-ing out an output digit |
| 2300 | + // at a time until input is gone. |
| 2301 | + while( count( $inDigits ) ) { |
| 2302 | + $work = 0; |
| 2303 | + $workDigits = array(); |
| 2304 | + |
| 2305 | + // Long division... |
| 2306 | + foreach( $inDigits as $digit ) { |
| 2307 | + $work *= $sourceBase; |
| 2308 | + $work += $digit; |
| 2309 | + |
| 2310 | + if( $work < $destBase ) { |
| 2311 | + // Gonna need to pull another digit. |
| 2312 | + if( count( $workDigits ) ) { |
| 2313 | + // Avoid zero-padding; this lets us find |
| 2314 | + // the end of the input very easily when |
| 2315 | + // length drops to zero. |
| 2316 | + $workDigits[] = 0; |
| 2317 | + } |
| 2318 | + } else { |
| 2319 | + // Finally! Actual division! |
| 2320 | + $workDigits[] = intval( $work / $destBase ); |
| 2321 | + |
| 2322 | + // Isn't it annoying that most programming languages |
| 2323 | + // don't have a single divide-and-remainder operator, |
| 2324 | + // even though the CPU implements it that way? |
| 2325 | + $work = $work % $destBase; |
| 2326 | + } |
| 2327 | + } |
| 2328 | + |
| 2329 | + // All that division leaves us with a remainder, |
| 2330 | + // which is conveniently our next output digit. |
| 2331 | + $outChars .= $digitChars[$work]; |
| 2332 | + |
| 2333 | + // And we continue! |
| 2334 | + $inDigits = $workDigits; |
| 2335 | + } |
| 2336 | + |
| 2337 | + while( strlen( $outChars ) < $pad ) { |
| 2338 | + $outChars .= '0'; |
| 2339 | + } |
| 2340 | + |
| 2341 | + return strrev( $outChars ); |
| 2342 | +} |
| 2343 | + |
| 2344 | +/** |
| 2345 | + * Create an object with a given name and an array of construct parameters |
| 2346 | + * @param string $name |
| 2347 | + * @param array $p parameters |
| 2348 | + */ |
| 2349 | +function wfCreateObject( $name, $p ){ |
| 2350 | + $p = array_values( $p ); |
| 2351 | + switch ( count( $p ) ) { |
| 2352 | + case 0: |
| 2353 | + return new $name; |
| 2354 | + case 1: |
| 2355 | + return new $name( $p[0] ); |
| 2356 | + case 2: |
| 2357 | + return new $name( $p[0], $p[1] ); |
| 2358 | + case 3: |
| 2359 | + return new $name( $p[0], $p[1], $p[2] ); |
| 2360 | + case 4: |
| 2361 | + return new $name( $p[0], $p[1], $p[2], $p[3] ); |
| 2362 | + case 5: |
| 2363 | + return new $name( $p[0], $p[1], $p[2], $p[3], $p[4] ); |
| 2364 | + case 6: |
| 2365 | + return new $name( $p[0], $p[1], $p[2], $p[3], $p[4], $p[5] ); |
| 2366 | + default: |
| 2367 | + throw new MWException( "Too many arguments to construtor in wfCreateObject" ); |
| 2368 | + } |
| 2369 | +} |
| 2370 | + |
| 2371 | +/** |
| 2372 | + * Alias for modularized function |
| 2373 | + * @deprecated Use Http::get() instead |
| 2374 | + */ |
| 2375 | +function wfGetHTTP( $url, $timeout = 'default' ) { |
| 2376 | + wfDeprecated(__FUNCTION__); |
| 2377 | + return Http::get( $url, $timeout ); |
| 2378 | +} |
| 2379 | + |
| 2380 | +/** |
| 2381 | + * Alias for modularized function |
| 2382 | + * @deprecated Use Http::isLocalURL() instead |
| 2383 | + */ |
| 2384 | +function wfIsLocalURL( $url ) { |
| 2385 | + wfDeprecated(__FUNCTION__); |
| 2386 | + return Http::isLocalURL( $url ); |
| 2387 | +} |
| 2388 | + |
| 2389 | +function wfHttpOnlySafe() { |
| 2390 | + global $wgHttpOnlyBlacklist; |
| 2391 | + if( !version_compare("5.2", PHP_VERSION, "<") ) |
| 2392 | + return false; |
| 2393 | + |
| 2394 | + if( isset( $_SERVER['HTTP_USER_AGENT'] ) ) { |
| 2395 | + foreach( $wgHttpOnlyBlacklist as $regex ) { |
| 2396 | + if( preg_match( $regex, $_SERVER['HTTP_USER_AGENT'] ) ) { |
| 2397 | + return false; |
| 2398 | + } |
| 2399 | + } |
| 2400 | + } |
| 2401 | + |
| 2402 | + return true; |
| 2403 | +} |
| 2404 | + |
| 2405 | +/** |
| 2406 | + * Initialise php session |
| 2407 | + */ |
| 2408 | +function wfSetupSession() { |
| 2409 | + global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly; |
| 2410 | + if( $wgSessionsInMemcached ) { |
| 2411 | + require_once( 'MemcachedSessions.php' ); |
| 2412 | + } elseif( 'files' != ini_get( 'session.save_handler' ) ) { |
| 2413 | + # If it's left on 'user' or another setting from another |
| 2414 | + # application, it will end up failing. Try to recover. |
| 2415 | + ini_set ( 'session.save_handler', 'files' ); |
| 2416 | + } |
| 2417 | + $httpOnlySafe = wfHttpOnlySafe(); |
| 2418 | + wfDebugLog( 'cookie', |
| 2419 | + 'session_set_cookie_params: "' . implode( '", "', |
| 2420 | + array( |
| 2421 | + 0, |
| 2422 | + $wgCookiePath, |
| 2423 | + $wgCookieDomain, |
| 2424 | + $wgCookieSecure, |
| 2425 | + $httpOnlySafe && $wgCookieHttpOnly ) ) . '"' ); |
| 2426 | + if( $httpOnlySafe && $wgCookieHttpOnly ) { |
| 2427 | + session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly ); |
| 2428 | + } else { |
| 2429 | + // PHP 5.1 throws warnings if you pass the HttpOnly parameter for 5.2. |
| 2430 | + session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); |
| 2431 | + } |
| 2432 | + session_cache_limiter( 'private, must-revalidate' ); |
| 2433 | + wfSuppressWarnings(); |
| 2434 | + session_start(); |
| 2435 | + wfRestoreWarnings(); |
| 2436 | +} |
| 2437 | + |
| 2438 | +/** |
| 2439 | + * Get an object from the precompiled serialized directory |
| 2440 | + * |
| 2441 | + * @return mixed The variable on success, false on failure |
| 2442 | + */ |
| 2443 | +function wfGetPrecompiledData( $name ) { |
| 2444 | + global $IP; |
| 2445 | + |
| 2446 | + $file = "$IP/serialized/$name"; |
| 2447 | + if ( file_exists( $file ) ) { |
| 2448 | + $blob = file_get_contents( $file ); |
| 2449 | + if ( $blob ) { |
| 2450 | + return unserialize( $blob ); |
| 2451 | + } |
| 2452 | + } |
| 2453 | + return false; |
| 2454 | +} |
| 2455 | + |
| 2456 | +function wfGetCaller( $level = 2 ) { |
| 2457 | + $backtrace = wfDebugBacktrace(); |
| 2458 | + if ( isset( $backtrace[$level] ) ) { |
| 2459 | + return wfFormatStackFrame($backtrace[$level]); |
| 2460 | + } else { |
| 2461 | + $caller = 'unknown'; |
| 2462 | + } |
| 2463 | + return $caller; |
| 2464 | +} |
| 2465 | + |
| 2466 | +/** Return a string consisting all callers in stack, somewhat useful sometimes for profiling specific points */ |
| 2467 | +function wfGetAllCallers() { |
| 2468 | + return implode('/', array_map('wfFormatStackFrame',array_reverse(wfDebugBacktrace()))); |
| 2469 | +} |
| 2470 | + |
| 2471 | +/** Return a string representation of frame */ |
| 2472 | +function wfFormatStackFrame($frame) { |
| 2473 | + return isset( $frame["class"] )? |
| 2474 | + $frame["class"]."::".$frame["function"]: |
| 2475 | + $frame["function"]; |
| 2476 | +} |
| 2477 | + |
| 2478 | +/** |
| 2479 | + * Get a cache key |
| 2480 | + */ |
| 2481 | +function wfMemcKey( /*... */ ) { |
| 2482 | + $args = func_get_args(); |
| 2483 | + $key = wfWikiID() . ':' . implode( ':', $args ); |
| 2484 | + return $key; |
| 2485 | +} |
| 2486 | + |
| 2487 | +/** |
| 2488 | + * Get a cache key for a foreign DB |
| 2489 | + */ |
| 2490 | +function wfForeignMemcKey( $db, $prefix /*, ... */ ) { |
| 2491 | + $args = array_slice( func_get_args(), 2 ); |
| 2492 | + if ( $prefix ) { |
| 2493 | + $key = "$db-$prefix:" . implode( ':', $args ); |
| 2494 | + } else { |
| 2495 | + $key = $db . ':' . implode( ':', $args ); |
| 2496 | + } |
| 2497 | + return $key; |
| 2498 | +} |
| 2499 | + |
| 2500 | +/** |
| 2501 | + * Get an ASCII string identifying this wiki |
| 2502 | + * This is used as a prefix in memcached keys |
| 2503 | + */ |
| 2504 | +function wfWikiID( $db = null ) { |
| 2505 | + if( $db instanceof Database ) { |
| 2506 | + return $db->getWikiID(); |
| 2507 | + } else { |
| 2508 | + global $wgDBprefix, $wgDBname; |
| 2509 | + if ( $wgDBprefix ) { |
| 2510 | + return "$wgDBname-$wgDBprefix"; |
| 2511 | + } else { |
| 2512 | + return $wgDBname; |
| 2513 | + } |
| 2514 | + } |
| 2515 | +} |
| 2516 | + |
| 2517 | +/** |
| 2518 | + * Split a wiki ID into DB name and table prefix |
| 2519 | + */ |
| 2520 | +function wfSplitWikiID( $wiki ) { |
| 2521 | + $bits = explode( '-', $wiki, 2 ); |
| 2522 | + if ( count( $bits ) < 2 ) { |
| 2523 | + $bits[] = ''; |
| 2524 | + } |
| 2525 | + return $bits; |
| 2526 | +} |
| 2527 | + |
| 2528 | +/* |
| 2529 | + * Get a Database object. |
| 2530 | + * @param integer $db Index of the connection to get. May be DB_MASTER for the |
| 2531 | + * master (for write queries), DB_SLAVE for potentially lagged |
| 2532 | + * read queries, or an integer >= 0 for a particular server. |
| 2533 | + * |
| 2534 | + * @param mixed $groups Query groups. An array of group names that this query |
| 2535 | + * belongs to. May contain a single string if the query is only |
| 2536 | + * in one group. |
| 2537 | + * |
| 2538 | + * @param string $wiki The wiki ID, or false for the current wiki |
| 2539 | + * |
| 2540 | + * Note: multiple calls to wfGetDB(DB_SLAVE) during the course of one request |
| 2541 | + * will always return the same object, unless the underlying connection or load |
| 2542 | + * balancer is manually destroyed. |
| 2543 | + */ |
| 2544 | +function &wfGetDB( $db = DB_LAST, $groups = array(), $wiki = false ) { |
| 2545 | + return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki ); |
| 2546 | +} |
| 2547 | + |
| 2548 | +/** |
| 2549 | + * Get a load balancer object. |
| 2550 | + * |
| 2551 | + * @param array $groups List of query groups |
| 2552 | + * @param string $wiki Wiki ID, or false for the current wiki |
| 2553 | + * @return LoadBalancer |
| 2554 | + */ |
| 2555 | +function wfGetLB( $wiki = false ) { |
| 2556 | + return wfGetLBFactory()->getMainLB( $wiki ); |
| 2557 | +} |
| 2558 | + |
| 2559 | +/** |
| 2560 | + * Get the load balancer factory object |
| 2561 | + */ |
| 2562 | +function &wfGetLBFactory() { |
| 2563 | + return LBFactory::singleton(); |
| 2564 | +} |
| 2565 | + |
| 2566 | +/** |
| 2567 | + * Find a file. |
| 2568 | + * Shortcut for RepoGroup::singleton()->findFile() |
| 2569 | + * @param mixed $title Title object or string. May be interwiki. |
| 2570 | + * @param mixed $time Requested time for an archived image, or false for the |
| 2571 | + * current version. An image object will be returned which |
| 2572 | + * was created at the specified time. |
| 2573 | + * @param mixed $flags FileRepo::FIND_ flags |
| 2574 | + * @return File, or false if the file does not exist |
| 2575 | + */ |
| 2576 | +function wfFindFile( $title, $time = false, $flags = 0 ) { |
| 2577 | + return RepoGroup::singleton()->findFile( $title, $time, $flags ); |
| 2578 | +} |
| 2579 | + |
| 2580 | +/** |
| 2581 | + * Get an object referring to a locally registered file. |
| 2582 | + * Returns a valid placeholder object if the file does not exist. |
| 2583 | + */ |
| 2584 | +function wfLocalFile( $title ) { |
| 2585 | + return RepoGroup::singleton()->getLocalRepo()->newFile( $title ); |
| 2586 | +} |
| 2587 | + |
| 2588 | +/** |
| 2589 | + * Should low-performance queries be disabled? |
| 2590 | + * |
| 2591 | + * @return bool |
| 2592 | + */ |
| 2593 | +function wfQueriesMustScale() { |
| 2594 | + global $wgMiserMode; |
| 2595 | + return $wgMiserMode |
| 2596 | + || ( SiteStats::pages() > 100000 |
| 2597 | + && SiteStats::edits() > 1000000 |
| 2598 | + && SiteStats::users() > 10000 ); |
| 2599 | +} |
| 2600 | + |
| 2601 | +/** |
| 2602 | + * Get the path to a specified script file, respecting file |
| 2603 | + * extensions; this is a wrapper around $wgScriptExtension etc. |
| 2604 | + * |
| 2605 | + * @param string $script Script filename, sans extension |
| 2606 | + * @return string |
| 2607 | + */ |
| 2608 | +function wfScript( $script = 'index' ) { |
| 2609 | + global $wgScriptPath, $wgScriptExtension; |
| 2610 | + return "{$wgScriptPath}/{$script}{$wgScriptExtension}"; |
| 2611 | +} |
| 2612 | + |
| 2613 | +/** |
| 2614 | + * Convenience function converts boolean values into "true" |
| 2615 | + * or "false" (string) values |
| 2616 | + * |
| 2617 | + * @param bool $value |
| 2618 | + * @return string |
| 2619 | + */ |
| 2620 | +function wfBoolToStr( $value ) { |
| 2621 | + return $value ? 'true' : 'false'; |
| 2622 | +} |
| 2623 | + |
| 2624 | +/** |
| 2625 | + * Load an extension messages file |
| 2626 | + * |
| 2627 | + * @param string $extensionName Name of extension to load messages from\for. |
| 2628 | + * @param string $langcode Language to load messages for, or false for default |
| 2629 | + * behvaiour (en, content language and user language). |
| 2630 | + */ |
| 2631 | +function wfLoadExtensionMessages( $extensionName, $langcode = false ) { |
| 2632 | + global $wgExtensionMessagesFiles, $wgMessageCache, $wgLang, $wgContLang; |
| 2633 | + |
| 2634 | + #For recording whether extension message files have been loaded in a given language. |
| 2635 | + static $loaded = array(); |
| 2636 | + |
| 2637 | + if( !array_key_exists( $extensionName, $loaded ) ) { |
| 2638 | + $loaded[$extensionName] = array(); |
| 2639 | + } |
| 2640 | + |
| 2641 | + if ( !isset($wgExtensionMessagesFiles[$extensionName]) ) { |
| 2642 | + throw new MWException( "Messages file for extensions $extensionName is not defined" ); |
| 2643 | + } |
| 2644 | + |
| 2645 | + if( !$langcode && !array_key_exists( '*', $loaded[$extensionName] ) ) { |
| 2646 | + # Just do en, content language and user language. |
| 2647 | + $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName], false ); |
| 2648 | + # Mark that they have been loaded. |
| 2649 | + $loaded[$extensionName]['en'] = true; |
| 2650 | + $loaded[$extensionName][$wgLang->getCode()] = true; |
| 2651 | + $loaded[$extensionName][$wgContLang->getCode()] = true; |
| 2652 | + # Mark that this part has been done to avoid weird if statements. |
| 2653 | + $loaded[$extensionName]['*'] = true; |
| 2654 | + } elseif( is_string( $langcode ) && !array_key_exists( $langcode, $loaded[$extensionName] ) ) { |
| 2655 | + # Load messages for specified language. |
| 2656 | + $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName], $langcode ); |
| 2657 | + # Mark that they have been loaded. |
| 2658 | + $loaded[$extensionName][$langcode] = true; |
| 2659 | + } |
| 2660 | +} |
| 2661 | + |
| 2662 | +/** |
| 2663 | + * Get a platform-independent path to the null file, e.g. |
| 2664 | + * /dev/null |
| 2665 | + * |
| 2666 | + * @return string |
| 2667 | + */ |
| 2668 | +function wfGetNull() { |
| 2669 | + return wfIsWindows() |
| 2670 | + ? 'NUL' |
| 2671 | + : '/dev/null'; |
| 2672 | +} |
| 2673 | + |
| 2674 | +/** |
| 2675 | + * Displays a maxlag error |
| 2676 | + * |
| 2677 | + * @param string $host Server that lags the most |
| 2678 | + * @param int $lag Maxlag (actual) |
| 2679 | + * @param int $maxLag Maxlag (requested) |
| 2680 | + */ |
| 2681 | +function wfMaxlagError( $host, $lag, $maxLag ) { |
| 2682 | + global $wgShowHostnames; |
| 2683 | + header( 'HTTP/1.1 503 Service Unavailable' ); |
| 2684 | + header( 'Retry-After: ' . max( intval( $maxLag ), 5 ) ); |
| 2685 | + header( 'X-Database-Lag: ' . intval( $lag ) ); |
| 2686 | + header( 'Content-Type: text/plain' ); |
| 2687 | + if( $wgShowHostnames ) { |
| 2688 | + echo "Waiting for $host: $lag seconds lagged\n"; |
| 2689 | + } else { |
| 2690 | + echo "Waiting for a database server: $lag seconds lagged\n"; |
| 2691 | + } |
| 2692 | +} |
| 2693 | + |
| 2694 | +/** |
| 2695 | + * Throws an E_USER_NOTICE saying that $function is deprecated |
| 2696 | + * @param string $function |
| 2697 | + * @return null |
| 2698 | + */ |
| 2699 | +function wfDeprecated( $function ) { |
| 2700 | + global $wgDebugLogFile; |
| 2701 | + if ( !$wgDebugLogFile ) { |
| 2702 | + return; |
| 2703 | + } |
| 2704 | + $callers = wfDebugBacktrace(); |
| 2705 | + if( isset( $callers[2] ) ){ |
| 2706 | + $callerfunc = $callers[2]; |
| 2707 | + $callerfile = $callers[1]; |
| 2708 | + if( isset( $callerfile['file'] ) && isset( $callerfile['line'] ) ){ |
| 2709 | + $file = $callerfile['file'] . ' at line ' . $callerfile['line']; |
| 2710 | + } else { |
| 2711 | + $file = '(internal function)'; |
| 2712 | + } |
| 2713 | + $func = ''; |
| 2714 | + if( isset( $callerfunc['class'] ) ) |
| 2715 | + $func .= $callerfunc['class'] . '::'; |
| 2716 | + $func .= @$callerfunc['function']; |
| 2717 | + $msg = "Use of $function is deprecated. Called from $func in $file"; |
| 2718 | + } else { |
| 2719 | + $msg = "Use of $function is deprecated."; |
| 2720 | + } |
| 2721 | + wfDebug( "$msg\n" ); |
| 2722 | +} |
| 2723 | + |
| 2724 | +/** |
| 2725 | + * Sleep until the worst slave's replication lag is less than or equal to |
| 2726 | + * $maxLag, in seconds. Use this when updating very large numbers of rows, as |
| 2727 | + * in maintenance scripts, to avoid causing too much lag. Of course, this is |
| 2728 | + * a no-op if there are no slaves. |
| 2729 | + * |
| 2730 | + * Every time the function has to wait for a slave, it will print a message to |
| 2731 | + * that effect (and then sleep for a little while), so it's probably not best |
| 2732 | + * to use this outside maintenance scripts in its present form. |
| 2733 | + * |
| 2734 | + * @param int $maxLag |
| 2735 | + * @return null |
| 2736 | + */ |
| 2737 | +function wfWaitForSlaves( $maxLag ) { |
| 2738 | + if( $maxLag ) { |
| 2739 | + $lb = wfGetLB(); |
| 2740 | + list( $host, $lag ) = $lb->getMaxLag(); |
| 2741 | + while( $lag > $maxLag ) { |
| 2742 | + $name = @gethostbyaddr( $host ); |
| 2743 | + if( $name !== false ) { |
| 2744 | + $host = $name; |
| 2745 | + } |
| 2746 | + print "Waiting for $host (lagged $lag seconds)...\n"; |
| 2747 | + sleep($maxLag); |
| 2748 | + list( $host, $lag ) = $lb->getMaxLag(); |
| 2749 | + } |
| 2750 | + } |
| 2751 | +} |
| 2752 | + |
| 2753 | +/** Generate a random 32-character hexadecimal token. |
| 2754 | + * @param mixed $salt Some sort of salt, if necessary, to add to random characters before hashing. |
| 2755 | + */ |
| 2756 | +function wfGenerateToken( $salt = '' ) { |
| 2757 | + $salt = serialize($salt); |
| 2758 | + |
| 2759 | + return md5( mt_rand( 0, 0x7fffffff ) . $salt ); |
| 2760 | +} |
| 2761 | + |
| 2762 | +/** |
| 2763 | + * Replace all invalid characters with - |
| 2764 | + * @param mixed $title Filename to process |
| 2765 | + */ |
| 2766 | +function wfStripIllegalFilenameChars( $name ) { |
| 2767 | + $name = wfBaseName( $name ); |
| 2768 | + $name = preg_replace ( "/[^".Title::legalChars()."]/", '-', $name ); |
| 2769 | + return $name; |
| 2770 | +} |
Property changes on: trunk/extensions/NSFileRepo/REL1_13_0/phase3/includes/GlobalFunctions.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 2771 | + native |
Index: trunk/extensions/NSFileRepo/REL1_13_0/phase3/includes/GlobalFunctions.patch |
— | — | @@ -0,0 +1,12 @@ |
| 2 | +Index: GlobalFunctions.php
|
| 3 | +===================================================================
|
| 4 | +--- GlobalFunctions.php (revision 53111)
|
| 5 | +@@ -2764,6 +2764,6 @@
|
| 6 | + */ |
| 7 | + function wfStripIllegalFilenameChars( $name ) { |
| 8 | + $name = wfBaseName( $name ); |
| 9 | +- $name = preg_replace ( "/[^".Title::legalChars()."]|:/", '-', $name ); |
| 10 | ++ $name = preg_replace ( "/[^".Title::legalChars()."]/", '-', $name ); |
| 11 | + return $name; |
| 12 | + } |
Index: trunk/extensions/NSFileRepo/REL1_14_0/phase3/img_auth.i18n.php |
— | — | @@ -0,0 +1,32 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Internationalisation file for img_auth script (see see http://www.mediawiki.org/wiki/Manual:Image_Authorization). |
| 5 | +*/ |
| 6 | + |
| 7 | +$messages = array(); |
| 8 | + |
| 9 | +/** English |
| 10 | + * @author Jack D. Pond |
| 11 | + */ |
| 12 | +$messages['en'] = array( |
| 13 | + 'image_auth-desc' => 'Image authorisation script', |
| 14 | + 'image_auth-nopathinfo' => "Missing PATH_INFO. Your server is not set up to pass this information - |
| 15 | +may be CGI-based and can't support img_auth. See `Image Authorization` on MediaWiki.", |
| 16 | + 'image_auth-notindir' => "Requested path not in upload directory.", |
| 17 | + 'image_auth-badtitle' => "Unable to construct a valid Title from `$1`.", |
| 18 | + 'image_auth-nologinnWL' => "Not logged in and `$1` not in whitelist.", |
| 19 | + 'image_auth-nofile' => "`$1` does not exist.", |
| 20 | + 'image_auth-isdir' => "`$1` is a directory.", |
| 21 | + 'image_auth-streaming' => "Streaming `$1`.", |
| 22 | + 'image_auth-public' => "The function of img_auth.php is to output files from a private wiki. This wiki |
| 23 | +is configured as a public wiki. For optimal security, img_auth.php is disabled for this case.", |
| 24 | + 'image_auth-noread' => "User does not have access to read `$1`." |
| 25 | +); |
| 26 | + |
| 27 | +/** Message documentation (Message documentation) |
| 28 | + * @author Jack D. Pond |
| 29 | + */ |
| 30 | +$messages['qqq'] = array( |
| 31 | + 'image_auth-desc' => 'Image authorisation script' |
| 32 | +); |
| 33 | + |
Property changes on: trunk/extensions/NSFileRepo/REL1_14_0/phase3/img_auth.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 34 | + native |
Index: trunk/extensions/NSFileRepo/REL1_14_0/phase3/img_auth.php |
— | — | @@ -0,0 +1,136 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Image authorisation script |
| 6 | + * |
| 7 | + * To use this, see http://www.mediawiki.org/wiki/Manual:Image_Authorization |
| 8 | + * |
| 9 | + * - Set $wgUploadDirectory to a non-public directory (not web accessible) |
| 10 | + * - Set $wgUploadPath to point to this file |
| 11 | + * |
| 12 | + * Your server needs to support PATH_INFO; CGI-based configurations usually don't. |
| 13 | + * |
| 14 | + * @file |
| 15 | + */ |
| 16 | + |
| 17 | + |
| 18 | +/** |
| 19 | + For security reasons, you usually don't want your user to know access was denied, just that it was. |
| 20 | + If you want to change this, you can set $wgImgAuthDetails to 'true' in localsettings.php and it will give the user the reason |
| 21 | + why access was denied. |
| 22 | +**/ |
| 23 | + |
| 24 | +global $wgImgAuthDetails; |
| 25 | +$wgImgAuthDetails = false; |
| 26 | + |
| 27 | +define( 'MW_NO_OUTPUT_COMPRESSION', 1 ); |
| 28 | +require_once( dirname( __FILE__ ) . '/includes/WebStart.php' ); |
| 29 | +wfProfileIn( 'img_auth.php' ); |
| 30 | +require_once( dirname( __FILE__ ) . '/includes/StreamFile.php' ); |
| 31 | + |
| 32 | +global $wgMessageCache, $messages; |
| 33 | +require_once( dirname( __FILE__ ) . '/img_auth.i18n.php' ); |
| 34 | +foreach( $messages as $lang => $LangMsg ) |
| 35 | + $wgMessageCache->addMessages( $LangMsg, $lang ); |
| 36 | + |
| 37 | +$perms = User::getGroupPermissions( array( '*' ) ); |
| 38 | + |
| 39 | +// See if this is a public Wiki (no protections) |
| 40 | +if ( in_array( 'read', $perms, true ) ) |
| 41 | + wfPublicError(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-public')); |
| 42 | + |
| 43 | +// Extract path and image information |
| 44 | +if( !isset( $_SERVER['PATH_INFO'] ) ) |
| 45 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-nopathinfo')); |
| 46 | + |
| 47 | +$path = $_SERVER['PATH_INFO']; |
| 48 | +$filename = realpath( $wgUploadDirectory . $_SERVER['PATH_INFO'] ); |
| 49 | +$realUpload = realpath( $wgUploadDirectory ); |
| 50 | + |
| 51 | +// Basic directory traversal check |
| 52 | +if( substr( $filename, 0, strlen( $realUpload ) ) != $realUpload ) |
| 53 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-notindir')); |
| 54 | + |
| 55 | +// Extract the file name and chop off the size specifier |
| 56 | +// (e.g. 120px-Foo.png => Foo.png) |
| 57 | +$name = wfBaseName( $path ); |
| 58 | +if( preg_match( '!\d+px-(.*)!i', $name, $m ) ) |
| 59 | + $name = $m[1]; |
| 60 | + |
| 61 | +// Check to see if the file exists |
| 62 | +if( !file_exists( $filename ) ) |
| 63 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-nofile',$filename)); |
| 64 | + |
| 65 | +// Check to see if tried to access a directory |
| 66 | +if( is_dir( $filename ) ) |
| 67 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-isdir',$filename)); |
| 68 | + |
| 69 | + |
| 70 | +$title = Title::makeTitleSafe( NS_FILE, $name ); |
| 71 | + |
| 72 | +// See if could create the title object |
| 73 | +if( !$title instanceof Title ) |
| 74 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-badtitle',$name)); |
| 75 | + |
| 76 | +// Run hook |
| 77 | +if (!wfRunHooks( 'ImgAuthBeforeStream', array( &$title, &$path, &$name, &$result ) ) ) |
| 78 | + wfForbidden($result[0],$result[1]); |
| 79 | + |
| 80 | +// Check the whitelist if needed, deprecated since usercan added |
| 81 | +// $pTitle = $title->getPrefixedText(); |
| 82 | +// if( !$wgUser->getId() && ( !is_array( $wgWhitelistRead ) || !in_array( $pTitle, $wgWhitelistRead ) ) ) |
| 83 | +// wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-nologinnWL',$pTitle)); |
| 84 | + |
| 85 | + |
| 86 | +// Check user authorization for this title |
| 87 | +if( !$title->userCanRead() ) |
| 88 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-noread',$name)); |
| 89 | + |
| 90 | + |
| 91 | +// Stream the requested file |
| 92 | +wfDebugLog( 'img_auth', "Streaming `{$filename}`" ); |
| 93 | +wfStreamFile( $filename, array( 'Cache-Control: private', 'Vary: Cookie' ) ); |
| 94 | +wfLogProfilingData(); |
| 95 | + |
| 96 | +/** |
| 97 | + * Issue a standard HTTP 403 Forbidden header ($msg1) and an |
| 98 | + * error message ($msg2), then end the script |
| 99 | + */ |
| 100 | +function wfForbidden($msg1,$msg2) { |
| 101 | + global $wgImgAuthDetails; |
| 102 | + $detailMsg = $wgImgAuthDetails ? $msg2 : wfMsgHTML('badaccess-group0'); |
| 103 | + wfDebugLog( 'img_auth', "wfForbidden Msg: ".$msg2 ); |
| 104 | + header( 'HTTP/1.0 403 Forbidden' ); |
| 105 | + header( 'Vary: Cookie' ); |
| 106 | + header( 'Content-Type: text/html; charset=utf-8' ); |
| 107 | + echo <<<ENDS |
| 108 | +<html> |
| 109 | +<body> |
| 110 | +<h1>$msg1</h1> |
| 111 | +<p>$detailMsg</p> |
| 112 | +</body> |
| 113 | +</html> |
| 114 | +ENDS; |
| 115 | + wfLogProfilingData(); |
| 116 | + exit(); |
| 117 | +} |
| 118 | + |
| 119 | +/** |
| 120 | + * Show a 403 error for use when the wiki is public |
| 121 | + */ |
| 122 | +function wfPublicError($msg1,$msg2) { |
| 123 | + header( 'HTTP/1.0 403 Forbidden' ); |
| 124 | + header( 'Content-Type: text/html; charset=utf-8' ); |
| 125 | + wfDebugLog( 'img_auth', "wfPublicError Msg: ".$msg2 ); |
| 126 | + echo <<<ENDS |
| 127 | +<html> |
| 128 | +<body> |
| 129 | +<h1>$msg1</h1> |
| 130 | +<p>$msg2</p> |
| 131 | +</body> |
| 132 | +</html> |
| 133 | +ENDS; |
| 134 | + wfLogProfilingData(); |
| 135 | + exit; |
| 136 | +} |
| 137 | + |
Property changes on: trunk/extensions/NSFileRepo/REL1_14_0/phase3/img_auth.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 138 | + native |
Index: trunk/extensions/NSFileRepo/REL1_14_0/phase3/includes/GlobalFunctions.php |
— | — | @@ -0,0 +1,2939 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 5 | + die( "This file is part of MediaWiki, it is not a valid entry point" ); |
| 6 | +} |
| 7 | + |
| 8 | +/** |
| 9 | + * Global functions used everywhere |
| 10 | + */ |
| 11 | + |
| 12 | +require_once dirname(__FILE__) . '/normal/UtfNormalUtil.php'; |
| 13 | +require_once dirname(__FILE__) . '/XmlFunctions.php'; |
| 14 | + |
| 15 | +// Hide compatibility functions from Doxygen |
| 16 | +/// @cond |
| 17 | + |
| 18 | +/** |
| 19 | + * Compatibility functions |
| 20 | + * |
| 21 | + * We more or less support PHP 5.0.x and up. |
| 22 | + * Re-implementations of newer functions or functions in non-standard |
| 23 | + * PHP extensions may be included here. |
| 24 | + */ |
| 25 | +if( !function_exists('iconv') ) { |
| 26 | + # iconv support is not in the default configuration and so may not be present. |
| 27 | + # Assume will only ever use utf-8 and iso-8859-1. |
| 28 | + # This will *not* work in all circumstances. |
| 29 | + function iconv( $from, $to, $string ) { |
| 30 | + if(strcasecmp( $from, $to ) == 0) return $string; |
| 31 | + if(strcasecmp( $from, 'utf-8' ) == 0) return utf8_decode( $string ); |
| 32 | + if(strcasecmp( $to, 'utf-8' ) == 0) return utf8_encode( $string ); |
| 33 | + return $string; |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +# UTF-8 substr function based on a PHP manual comment |
| 38 | +if ( !function_exists( 'mb_substr' ) ) { |
| 39 | + function mb_substr( $str, $start ) { |
| 40 | + $ar = array(); |
| 41 | + preg_match_all( '/./us', $str, $ar ); |
| 42 | + |
| 43 | + if( func_num_args() >= 3 ) { |
| 44 | + $end = func_get_arg( 2 ); |
| 45 | + return join( '', array_slice( $ar[0], $start, $end ) ); |
| 46 | + } else { |
| 47 | + return join( '', array_slice( $ar[0], $start ) ); |
| 48 | + } |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +if ( !function_exists( 'mb_strlen' ) ) { |
| 53 | + /** |
| 54 | + * Fallback implementation of mb_strlen, hardcoded to UTF-8. |
| 55 | + * @param string $str |
| 56 | + * @param string $enc optional encoding; ignored |
| 57 | + * @return int |
| 58 | + */ |
| 59 | + function mb_strlen( $str, $enc="" ) { |
| 60 | + $counts = count_chars( $str ); |
| 61 | + $total = 0; |
| 62 | + |
| 63 | + // Count ASCII bytes |
| 64 | + for( $i = 0; $i < 0x80; $i++ ) { |
| 65 | + $total += $counts[$i]; |
| 66 | + } |
| 67 | + |
| 68 | + // Count multibyte sequence heads |
| 69 | + for( $i = 0xc0; $i < 0xff; $i++ ) { |
| 70 | + $total += $counts[$i]; |
| 71 | + } |
| 72 | + return $total; |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +if ( !function_exists( 'array_diff_key' ) ) { |
| 77 | + /** |
| 78 | + * Exists in PHP 5.1.0+ |
| 79 | + * Not quite compatible, two-argument version only |
| 80 | + * Null values will cause problems due to this use of isset() |
| 81 | + */ |
| 82 | + function array_diff_key( $left, $right ) { |
| 83 | + $result = $left; |
| 84 | + foreach ( $left as $key => $unused ) { |
| 85 | + if ( isset( $right[$key] ) ) { |
| 86 | + unset( $result[$key] ); |
| 87 | + } |
| 88 | + } |
| 89 | + return $result; |
| 90 | + } |
| 91 | +} |
| 92 | + |
| 93 | +/// @endcond |
| 94 | + |
| 95 | + |
| 96 | +/** |
| 97 | + * Like array_diff( $a, $b ) except that it works with two-dimensional arrays. |
| 98 | + */ |
| 99 | +function wfArrayDiff2( $a, $b ) { |
| 100 | + return array_udiff( $a, $b, 'wfArrayDiff2_cmp' ); |
| 101 | +} |
| 102 | +function wfArrayDiff2_cmp( $a, $b ) { |
| 103 | + if ( !is_array( $a ) ) { |
| 104 | + return strcmp( $a, $b ); |
| 105 | + } elseif ( count( $a ) !== count( $b ) ) { |
| 106 | + return count( $a ) < count( $b ) ? -1 : 1; |
| 107 | + } else { |
| 108 | + reset( $a ); |
| 109 | + reset( $b ); |
| 110 | + while( ( list( $keyA, $valueA ) = each( $a ) ) && ( list( $keyB, $valueB ) = each( $b ) ) ) { |
| 111 | + $cmp = strcmp( $valueA, $valueB ); |
| 112 | + if ( $cmp !== 0 ) { |
| 113 | + return $cmp; |
| 114 | + } |
| 115 | + } |
| 116 | + return 0; |
| 117 | + } |
| 118 | +} |
| 119 | + |
| 120 | +/** |
| 121 | + * Wrapper for clone(), for compatibility with PHP4-friendly extensions. |
| 122 | + * PHP 5 won't let you declare a 'clone' function, even conditionally, |
| 123 | + * so it has to be a wrapper with a different name. |
| 124 | + */ |
| 125 | +function wfClone( $object ) { |
| 126 | + return clone( $object ); |
| 127 | +} |
| 128 | + |
| 129 | +/** |
| 130 | + * Seed Mersenne Twister |
| 131 | + * No-op for compatibility; only necessary in PHP < 4.2.0 |
| 132 | + */ |
| 133 | +function wfSeedRandom() { |
| 134 | + /* No-op */ |
| 135 | +} |
| 136 | + |
| 137 | +/** |
| 138 | + * Get a random decimal value between 0 and 1, in a way |
| 139 | + * not likely to give duplicate values for any realistic |
| 140 | + * number of articles. |
| 141 | + * |
| 142 | + * @return string |
| 143 | + */ |
| 144 | +function wfRandom() { |
| 145 | + # The maximum random value is "only" 2^31-1, so get two random |
| 146 | + # values to reduce the chance of dupes |
| 147 | + $max = mt_getrandmax() + 1; |
| 148 | + $rand = number_format( (mt_rand() * $max + mt_rand()) |
| 149 | + / $max / $max, 12, '.', '' ); |
| 150 | + return $rand; |
| 151 | +} |
| 152 | + |
| 153 | +/** |
| 154 | + * We want some things to be included as literal characters in our title URLs |
| 155 | + * for prettiness, which urlencode encodes by default. According to RFC 1738, |
| 156 | + * all of the following should be safe: |
| 157 | + * |
| 158 | + * ;:@&=$-_.+!*'(), |
| 159 | + * |
| 160 | + * But + is not safe because it's used to indicate a space; &= are only safe in |
| 161 | + * paths and not in queries (and we don't distinguish here); ' seems kind of |
| 162 | + * scary; and urlencode() doesn't touch -_. to begin with. Plus, although / |
| 163 | + * is reserved, we don't care. So the list we unescape is: |
| 164 | + * |
| 165 | + * ;:@$!*(),/ |
| 166 | + * |
| 167 | + * %2F in the page titles seems to fatally break for some reason. |
| 168 | + * |
| 169 | + * @param $s String: |
| 170 | + * @return string |
| 171 | +*/ |
| 172 | +function wfUrlencode( $s ) { |
| 173 | + $s = urlencode( $s ); |
| 174 | + $s = str_ireplace( |
| 175 | + array( '%3B','%3A','%40','%24','%21','%2A','%28','%29','%2C','%2F' ), |
| 176 | + array( ';', ':', '@', '$', '!', '*', '(', ')', ',', '/' ), |
| 177 | + $s |
| 178 | + ); |
| 179 | + |
| 180 | + return $s; |
| 181 | +} |
| 182 | + |
| 183 | +/** |
| 184 | + * Sends a line to the debug log if enabled or, optionally, to a comment in output. |
| 185 | + * In normal operation this is a NOP. |
| 186 | + * |
| 187 | + * Controlling globals: |
| 188 | + * $wgDebugLogFile - points to the log file |
| 189 | + * $wgProfileOnly - if set, normal debug messages will not be recorded. |
| 190 | + * $wgDebugRawPage - if false, 'action=raw' hits will not result in debug output. |
| 191 | + * $wgDebugComments - if on, some debug items may appear in comments in the HTML output. |
| 192 | + * |
| 193 | + * @param $text String |
| 194 | + * @param $logonly Bool: set true to avoid appearing in HTML when $wgDebugComments is set |
| 195 | + */ |
| 196 | +function wfDebug( $text, $logonly = false ) { |
| 197 | + global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage; |
| 198 | + global $wgDebugLogPrefix; |
| 199 | + static $recursion = 0; |
| 200 | + |
| 201 | + static $cache = array(); // Cache of unoutputted messages |
| 202 | + |
| 203 | + # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet |
| 204 | + if ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' && !$wgDebugRawPage ) { |
| 205 | + return; |
| 206 | + } |
| 207 | + |
| 208 | + if ( $wgDebugComments && !$logonly ) { |
| 209 | + $cache[] = $text; |
| 210 | + |
| 211 | + if ( !isset( $wgOut ) ) { |
| 212 | + return; |
| 213 | + } |
| 214 | + if ( !StubObject::isRealObject( $wgOut ) ) { |
| 215 | + if ( $recursion ) { |
| 216 | + return; |
| 217 | + } |
| 218 | + $recursion++; |
| 219 | + $wgOut->_unstub(); |
| 220 | + $recursion--; |
| 221 | + } |
| 222 | + |
| 223 | + // add the message and possible cached ones to the output |
| 224 | + array_map( array( $wgOut, 'debug' ), $cache ); |
| 225 | + $cache = array(); |
| 226 | + } |
| 227 | + if ( '' != $wgDebugLogFile && !$wgProfileOnly ) { |
| 228 | + # Strip unprintables; they can switch terminal modes when binary data |
| 229 | + # gets dumped, which is pretty annoying. |
| 230 | + $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text ); |
| 231 | + $text = $wgDebugLogPrefix . $text; |
| 232 | + wfErrorLog( $text, $wgDebugLogFile ); |
| 233 | + } |
| 234 | +} |
| 235 | + |
| 236 | +/** |
| 237 | + * Send a line giving PHP memory usage. |
| 238 | + * @param $exact Bool : print exact values instead of kilobytes (default: false) |
| 239 | + */ |
| 240 | +function wfDebugMem( $exact = false ) { |
| 241 | + $mem = memory_get_usage(); |
| 242 | + if( !$exact ) { |
| 243 | + $mem = floor( $mem / 1024 ) . ' kilobytes'; |
| 244 | + } else { |
| 245 | + $mem .= ' bytes'; |
| 246 | + } |
| 247 | + wfDebug( "Memory usage: $mem\n" ); |
| 248 | +} |
| 249 | + |
| 250 | +/** |
| 251 | + * Send a line to a supplementary debug log file, if configured, or main debug log if not. |
| 252 | + * $wgDebugLogGroups[$logGroup] should be set to a filename to send to a separate log. |
| 253 | + * |
| 254 | + * @param $logGroup String |
| 255 | + * @param $text String |
| 256 | + * @param $public Bool: whether to log the event in the public log if no private |
| 257 | + * log file is specified, (default true) |
| 258 | + */ |
| 259 | +function wfDebugLog( $logGroup, $text, $public = true ) { |
| 260 | + global $wgDebugLogGroups, $wgShowHostnames; |
| 261 | + $text = trim($text)."\n"; |
| 262 | + if( isset( $wgDebugLogGroups[$logGroup] ) ) { |
| 263 | + $time = wfTimestamp( TS_DB ); |
| 264 | + $wiki = wfWikiID(); |
| 265 | + if ( $wgShowHostnames ) { |
| 266 | + $host = wfHostname(); |
| 267 | + } else { |
| 268 | + $host = ''; |
| 269 | + } |
| 270 | + wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] ); |
| 271 | + } else if ( $public === true ) { |
| 272 | + wfDebug( $text, true ); |
| 273 | + } |
| 274 | +} |
| 275 | + |
| 276 | +/** |
| 277 | + * Log for database errors |
| 278 | + * @param $text String: database error message. |
| 279 | + */ |
| 280 | +function wfLogDBError( $text ) { |
| 281 | + global $wgDBerrorLog, $wgDBname; |
| 282 | + if ( $wgDBerrorLog ) { |
| 283 | + $host = trim(`hostname`); |
| 284 | + $text = date('D M j G:i:s T Y') . "\t$host\t$wgDBname\t$text"; |
| 285 | + wfErrorLog( $text, $wgDBerrorLog ); |
| 286 | + } |
| 287 | +} |
| 288 | + |
| 289 | +/** |
| 290 | + * Log to a file without getting "file size exceeded" signals. |
| 291 | + * |
| 292 | + * Can also log to TCP or UDP with the syntax udp://host:port/prefix. This will |
| 293 | + * send lines to the specified port, prefixed by the specified prefix and a space. |
| 294 | + */ |
| 295 | +function wfErrorLog( $text, $file ) { |
| 296 | + if ( substr( $file, 0, 4 ) == 'udp:' ) { |
| 297 | + if ( preg_match( '!^(tcp|udp):(?://)?\[([0-9a-fA-F:]+)\]:(\d+)(?:/(.*))?$!', $file, $m ) ) { |
| 298 | + // IPv6 bracketed host |
| 299 | + $protocol = $m[1]; |
| 300 | + $host = $m[2]; |
| 301 | + $port = $m[3]; |
| 302 | + $prefix = isset( $m[4] ) ? $m[4] : false; |
| 303 | + } elseif ( preg_match( '!^(tcp|udp):(?://)?([a-zA-Z0-9.-]+):(\d+)(?:/(.*))?$!', $file, $m ) ) { |
| 304 | + $protocol = $m[1]; |
| 305 | + $host = $m[2]; |
| 306 | + $port = $m[3]; |
| 307 | + $prefix = isset( $m[4] ) ? $m[4] : false; |
| 308 | + } else { |
| 309 | + throw new MWException( __METHOD__.": Invalid UDP specification" ); |
| 310 | + } |
| 311 | + // Clean it up for the multiplexer |
| 312 | + if ( strval( $prefix ) !== '' ) { |
| 313 | + $text = preg_replace( '/^/m', $prefix . ' ', $text ); |
| 314 | + if ( substr( $text, -1 ) != "\n" ) { |
| 315 | + $text .= "\n"; |
| 316 | + } |
| 317 | + } |
| 318 | + |
| 319 | + $sock = fsockopen( "$protocol://$host", $port ); |
| 320 | + if ( !$sock ) { |
| 321 | + return; |
| 322 | + } |
| 323 | + fwrite( $sock, $text ); |
| 324 | + fclose( $sock ); |
| 325 | + } else { |
| 326 | + wfSuppressWarnings(); |
| 327 | + $exists = file_exists( $file ); |
| 328 | + $size = $exists ? filesize( $file ) : false; |
| 329 | + if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) { |
| 330 | + error_log( $text, 3, $file ); |
| 331 | + } |
| 332 | + wfRestoreWarnings(); |
| 333 | + } |
| 334 | +} |
| 335 | + |
| 336 | +/** |
| 337 | + * @todo document |
| 338 | + */ |
| 339 | +function wfLogProfilingData() { |
| 340 | + global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest; |
| 341 | + global $wgProfiler, $wgUser; |
| 342 | + if ( !isset( $wgProfiler ) ) |
| 343 | + return; |
| 344 | + |
| 345 | + $now = wfTime(); |
| 346 | + $elapsed = $now - $wgRequestTime; |
| 347 | + $prof = wfGetProfilingOutput( $wgRequestTime, $elapsed ); |
| 348 | + $forward = ''; |
| 349 | + if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) |
| 350 | + $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR']; |
| 351 | + if( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) |
| 352 | + $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP']; |
| 353 | + if( !empty( $_SERVER['HTTP_FROM'] ) ) |
| 354 | + $forward .= ' from ' . $_SERVER['HTTP_FROM']; |
| 355 | + if( $forward ) |
| 356 | + $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})"; |
| 357 | + // Don't unstub $wgUser at this late stage just for statistics purposes |
| 358 | + if( StubObject::isRealObject($wgUser) && $wgUser->isAnon() ) |
| 359 | + $forward .= ' anon'; |
| 360 | + $log = sprintf( "%s\t%04.3f\t%s\n", |
| 361 | + gmdate( 'YmdHis' ), $elapsed, |
| 362 | + urldecode( $wgRequest->getRequestURL() . $forward ) ); |
| 363 | + if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) { |
| 364 | + wfErrorLog( $log . $prof, $wgDebugLogFile ); |
| 365 | + } |
| 366 | +} |
| 367 | + |
| 368 | +/** |
| 369 | + * Check if the wiki read-only lock file is present. This can be used to lock |
| 370 | + * off editing functions, but doesn't guarantee that the database will not be |
| 371 | + * modified. |
| 372 | + * @return bool |
| 373 | + */ |
| 374 | +function wfReadOnly() { |
| 375 | + global $wgReadOnlyFile, $wgReadOnly; |
| 376 | + |
| 377 | + if ( !is_null( $wgReadOnly ) ) { |
| 378 | + return (bool)$wgReadOnly; |
| 379 | + } |
| 380 | + if ( '' == $wgReadOnlyFile ) { |
| 381 | + return false; |
| 382 | + } |
| 383 | + // Set $wgReadOnly for faster access next time |
| 384 | + if ( is_file( $wgReadOnlyFile ) ) { |
| 385 | + $wgReadOnly = file_get_contents( $wgReadOnlyFile ); |
| 386 | + } else { |
| 387 | + $wgReadOnly = false; |
| 388 | + } |
| 389 | + return (bool)$wgReadOnly; |
| 390 | +} |
| 391 | + |
| 392 | +function wfReadOnlyReason() { |
| 393 | + global $wgReadOnly; |
| 394 | + wfReadOnly(); |
| 395 | + return $wgReadOnly; |
| 396 | +} |
| 397 | + |
| 398 | +/** |
| 399 | + * Return a Language object from $langcode |
| 400 | + * @param $langcode Mixed: either: |
| 401 | + * - a Language object |
| 402 | + * - code of the language to get the message for, if it is |
| 403 | + * a valid code create a language for that language, if |
| 404 | + * it is a string but not a valid code then make a basic |
| 405 | + * language object |
| 406 | + * - a boolean: if it's false then use the current users |
| 407 | + * language (as a fallback for the old parameter |
| 408 | + * functionality), or if it is true then use the wikis |
| 409 | + * @return Language object |
| 410 | + */ |
| 411 | +function wfGetLangObj( $langcode = false ){ |
| 412 | + # Identify which language to get or create a language object for. |
| 413 | + if( $langcode instanceof Language ) |
| 414 | + # Great, we already have the object! |
| 415 | + return $langcode; |
| 416 | + |
| 417 | + global $wgContLang; |
| 418 | + if( $langcode === $wgContLang->getCode() || $langcode === true ) |
| 419 | + # $langcode is the language code of the wikis content language object. |
| 420 | + # or it is a boolean and value is true |
| 421 | + return $wgContLang; |
| 422 | + |
| 423 | + global $wgLang; |
| 424 | + if( $langcode === $wgLang->getCode() || $langcode === false ) |
| 425 | + # $langcode is the language code of user language object. |
| 426 | + # or it was a boolean and value is false |
| 427 | + return $wgLang; |
| 428 | + |
| 429 | + $validCodes = array_keys( Language::getLanguageNames() ); |
| 430 | + if( in_array( $langcode, $validCodes ) ) |
| 431 | + # $langcode corresponds to a valid language. |
| 432 | + return Language::factory( $langcode ); |
| 433 | + |
| 434 | + # $langcode is a string, but not a valid language code; use content language. |
| 435 | + wfDebug( 'Invalid language code passed to wfGetLangObj, falling back to content language.' ); |
| 436 | + return $wgContLang; |
| 437 | +} |
| 438 | + |
| 439 | +/** |
| 440 | + * Get a message from anywhere, for the current user language. |
| 441 | + * |
| 442 | + * Use wfMsgForContent() instead if the message should NOT |
| 443 | + * change depending on the user preferences. |
| 444 | + * |
| 445 | + * @param $key String: lookup key for the message, usually |
| 446 | + * defined in languages/Language.php |
| 447 | + * |
| 448 | + * This function also takes extra optional parameters (not |
| 449 | + * shown in the function definition), which can by used to |
| 450 | + * insert variable text into the predefined message. |
| 451 | + */ |
| 452 | +function wfMsg( $key ) { |
| 453 | + $args = func_get_args(); |
| 454 | + array_shift( $args ); |
| 455 | + return wfMsgReal( $key, $args, true ); |
| 456 | +} |
| 457 | + |
| 458 | +/** |
| 459 | + * Same as above except doesn't transform the message |
| 460 | + */ |
| 461 | +function wfMsgNoTrans( $key ) { |
| 462 | + $args = func_get_args(); |
| 463 | + array_shift( $args ); |
| 464 | + return wfMsgReal( $key, $args, true, false, false ); |
| 465 | +} |
| 466 | + |
| 467 | +/** |
| 468 | + * Get a message from anywhere, for the current global language |
| 469 | + * set with $wgLanguageCode. |
| 470 | + * |
| 471 | + * Use this if the message should NOT change dependent on the |
| 472 | + * language set in the user's preferences. This is the case for |
| 473 | + * most text written into logs, as well as link targets (such as |
| 474 | + * the name of the copyright policy page). Link titles, on the |
| 475 | + * other hand, should be shown in the UI language. |
| 476 | + * |
| 477 | + * Note that MediaWiki allows users to change the user interface |
| 478 | + * language in their preferences, but a single installation |
| 479 | + * typically only contains content in one language. |
| 480 | + * |
| 481 | + * Be wary of this distinction: If you use wfMsg() where you should |
| 482 | + * use wfMsgForContent(), a user of the software may have to |
| 483 | + * customize over 70 messages in order to, e.g., fix a link in every |
| 484 | + * possible language. |
| 485 | + * |
| 486 | + * @param $key String: lookup key for the message, usually |
| 487 | + * defined in languages/Language.php |
| 488 | + */ |
| 489 | +function wfMsgForContent( $key ) { |
| 490 | + global $wgForceUIMsgAsContentMsg; |
| 491 | + $args = func_get_args(); |
| 492 | + array_shift( $args ); |
| 493 | + $forcontent = true; |
| 494 | + if( is_array( $wgForceUIMsgAsContentMsg ) && |
| 495 | + in_array( $key, $wgForceUIMsgAsContentMsg ) ) |
| 496 | + $forcontent = false; |
| 497 | + return wfMsgReal( $key, $args, true, $forcontent ); |
| 498 | +} |
| 499 | + |
| 500 | +/** |
| 501 | + * Same as above except doesn't transform the message |
| 502 | + */ |
| 503 | +function wfMsgForContentNoTrans( $key ) { |
| 504 | + global $wgForceUIMsgAsContentMsg; |
| 505 | + $args = func_get_args(); |
| 506 | + array_shift( $args ); |
| 507 | + $forcontent = true; |
| 508 | + if( is_array( $wgForceUIMsgAsContentMsg ) && |
| 509 | + in_array( $key, $wgForceUIMsgAsContentMsg ) ) |
| 510 | + $forcontent = false; |
| 511 | + return wfMsgReal( $key, $args, true, $forcontent, false ); |
| 512 | +} |
| 513 | + |
| 514 | +/** |
| 515 | + * Get a message from the language file, for the UI elements |
| 516 | + */ |
| 517 | +function wfMsgNoDB( $key ) { |
| 518 | + $args = func_get_args(); |
| 519 | + array_shift( $args ); |
| 520 | + return wfMsgReal( $key, $args, false ); |
| 521 | +} |
| 522 | + |
| 523 | +/** |
| 524 | + * Get a message from the language file, for the content |
| 525 | + */ |
| 526 | +function wfMsgNoDBForContent( $key ) { |
| 527 | + global $wgForceUIMsgAsContentMsg; |
| 528 | + $args = func_get_args(); |
| 529 | + array_shift( $args ); |
| 530 | + $forcontent = true; |
| 531 | + if( is_array( $wgForceUIMsgAsContentMsg ) && |
| 532 | + in_array( $key, $wgForceUIMsgAsContentMsg ) ) |
| 533 | + $forcontent = false; |
| 534 | + return wfMsgReal( $key, $args, false, $forcontent ); |
| 535 | +} |
| 536 | + |
| 537 | + |
| 538 | +/** |
| 539 | + * Really get a message |
| 540 | + * @param $key String: key to get. |
| 541 | + * @param $args |
| 542 | + * @param $useDB Boolean |
| 543 | + * @param $transform Boolean: Whether or not to transform the message. |
| 544 | + * @param $forContent Boolean |
| 545 | + * @return String: the requested message. |
| 546 | + */ |
| 547 | +function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform = true ) { |
| 548 | + wfProfileIn( __METHOD__ ); |
| 549 | + $message = wfMsgGetKey( $key, $useDB, $forContent, $transform ); |
| 550 | + $message = wfMsgReplaceArgs( $message, $args ); |
| 551 | + wfProfileOut( __METHOD__ ); |
| 552 | + return $message; |
| 553 | +} |
| 554 | + |
| 555 | +/** |
| 556 | + * This function provides the message source for messages to be edited which are *not* stored in the database. |
| 557 | + * @param $key String: |
| 558 | + */ |
| 559 | +function wfMsgWeirdKey ( $key ) { |
| 560 | + $source = wfMsgGetKey( $key, false, true, false ); |
| 561 | + if ( wfEmptyMsg( $key, $source ) ) |
| 562 | + return ""; |
| 563 | + else |
| 564 | + return $source; |
| 565 | +} |
| 566 | + |
| 567 | +/** |
| 568 | + * Fetch a message string value, but don't replace any keys yet. |
| 569 | + * @param string $key |
| 570 | + * @param bool $useDB |
| 571 | + * @param string $langcode Code of the language to get the message for, or |
| 572 | + * behaves as a content language switch if it is a |
| 573 | + * boolean. |
| 574 | + * @return string |
| 575 | + * @private |
| 576 | + */ |
| 577 | +function wfMsgGetKey( $key, $useDB, $langCode = false, $transform = true ) { |
| 578 | + global $wgContLang, $wgMessageCache; |
| 579 | + |
| 580 | + wfRunHooks('NormalizeMessageKey', array(&$key, &$useDB, &$langCode, &$transform)); |
| 581 | + |
| 582 | + # If $wgMessageCache isn't initialised yet, try to return something sensible. |
| 583 | + if( is_object( $wgMessageCache ) ) { |
| 584 | + $message = $wgMessageCache->get( $key, $useDB, $langCode ); |
| 585 | + if ( $transform ) { |
| 586 | + $message = $wgMessageCache->transform( $message ); |
| 587 | + } |
| 588 | + } else { |
| 589 | + $lang = wfGetLangObj( $langCode ); |
| 590 | + |
| 591 | + # MessageCache::get() does this already, Language::getMessage() doesn't |
| 592 | + # ISSUE: Should we try to handle "message/lang" here too? |
| 593 | + $key = str_replace( ' ' , '_' , $wgContLang->lcfirst( $key ) ); |
| 594 | + |
| 595 | + if( is_object( $lang ) ) { |
| 596 | + $message = $lang->getMessage( $key ); |
| 597 | + } else { |
| 598 | + $message = false; |
| 599 | + } |
| 600 | + } |
| 601 | + |
| 602 | + return $message; |
| 603 | +} |
| 604 | + |
| 605 | +/** |
| 606 | + * Replace message parameter keys on the given formatted output. |
| 607 | + * |
| 608 | + * @param string $message |
| 609 | + * @param array $args |
| 610 | + * @return string |
| 611 | + * @private |
| 612 | + */ |
| 613 | +function wfMsgReplaceArgs( $message, $args ) { |
| 614 | + # Fix windows line-endings |
| 615 | + # Some messages are split with explode("\n", $msg) |
| 616 | + $message = str_replace( "\r", '', $message ); |
| 617 | + |
| 618 | + // Replace arguments |
| 619 | + if ( count( $args ) ) { |
| 620 | + if ( is_array( $args[0] ) ) { |
| 621 | + $args = array_values( $args[0] ); |
| 622 | + } |
| 623 | + $replacementKeys = array(); |
| 624 | + foreach( $args as $n => $param ) { |
| 625 | + $replacementKeys['$' . ($n + 1)] = $param; |
| 626 | + } |
| 627 | + $message = strtr( $message, $replacementKeys ); |
| 628 | + } |
| 629 | + |
| 630 | + return $message; |
| 631 | +} |
| 632 | + |
| 633 | +/** |
| 634 | + * Return an HTML-escaped version of a message. |
| 635 | + * Parameter replacements, if any, are done *after* the HTML-escaping, |
| 636 | + * so parameters may contain HTML (eg links or form controls). Be sure |
| 637 | + * to pre-escape them if you really do want plaintext, or just wrap |
| 638 | + * the whole thing in htmlspecialchars(). |
| 639 | + * |
| 640 | + * @param string $key |
| 641 | + * @param string ... parameters |
| 642 | + * @return string |
| 643 | + */ |
| 644 | +function wfMsgHtml( $key ) { |
| 645 | + $args = func_get_args(); |
| 646 | + array_shift( $args ); |
| 647 | + return wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $key, true ) ), $args ); |
| 648 | +} |
| 649 | + |
| 650 | +/** |
| 651 | + * Return an HTML version of message |
| 652 | + * Parameter replacements, if any, are done *after* parsing the wiki-text message, |
| 653 | + * so parameters may contain HTML (eg links or form controls). Be sure |
| 654 | + * to pre-escape them if you really do want plaintext, or just wrap |
| 655 | + * the whole thing in htmlspecialchars(). |
| 656 | + * |
| 657 | + * @param string $key |
| 658 | + * @param string ... parameters |
| 659 | + * @return string |
| 660 | + */ |
| 661 | +function wfMsgWikiHtml( $key ) { |
| 662 | + global $wgOut; |
| 663 | + $args = func_get_args(); |
| 664 | + array_shift( $args ); |
| 665 | + return wfMsgReplaceArgs( $wgOut->parse( wfMsgGetKey( $key, true ), /* can't be set to false */ true ), $args ); |
| 666 | +} |
| 667 | + |
| 668 | +/** |
| 669 | + * Returns message in the requested format |
| 670 | + * @param string $key Key of the message |
| 671 | + * @param array $options Processing rules. Can take the following options: |
| 672 | + * <i>parse</i>: parses wikitext to html |
| 673 | + * <i>parseinline</i>: parses wikitext to html and removes the surrounding |
| 674 | + * p's added by parser or tidy |
| 675 | + * <i>escape</i>: filters message through htmlspecialchars |
| 676 | + * <i>escapenoentities</i>: same, but allows entity references like through |
| 677 | + * <i>replaceafter</i>: parameters are substituted after parsing or escaping |
| 678 | + * <i>parsemag</i>: transform the message using magic phrases |
| 679 | + * <i>content</i>: fetch message for content language instead of interface |
| 680 | + * Also can accept a single associative argument, of the form 'language' => 'xx': |
| 681 | + * <i>language</i>: Language object or language code to fetch message for |
| 682 | + * (overriden by <i>content</i>), its behaviour with parser, parseinline |
| 683 | + * and parsemag is undefined. |
| 684 | + * Behavior for conflicting options (e.g., parse+parseinline) is undefined. |
| 685 | + */ |
| 686 | +function wfMsgExt( $key, $options ) { |
| 687 | + global $wgOut; |
| 688 | + |
| 689 | + $args = func_get_args(); |
| 690 | + array_shift( $args ); |
| 691 | + array_shift( $args ); |
| 692 | + $options = (array)$options; |
| 693 | + |
| 694 | + foreach( $options as $arrayKey => $option ) { |
| 695 | + if( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) { |
| 696 | + # An unknown index, neither numeric nor "language" |
| 697 | + trigger_error( "wfMsgExt called with incorrect parameter key $arrayKey", E_USER_WARNING ); |
| 698 | + } elseif( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option, |
| 699 | + array( 'parse', 'parseinline', 'escape', 'escapenoentities', |
| 700 | + 'replaceafter', 'parsemag', 'content' ) ) ) { |
| 701 | + # A numeric index with unknown value |
| 702 | + trigger_error( "wfMsgExt called with incorrect parameter $option", E_USER_WARNING ); |
| 703 | + } |
| 704 | + } |
| 705 | + |
| 706 | + if( in_array('content', $options, true ) ) { |
| 707 | + $forContent = true; |
| 708 | + $langCode = true; |
| 709 | + } elseif( array_key_exists('language', $options) ) { |
| 710 | + $forContent = false; |
| 711 | + $langCode = wfGetLangObj( $options['language'] ); |
| 712 | + } else { |
| 713 | + $forContent = false; |
| 714 | + $langCode = false; |
| 715 | + } |
| 716 | + |
| 717 | + $string = wfMsgGetKey( $key, /*DB*/true, $langCode, /*Transform*/false ); |
| 718 | + |
| 719 | + if( !in_array('replaceafter', $options, true ) ) { |
| 720 | + $string = wfMsgReplaceArgs( $string, $args ); |
| 721 | + } |
| 722 | + |
| 723 | + if( in_array('parse', $options, true ) ) { |
| 724 | + $string = $wgOut->parse( $string, true, !$forContent ); |
| 725 | + } elseif ( in_array('parseinline', $options, true ) ) { |
| 726 | + $string = $wgOut->parse( $string, true, !$forContent ); |
| 727 | + $m = array(); |
| 728 | + if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) { |
| 729 | + $string = $m[1]; |
| 730 | + } |
| 731 | + } elseif ( in_array('parsemag', $options, true ) ) { |
| 732 | + global $wgMessageCache; |
| 733 | + if ( isset( $wgMessageCache ) ) { |
| 734 | + $string = $wgMessageCache->transform( $string, |
| 735 | + !$forContent, |
| 736 | + is_object( $langCode ) ? $langCode : null ); |
| 737 | + } |
| 738 | + } |
| 739 | + |
| 740 | + if ( in_array('escape', $options, true ) ) { |
| 741 | + $string = htmlspecialchars ( $string ); |
| 742 | + } elseif ( in_array( 'escapenoentities', $options, true ) ) { |
| 743 | + $string = Sanitizer::escapeHtmlAllowEntities( $string ); |
| 744 | + } |
| 745 | + |
| 746 | + if( in_array('replaceafter', $options, true ) ) { |
| 747 | + $string = wfMsgReplaceArgs( $string, $args ); |
| 748 | + } |
| 749 | + |
| 750 | + return $string; |
| 751 | +} |
| 752 | + |
| 753 | + |
| 754 | +/** |
| 755 | + * Just like exit() but makes a note of it. |
| 756 | + * Commits open transactions except if the error parameter is set |
| 757 | + * |
| 758 | + * @deprecated Please return control to the caller or throw an exception |
| 759 | + */ |
| 760 | +function wfAbruptExit( $error = false ){ |
| 761 | + static $called = false; |
| 762 | + if ( $called ){ |
| 763 | + exit( -1 ); |
| 764 | + } |
| 765 | + $called = true; |
| 766 | + |
| 767 | + $bt = wfDebugBacktrace(); |
| 768 | + if( $bt ) { |
| 769 | + for($i = 0; $i < count($bt) ; $i++){ |
| 770 | + $file = isset($bt[$i]['file']) ? $bt[$i]['file'] : "unknown"; |
| 771 | + $line = isset($bt[$i]['line']) ? $bt[$i]['line'] : "unknown"; |
| 772 | + wfDebug("WARNING: Abrupt exit in $file at line $line\n"); |
| 773 | + } |
| 774 | + } else { |
| 775 | + wfDebug('WARNING: Abrupt exit\n'); |
| 776 | + } |
| 777 | + |
| 778 | + wfLogProfilingData(); |
| 779 | + |
| 780 | + if ( !$error ) { |
| 781 | + wfGetLB()->closeAll(); |
| 782 | + } |
| 783 | + exit( -1 ); |
| 784 | +} |
| 785 | + |
| 786 | +/** |
| 787 | + * @deprecated Please return control the caller or throw an exception |
| 788 | + */ |
| 789 | +function wfErrorExit() { |
| 790 | + wfAbruptExit( true ); |
| 791 | +} |
| 792 | + |
| 793 | +/** |
| 794 | + * Print a simple message and die, returning nonzero to the shell if any. |
| 795 | + * Plain die() fails to return nonzero to the shell if you pass a string. |
| 796 | + * @param string $msg |
| 797 | + */ |
| 798 | +function wfDie( $msg='' ) { |
| 799 | + echo $msg; |
| 800 | + die( 1 ); |
| 801 | +} |
| 802 | + |
| 803 | +/** |
| 804 | + * Throw a debugging exception. This function previously once exited the process, |
| 805 | + * but now throws an exception instead, with similar results. |
| 806 | + * |
| 807 | + * @param string $msg Message shown when dieing. |
| 808 | + */ |
| 809 | +function wfDebugDieBacktrace( $msg = '' ) { |
| 810 | + throw new MWException( $msg ); |
| 811 | +} |
| 812 | + |
| 813 | +/** |
| 814 | + * Fetch server name for use in error reporting etc. |
| 815 | + * Use real server name if available, so we know which machine |
| 816 | + * in a server farm generated the current page. |
| 817 | + * @return string |
| 818 | + */ |
| 819 | +function wfHostname() { |
| 820 | + static $host; |
| 821 | + if ( is_null( $host ) ) { |
| 822 | + if ( function_exists( 'posix_uname' ) ) { |
| 823 | + // This function not present on Windows |
| 824 | + $uname = @posix_uname(); |
| 825 | + } else { |
| 826 | + $uname = false; |
| 827 | + } |
| 828 | + if( is_array( $uname ) && isset( $uname['nodename'] ) ) { |
| 829 | + $host = $uname['nodename']; |
| 830 | + } elseif ( getenv( 'COMPUTERNAME' ) ) { |
| 831 | + # Windows computer name |
| 832 | + $host = getenv( 'COMPUTERNAME' ); |
| 833 | + } else { |
| 834 | + # This may be a virtual server. |
| 835 | + $host = $_SERVER['SERVER_NAME']; |
| 836 | + } |
| 837 | + } |
| 838 | + return $host; |
| 839 | +} |
| 840 | + |
| 841 | + /** |
| 842 | + * Returns a HTML comment with the elapsed time since request. |
| 843 | + * This method has no side effects. |
| 844 | + * @return string |
| 845 | + */ |
| 846 | + function wfReportTime() { |
| 847 | + global $wgRequestTime, $wgShowHostnames; |
| 848 | + |
| 849 | + $now = wfTime(); |
| 850 | + $elapsed = $now - $wgRequestTime; |
| 851 | + |
| 852 | + return $wgShowHostnames |
| 853 | + ? sprintf( "<!-- Served by %s in %01.3f secs. -->", wfHostname(), $elapsed ) |
| 854 | + : sprintf( "<!-- Served in %01.3f secs. -->", $elapsed ); |
| 855 | + } |
| 856 | + |
| 857 | +/** |
| 858 | + * Safety wrapper for debug_backtrace(). |
| 859 | + * |
| 860 | + * With Zend Optimizer 3.2.0 loaded, this causes segfaults under somewhat |
| 861 | + * murky circumstances, which may be triggered in part by stub objects |
| 862 | + * or other fancy talkin'. |
| 863 | + * |
| 864 | + * Will return an empty array if Zend Optimizer is detected, otherwise |
| 865 | + * the output from debug_backtrace() (trimmed). |
| 866 | + * |
| 867 | + * @return array of backtrace information |
| 868 | + */ |
| 869 | +function wfDebugBacktrace() { |
| 870 | + if( extension_loaded( 'Zend Optimizer' ) ) { |
| 871 | + wfDebug( "Zend Optimizer detected; skipping debug_backtrace for safety.\n" ); |
| 872 | + return array(); |
| 873 | + } else { |
| 874 | + return array_slice( debug_backtrace(), 1 ); |
| 875 | + } |
| 876 | +} |
| 877 | + |
| 878 | +function wfBacktrace() { |
| 879 | + global $wgCommandLineMode; |
| 880 | + |
| 881 | + if ( $wgCommandLineMode ) { |
| 882 | + $msg = ''; |
| 883 | + } else { |
| 884 | + $msg = "<ul>\n"; |
| 885 | + } |
| 886 | + $backtrace = wfDebugBacktrace(); |
| 887 | + foreach( $backtrace as $call ) { |
| 888 | + if( isset( $call['file'] ) ) { |
| 889 | + $f = explode( DIRECTORY_SEPARATOR, $call['file'] ); |
| 890 | + $file = $f[count($f)-1]; |
| 891 | + } else { |
| 892 | + $file = '-'; |
| 893 | + } |
| 894 | + if( isset( $call['line'] ) ) { |
| 895 | + $line = $call['line']; |
| 896 | + } else { |
| 897 | + $line = '-'; |
| 898 | + } |
| 899 | + if ( $wgCommandLineMode ) { |
| 900 | + $msg .= "$file line $line calls "; |
| 901 | + } else { |
| 902 | + $msg .= '<li>' . $file . ' line ' . $line . ' calls '; |
| 903 | + } |
| 904 | + if( !empty( $call['class'] ) ) $msg .= $call['class'] . '::'; |
| 905 | + $msg .= $call['function'] . '()'; |
| 906 | + |
| 907 | + if ( $wgCommandLineMode ) { |
| 908 | + $msg .= "\n"; |
| 909 | + } else { |
| 910 | + $msg .= "</li>\n"; |
| 911 | + } |
| 912 | + } |
| 913 | + if ( $wgCommandLineMode ) { |
| 914 | + $msg .= "\n"; |
| 915 | + } else { |
| 916 | + $msg .= "</ul>\n"; |
| 917 | + } |
| 918 | + |
| 919 | + return $msg; |
| 920 | +} |
| 921 | + |
| 922 | + |
| 923 | +/* Some generic result counters, pulled out of SearchEngine */ |
| 924 | + |
| 925 | + |
| 926 | +/** |
| 927 | + * @todo document |
| 928 | + */ |
| 929 | +function wfShowingResults( $offset, $limit ) { |
| 930 | + global $wgLang; |
| 931 | + return wfMsgExt( 'showingresults', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) ); |
| 932 | +} |
| 933 | + |
| 934 | +/** |
| 935 | + * @todo document |
| 936 | + */ |
| 937 | +function wfShowingResultsNum( $offset, $limit, $num ) { |
| 938 | + global $wgLang; |
| 939 | + return wfMsgExt( 'showingresultsnum', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ), $wgLang->formatNum( $num ) ); |
| 940 | +} |
| 941 | + |
| 942 | +/** |
| 943 | + * @todo document |
| 944 | + */ |
| 945 | +function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { |
| 946 | + global $wgLang; |
| 947 | + $fmtLimit = $wgLang->formatNum( $limit ); |
| 948 | + $prev = wfMsg( 'prevn', $fmtLimit ); |
| 949 | + $next = wfMsg( 'nextn', $fmtLimit ); |
| 950 | + |
| 951 | + if( is_object( $link ) ) { |
| 952 | + $title =& $link; |
| 953 | + } else { |
| 954 | + $title = Title::newFromText( $link ); |
| 955 | + if( is_null( $title ) ) { |
| 956 | + return false; |
| 957 | + } |
| 958 | + } |
| 959 | + |
| 960 | + if ( 0 != $offset ) { |
| 961 | + $po = $offset - $limit; |
| 962 | + if ( $po < 0 ) { $po = 0; } |
| 963 | + $q = "limit={$limit}&offset={$po}"; |
| 964 | + if ( '' != $query ) { $q .= '&'.$query; } |
| 965 | + $plink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-prevlink\">{$prev}</a>"; |
| 966 | + } else { $plink = $prev; } |
| 967 | + |
| 968 | + $no = $offset + $limit; |
| 969 | + $q = 'limit='.$limit.'&offset='.$no; |
| 970 | + if ( '' != $query ) { $q .= '&'.$query; } |
| 971 | + |
| 972 | + if ( $atend ) { |
| 973 | + $nlink = $next; |
| 974 | + } else { |
| 975 | + $nlink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-nextlink\">{$next}</a>"; |
| 976 | + } |
| 977 | + $nums = wfNumLink( $offset, 20, $title, $query ) . ' | ' . |
| 978 | + wfNumLink( $offset, 50, $title, $query ) . ' | ' . |
| 979 | + wfNumLink( $offset, 100, $title, $query ) . ' | ' . |
| 980 | + wfNumLink( $offset, 250, $title, $query ) . ' | ' . |
| 981 | + wfNumLink( $offset, 500, $title, $query ); |
| 982 | + |
| 983 | + return wfMsg( 'viewprevnext', $plink, $nlink, $nums ); |
| 984 | +} |
| 985 | + |
| 986 | +/** |
| 987 | + * @todo document |
| 988 | + */ |
| 989 | +function wfNumLink( $offset, $limit, &$title, $query = '' ) { |
| 990 | + global $wgLang; |
| 991 | + if ( '' == $query ) { $q = ''; } |
| 992 | + else { $q = $query.'&'; } |
| 993 | + $q .= 'limit='.$limit.'&offset='.$offset; |
| 994 | + |
| 995 | + $fmtLimit = $wgLang->formatNum( $limit ); |
| 996 | + $s = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-numlink\">{$fmtLimit}</a>"; |
| 997 | + return $s; |
| 998 | +} |
| 999 | + |
| 1000 | +/** |
| 1001 | + * @todo document |
| 1002 | + * @todo FIXME: we may want to blacklist some broken browsers |
| 1003 | + * |
| 1004 | + * @return bool Whereas client accept gzip compression |
| 1005 | + */ |
| 1006 | +function wfClientAcceptsGzip() { |
| 1007 | + global $wgUseGzip; |
| 1008 | + if( $wgUseGzip ) { |
| 1009 | + # FIXME: we may want to blacklist some broken browsers |
| 1010 | + $m = array(); |
| 1011 | + if( preg_match( |
| 1012 | + '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/', |
| 1013 | + $_SERVER['HTTP_ACCEPT_ENCODING'], |
| 1014 | + $m ) ) { |
| 1015 | + if( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) return false; |
| 1016 | + wfDebug( " accepts gzip\n" ); |
| 1017 | + return true; |
| 1018 | + } |
| 1019 | + } |
| 1020 | + return false; |
| 1021 | +} |
| 1022 | + |
| 1023 | +/** |
| 1024 | + * Obtain the offset and limit values from the request string; |
| 1025 | + * used in special pages |
| 1026 | + * |
| 1027 | + * @param $deflimit Default limit if none supplied |
| 1028 | + * @param $optionname Name of a user preference to check against |
| 1029 | + * @return array |
| 1030 | + * |
| 1031 | + */ |
| 1032 | +function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) { |
| 1033 | + global $wgRequest; |
| 1034 | + return $wgRequest->getLimitOffset( $deflimit, $optionname ); |
| 1035 | +} |
| 1036 | + |
| 1037 | +/** |
| 1038 | + * Escapes the given text so that it may be output using addWikiText() |
| 1039 | + * without any linking, formatting, etc. making its way through. This |
| 1040 | + * is achieved by substituting certain characters with HTML entities. |
| 1041 | + * As required by the callers, <nowiki> is not used. It currently does |
| 1042 | + * not filter out characters which have special meaning only at the |
| 1043 | + * start of a line, such as "*". |
| 1044 | + * |
| 1045 | + * @param string $text Text to be escaped |
| 1046 | + */ |
| 1047 | +function wfEscapeWikiText( $text ) { |
| 1048 | + $text = str_replace( |
| 1049 | + array( '[', '|', ']', '\'', 'ISBN ', 'RFC ', '://', "\n=", '{{' ), # }} |
| 1050 | + array( '[', '|', ']', ''', 'ISBN ', 'RFC ', '://', "\n=", '{{' ), |
| 1051 | + htmlspecialchars($text) ); |
| 1052 | + return $text; |
| 1053 | +} |
| 1054 | + |
| 1055 | +/** |
| 1056 | + * @todo document |
| 1057 | + */ |
| 1058 | +function wfQuotedPrintable( $string, $charset = '' ) { |
| 1059 | + # Probably incomplete; see RFC 2045 |
| 1060 | + if( empty( $charset ) ) { |
| 1061 | + global $wgInputEncoding; |
| 1062 | + $charset = $wgInputEncoding; |
| 1063 | + } |
| 1064 | + $charset = strtoupper( $charset ); |
| 1065 | + $charset = str_replace( 'ISO-8859', 'ISO8859', $charset ); // ? |
| 1066 | + |
| 1067 | + $illegal = '\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff='; |
| 1068 | + $replace = $illegal . '\t ?_'; |
| 1069 | + if( !preg_match( "/[$illegal]/", $string ) ) return $string; |
| 1070 | + $out = "=?$charset?Q?"; |
| 1071 | + $out .= preg_replace( "/([$replace])/e", 'sprintf("=%02X",ord("$1"))', $string ); |
| 1072 | + $out .= '?='; |
| 1073 | + return $out; |
| 1074 | +} |
| 1075 | + |
| 1076 | + |
| 1077 | +/** |
| 1078 | + * @todo document |
| 1079 | + * @return float |
| 1080 | + */ |
| 1081 | +function wfTime() { |
| 1082 | + return microtime(true); |
| 1083 | +} |
| 1084 | + |
| 1085 | +/** |
| 1086 | + * Sets dest to source and returns the original value of dest |
| 1087 | + * If source is NULL, it just returns the value, it doesn't set the variable |
| 1088 | + */ |
| 1089 | +function wfSetVar( &$dest, $source ) { |
| 1090 | + $temp = $dest; |
| 1091 | + if ( !is_null( $source ) ) { |
| 1092 | + $dest = $source; |
| 1093 | + } |
| 1094 | + return $temp; |
| 1095 | +} |
| 1096 | + |
| 1097 | +/** |
| 1098 | + * As for wfSetVar except setting a bit |
| 1099 | + */ |
| 1100 | +function wfSetBit( &$dest, $bit, $state = true ) { |
| 1101 | + $temp = (bool)($dest & $bit ); |
| 1102 | + if ( !is_null( $state ) ) { |
| 1103 | + if ( $state ) { |
| 1104 | + $dest |= $bit; |
| 1105 | + } else { |
| 1106 | + $dest &= ~$bit; |
| 1107 | + } |
| 1108 | + } |
| 1109 | + return $temp; |
| 1110 | +} |
| 1111 | + |
| 1112 | +/** |
| 1113 | + * This function takes two arrays as input, and returns a CGI-style string, e.g. |
| 1114 | + * "days=7&limit=100". Options in the first array override options in the second. |
| 1115 | + * Options set to "" will not be output. |
| 1116 | + */ |
| 1117 | +function wfArrayToCGI( $array1, $array2 = NULL ) |
| 1118 | +{ |
| 1119 | + if ( !is_null( $array2 ) ) { |
| 1120 | + $array1 = $array1 + $array2; |
| 1121 | + } |
| 1122 | + |
| 1123 | + $cgi = ''; |
| 1124 | + foreach ( $array1 as $key => $value ) { |
| 1125 | + if ( '' !== $value ) { |
| 1126 | + if ( '' != $cgi ) { |
| 1127 | + $cgi .= '&'; |
| 1128 | + } |
| 1129 | + if(is_array($value)) |
| 1130 | + { |
| 1131 | + $firstTime = true; |
| 1132 | + foreach($value as $v) |
| 1133 | + { |
| 1134 | + $cgi .= ($firstTime ? '' : '&') . |
| 1135 | + urlencode( $key . '[]' ) . '=' . |
| 1136 | + urlencode( $v ); |
| 1137 | + $firstTime = false; |
| 1138 | + } |
| 1139 | + } |
| 1140 | + else |
| 1141 | + $cgi .= urlencode( $key ) . '=' . |
| 1142 | + urlencode( $value ); |
| 1143 | + } |
| 1144 | + } |
| 1145 | + return $cgi; |
| 1146 | +} |
| 1147 | + |
| 1148 | +/** |
| 1149 | + * This is the logical opposite of wfArrayToCGI(): it accepts a query string as |
| 1150 | + * its argument and returns the same string in array form. This allows compa- |
| 1151 | + * tibility with legacy functions that accept raw query strings instead of nice |
| 1152 | + * arrays. Of course, keys and values are urldecode()d. Don't try passing in- |
| 1153 | + * valid query strings, or it will explode. |
| 1154 | + * |
| 1155 | + * @param $query string Query string |
| 1156 | + * @return array Array version of input |
| 1157 | + */ |
| 1158 | +function wfCgiToArray( $query ) { |
| 1159 | + if( isset( $query[0] ) and $query[0] == '?' ) { |
| 1160 | + $query = substr( $query, 1 ); |
| 1161 | + } |
| 1162 | + $bits = explode( '&', $query ); |
| 1163 | + $ret = array(); |
| 1164 | + foreach( $bits as $bit ) { |
| 1165 | + if( $bit === '' ) { |
| 1166 | + continue; |
| 1167 | + } |
| 1168 | + list( $key, $value ) = explode( '=', $bit ); |
| 1169 | + $key = urldecode( $key ); |
| 1170 | + $value = urldecode( $value ); |
| 1171 | + $ret[$key] = $value; |
| 1172 | + } |
| 1173 | + return $ret; |
| 1174 | +} |
| 1175 | + |
| 1176 | +/** |
| 1177 | + * Append a query string to an existing URL, which may or may not already |
| 1178 | + * have query string parameters already. If so, they will be combined. |
| 1179 | + * |
| 1180 | + * @param string $url |
| 1181 | + * @param string $query |
| 1182 | + * @return string |
| 1183 | + */ |
| 1184 | +function wfAppendQuery( $url, $query ) { |
| 1185 | + if( $query != '' ) { |
| 1186 | + if( false === strpos( $url, '?' ) ) { |
| 1187 | + $url .= '?'; |
| 1188 | + } else { |
| 1189 | + $url .= '&'; |
| 1190 | + } |
| 1191 | + $url .= $query; |
| 1192 | + } |
| 1193 | + return $url; |
| 1194 | +} |
| 1195 | + |
| 1196 | +/** |
| 1197 | + * Expand a potentially local URL to a fully-qualified URL. |
| 1198 | + * Assumes $wgServer is correct. :) |
| 1199 | + * @param string $url, either fully-qualified or a local path + query |
| 1200 | + * @return string Fully-qualified URL |
| 1201 | + */ |
| 1202 | +function wfExpandUrl( $url ) { |
| 1203 | + if( substr( $url, 0, 1 ) == '/' ) { |
| 1204 | + global $wgServer; |
| 1205 | + return $wgServer . $url; |
| 1206 | + } else { |
| 1207 | + return $url; |
| 1208 | + } |
| 1209 | +} |
| 1210 | + |
| 1211 | +/** |
| 1212 | + * This is obsolete, use SquidUpdate::purge() |
| 1213 | + * @deprecated |
| 1214 | + */ |
| 1215 | +function wfPurgeSquidServers ($urlArr) { |
| 1216 | + SquidUpdate::purge( $urlArr ); |
| 1217 | +} |
| 1218 | + |
| 1219 | +/** |
| 1220 | + * Windows-compatible version of escapeshellarg() |
| 1221 | + * Windows doesn't recognise single-quotes in the shell, but the escapeshellarg() |
| 1222 | + * function puts single quotes in regardless of OS. |
| 1223 | + * |
| 1224 | + * Also fixes the locale problems on Linux in PHP 5.2.6+ (bug backported to |
| 1225 | + * earlier distro releases of PHP) |
| 1226 | + */ |
| 1227 | +function wfEscapeShellArg( ) { |
| 1228 | + wfInitShellLocale(); |
| 1229 | + |
| 1230 | + $args = func_get_args(); |
| 1231 | + $first = true; |
| 1232 | + $retVal = ''; |
| 1233 | + foreach ( $args as $arg ) { |
| 1234 | + if ( !$first ) { |
| 1235 | + $retVal .= ' '; |
| 1236 | + } else { |
| 1237 | + $first = false; |
| 1238 | + } |
| 1239 | + |
| 1240 | + if ( wfIsWindows() ) { |
| 1241 | + // Escaping for an MSVC-style command line parser |
| 1242 | + // Ref: http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html |
| 1243 | + // Double the backslashes before any double quotes. Escape the double quotes. |
| 1244 | + $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE ); |
| 1245 | + $arg = ''; |
| 1246 | + $delim = false; |
| 1247 | + foreach ( $tokens as $token ) { |
| 1248 | + if ( $delim ) { |
| 1249 | + $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"'; |
| 1250 | + } else { |
| 1251 | + $arg .= $token; |
| 1252 | + } |
| 1253 | + $delim = !$delim; |
| 1254 | + } |
| 1255 | + // Double the backslashes before the end of the string, because |
| 1256 | + // we will soon add a quote |
| 1257 | + $m = array(); |
| 1258 | + if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) { |
| 1259 | + $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] ); |
| 1260 | + } |
| 1261 | + |
| 1262 | + // Add surrounding quotes |
| 1263 | + $retVal .= '"' . $arg . '"'; |
| 1264 | + } else { |
| 1265 | + $retVal .= escapeshellarg( $arg ); |
| 1266 | + } |
| 1267 | + } |
| 1268 | + return $retVal; |
| 1269 | +} |
| 1270 | + |
| 1271 | +/** |
| 1272 | + * wfMerge attempts to merge differences between three texts. |
| 1273 | + * Returns true for a clean merge and false for failure or a conflict. |
| 1274 | + */ |
| 1275 | +function wfMerge( $old, $mine, $yours, &$result ){ |
| 1276 | + global $wgDiff3; |
| 1277 | + |
| 1278 | + # This check may also protect against code injection in |
| 1279 | + # case of broken installations. |
| 1280 | + if( !$wgDiff3 || !file_exists( $wgDiff3 ) ) { |
| 1281 | + wfDebug( "diff3 not found\n" ); |
| 1282 | + return false; |
| 1283 | + } |
| 1284 | + |
| 1285 | + # Make temporary files |
| 1286 | + $td = wfTempDir(); |
| 1287 | + $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' ); |
| 1288 | + $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' ); |
| 1289 | + $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' ); |
| 1290 | + |
| 1291 | + fwrite( $oldtextFile, $old ); fclose( $oldtextFile ); |
| 1292 | + fwrite( $mytextFile, $mine ); fclose( $mytextFile ); |
| 1293 | + fwrite( $yourtextFile, $yours ); fclose( $yourtextFile ); |
| 1294 | + |
| 1295 | + # Check for a conflict |
| 1296 | + $cmd = $wgDiff3 . ' -a --overlap-only ' . |
| 1297 | + wfEscapeShellArg( $mytextName ) . ' ' . |
| 1298 | + wfEscapeShellArg( $oldtextName ) . ' ' . |
| 1299 | + wfEscapeShellArg( $yourtextName ); |
| 1300 | + $handle = popen( $cmd, 'r' ); |
| 1301 | + |
| 1302 | + if( fgets( $handle, 1024 ) ){ |
| 1303 | + $conflict = true; |
| 1304 | + } else { |
| 1305 | + $conflict = false; |
| 1306 | + } |
| 1307 | + pclose( $handle ); |
| 1308 | + |
| 1309 | + # Merge differences |
| 1310 | + $cmd = $wgDiff3 . ' -a -e --merge ' . |
| 1311 | + wfEscapeShellArg( $mytextName, $oldtextName, $yourtextName ); |
| 1312 | + $handle = popen( $cmd, 'r' ); |
| 1313 | + $result = ''; |
| 1314 | + do { |
| 1315 | + $data = fread( $handle, 8192 ); |
| 1316 | + if ( strlen( $data ) == 0 ) { |
| 1317 | + break; |
| 1318 | + } |
| 1319 | + $result .= $data; |
| 1320 | + } while ( true ); |
| 1321 | + pclose( $handle ); |
| 1322 | + unlink( $mytextName ); unlink( $oldtextName ); unlink( $yourtextName ); |
| 1323 | + |
| 1324 | + if ( $result === '' && $old !== '' && $conflict == false ) { |
| 1325 | + wfDebug( "Unexpected null result from diff3. Command: $cmd\n" ); |
| 1326 | + $conflict = true; |
| 1327 | + } |
| 1328 | + return ! $conflict; |
| 1329 | +} |
| 1330 | + |
| 1331 | +/** |
| 1332 | + * Returns unified plain-text diff of two texts. |
| 1333 | + * Useful for machine processing of diffs. |
| 1334 | + * @param $before string The text before the changes. |
| 1335 | + * @param $after string The text after the changes. |
| 1336 | + * @param $params string Command-line options for the diff command. |
| 1337 | + * @return string Unified diff of $before and $after |
| 1338 | + */ |
| 1339 | +function wfDiff( $before, $after, $params = '-u' ) { |
| 1340 | + global $wgDiff; |
| 1341 | + |
| 1342 | + # This check may also protect against code injection in |
| 1343 | + # case of broken installations. |
| 1344 | + if( !file_exists( $wgDiff ) ){ |
| 1345 | + wfDebug( "diff executable not found\n" ); |
| 1346 | + $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) ); |
| 1347 | + $format = new UnifiedDiffFormatter(); |
| 1348 | + return $format->format( $diffs ); |
| 1349 | + } |
| 1350 | + |
| 1351 | + # Make temporary files |
| 1352 | + $td = wfTempDir(); |
| 1353 | + $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' ); |
| 1354 | + $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' ); |
| 1355 | + |
| 1356 | + fwrite( $oldtextFile, $before ); fclose( $oldtextFile ); |
| 1357 | + fwrite( $newtextFile, $after ); fclose( $newtextFile ); |
| 1358 | + |
| 1359 | + // Get the diff of the two files |
| 1360 | + $cmd = "$wgDiff " . $params . ' ' .wfEscapeShellArg( $oldtextName, $newtextName ); |
| 1361 | + |
| 1362 | + $h = popen( $cmd, 'r' ); |
| 1363 | + |
| 1364 | + $diff = ''; |
| 1365 | + |
| 1366 | + do { |
| 1367 | + $data = fread( $h, 8192 ); |
| 1368 | + if ( strlen( $data ) == 0 ) { |
| 1369 | + break; |
| 1370 | + } |
| 1371 | + $diff .= $data; |
| 1372 | + } while ( true ); |
| 1373 | + |
| 1374 | + // Clean up |
| 1375 | + pclose( $h ); |
| 1376 | + unlink( $oldtextName ); |
| 1377 | + unlink( $newtextName ); |
| 1378 | + |
| 1379 | + // Kill the --- and +++ lines. They're not useful. |
| 1380 | + $diff_lines = explode( "\n", $diff ); |
| 1381 | + if (strpos( $diff_lines[0], '---' ) === 0) { |
| 1382 | + unset($diff_lines[0]); |
| 1383 | + } |
| 1384 | + if (strpos( $diff_lines[1], '+++' ) === 0) { |
| 1385 | + unset($diff_lines[1]); |
| 1386 | + } |
| 1387 | + |
| 1388 | + $diff = implode( "\n", $diff_lines ); |
| 1389 | + |
| 1390 | + return $diff; |
| 1391 | +} |
| 1392 | + |
| 1393 | +/** |
| 1394 | + * A wrapper around the PHP function var_export(). |
| 1395 | + * Either print it or add it to the regular output ($wgOut). |
| 1396 | + * |
| 1397 | + * @param $var A PHP variable to dump. |
| 1398 | + */ |
| 1399 | +function wfVarDump( $var ) { |
| 1400 | + global $wgOut; |
| 1401 | + $s = str_replace("\n","<br />\n", var_export( $var, true ) . "\n"); |
| 1402 | + if ( headers_sent() || !@is_object( $wgOut ) ) { |
| 1403 | + print $s; |
| 1404 | + } else { |
| 1405 | + $wgOut->addHTML( $s ); |
| 1406 | + } |
| 1407 | +} |
| 1408 | + |
| 1409 | +/** |
| 1410 | + * Provide a simple HTTP error. |
| 1411 | + */ |
| 1412 | +function wfHttpError( $code, $label, $desc ) { |
| 1413 | + global $wgOut; |
| 1414 | + $wgOut->disable(); |
| 1415 | + header( "HTTP/1.0 $code $label" ); |
| 1416 | + header( "Status: $code $label" ); |
| 1417 | + $wgOut->sendCacheControl(); |
| 1418 | + |
| 1419 | + header( 'Content-type: text/html; charset=utf-8' ); |
| 1420 | + print "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">". |
| 1421 | + "<html><head><title>" . |
| 1422 | + htmlspecialchars( $label ) . |
| 1423 | + "</title></head><body><h1>" . |
| 1424 | + htmlspecialchars( $label ) . |
| 1425 | + "</h1><p>" . |
| 1426 | + nl2br( htmlspecialchars( $desc ) ) . |
| 1427 | + "</p></body></html>\n"; |
| 1428 | +} |
| 1429 | + |
| 1430 | +/** |
| 1431 | + * Clear away any user-level output buffers, discarding contents. |
| 1432 | + * |
| 1433 | + * Suitable for 'starting afresh', for instance when streaming |
| 1434 | + * relatively large amounts of data without buffering, or wanting to |
| 1435 | + * output image files without ob_gzhandler's compression. |
| 1436 | + * |
| 1437 | + * The optional $resetGzipEncoding parameter controls suppression of |
| 1438 | + * the Content-Encoding header sent by ob_gzhandler; by default it |
| 1439 | + * is left. See comments for wfClearOutputBuffers() for why it would |
| 1440 | + * be used. |
| 1441 | + * |
| 1442 | + * Note that some PHP configuration options may add output buffer |
| 1443 | + * layers which cannot be removed; these are left in place. |
| 1444 | + * |
| 1445 | + * @param bool $resetGzipEncoding |
| 1446 | + */ |
| 1447 | +function wfResetOutputBuffers( $resetGzipEncoding=true ) { |
| 1448 | + if( $resetGzipEncoding ) { |
| 1449 | + // Suppress Content-Encoding and Content-Length |
| 1450 | + // headers from 1.10+s wfOutputHandler |
| 1451 | + global $wgDisableOutputCompression; |
| 1452 | + $wgDisableOutputCompression = true; |
| 1453 | + } |
| 1454 | + while( $status = ob_get_status() ) { |
| 1455 | + if( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) { |
| 1456 | + // Probably from zlib.output_compression or other |
| 1457 | + // PHP-internal setting which can't be removed. |
| 1458 | + // |
| 1459 | + // Give up, and hope the result doesn't break |
| 1460 | + // output behavior. |
| 1461 | + break; |
| 1462 | + } |
| 1463 | + if( !ob_end_clean() ) { |
| 1464 | + // Could not remove output buffer handler; abort now |
| 1465 | + // to avoid getting in some kind of infinite loop. |
| 1466 | + break; |
| 1467 | + } |
| 1468 | + if( $resetGzipEncoding ) { |
| 1469 | + if( $status['name'] == 'ob_gzhandler' ) { |
| 1470 | + // Reset the 'Content-Encoding' field set by this handler |
| 1471 | + // so we can start fresh. |
| 1472 | + header( 'Content-Encoding:' ); |
| 1473 | + break; |
| 1474 | + } |
| 1475 | + } |
| 1476 | + } |
| 1477 | +} |
| 1478 | + |
| 1479 | +/** |
| 1480 | + * More legible than passing a 'false' parameter to wfResetOutputBuffers(): |
| 1481 | + * |
| 1482 | + * Clear away output buffers, but keep the Content-Encoding header |
| 1483 | + * produced by ob_gzhandler, if any. |
| 1484 | + * |
| 1485 | + * This should be used for HTTP 304 responses, where you need to |
| 1486 | + * preserve the Content-Encoding header of the real result, but |
| 1487 | + * also need to suppress the output of ob_gzhandler to keep to spec |
| 1488 | + * and avoid breaking Firefox in rare cases where the headers and |
| 1489 | + * body are broken over two packets. |
| 1490 | + */ |
| 1491 | +function wfClearOutputBuffers() { |
| 1492 | + wfResetOutputBuffers( false ); |
| 1493 | +} |
| 1494 | + |
| 1495 | +/** |
| 1496 | + * Converts an Accept-* header into an array mapping string values to quality |
| 1497 | + * factors |
| 1498 | + */ |
| 1499 | +function wfAcceptToPrefs( $accept, $def = '*/*' ) { |
| 1500 | + # No arg means accept anything (per HTTP spec) |
| 1501 | + if( !$accept ) { |
| 1502 | + return array( $def => 1.0 ); |
| 1503 | + } |
| 1504 | + |
| 1505 | + $prefs = array(); |
| 1506 | + |
| 1507 | + $parts = explode( ',', $accept ); |
| 1508 | + |
| 1509 | + foreach( $parts as $part ) { |
| 1510 | + # FIXME: doesn't deal with params like 'text/html; level=1' |
| 1511 | + @list( $value, $qpart ) = explode( ';', trim( $part ) ); |
| 1512 | + $match = array(); |
| 1513 | + if( !isset( $qpart ) ) { |
| 1514 | + $prefs[$value] = 1.0; |
| 1515 | + } elseif( preg_match( '/q\s*=\s*(\d*\.\d+)/', $qpart, $match ) ) { |
| 1516 | + $prefs[$value] = floatval($match[1]); |
| 1517 | + } |
| 1518 | + } |
| 1519 | + |
| 1520 | + return $prefs; |
| 1521 | +} |
| 1522 | + |
| 1523 | +/** |
| 1524 | + * Checks if a given MIME type matches any of the keys in the given |
| 1525 | + * array. Basic wildcards are accepted in the array keys. |
| 1526 | + * |
| 1527 | + * Returns the matching MIME type (or wildcard) if a match, otherwise |
| 1528 | + * NULL if no match. |
| 1529 | + * |
| 1530 | + * @param string $type |
| 1531 | + * @param array $avail |
| 1532 | + * @return string |
| 1533 | + * @private |
| 1534 | + */ |
| 1535 | +function mimeTypeMatch( $type, $avail ) { |
| 1536 | + if( array_key_exists($type, $avail) ) { |
| 1537 | + return $type; |
| 1538 | + } else { |
| 1539 | + $parts = explode( '/', $type ); |
| 1540 | + if( array_key_exists( $parts[0] . '/*', $avail ) ) { |
| 1541 | + return $parts[0] . '/*'; |
| 1542 | + } elseif( array_key_exists( '*/*', $avail ) ) { |
| 1543 | + return '*/*'; |
| 1544 | + } else { |
| 1545 | + return NULL; |
| 1546 | + } |
| 1547 | + } |
| 1548 | +} |
| 1549 | + |
| 1550 | +/** |
| 1551 | + * Returns the 'best' match between a client's requested internet media types |
| 1552 | + * and the server's list of available types. Each list should be an associative |
| 1553 | + * array of type to preference (preference is a float between 0.0 and 1.0). |
| 1554 | + * Wildcards in the types are acceptable. |
| 1555 | + * |
| 1556 | + * @param array $cprefs Client's acceptable type list |
| 1557 | + * @param array $sprefs Server's offered types |
| 1558 | + * @return string |
| 1559 | + * |
| 1560 | + * @todo FIXME: doesn't handle params like 'text/plain; charset=UTF-8' |
| 1561 | + * XXX: generalize to negotiate other stuff |
| 1562 | + */ |
| 1563 | +function wfNegotiateType( $cprefs, $sprefs ) { |
| 1564 | + $combine = array(); |
| 1565 | + |
| 1566 | + foreach( array_keys($sprefs) as $type ) { |
| 1567 | + $parts = explode( '/', $type ); |
| 1568 | + if( $parts[1] != '*' ) { |
| 1569 | + $ckey = mimeTypeMatch( $type, $cprefs ); |
| 1570 | + if( $ckey ) { |
| 1571 | + $combine[$type] = $sprefs[$type] * $cprefs[$ckey]; |
| 1572 | + } |
| 1573 | + } |
| 1574 | + } |
| 1575 | + |
| 1576 | + foreach( array_keys( $cprefs ) as $type ) { |
| 1577 | + $parts = explode( '/', $type ); |
| 1578 | + if( $parts[1] != '*' && !array_key_exists( $type, $sprefs ) ) { |
| 1579 | + $skey = mimeTypeMatch( $type, $sprefs ); |
| 1580 | + if( $skey ) { |
| 1581 | + $combine[$type] = $sprefs[$skey] * $cprefs[$type]; |
| 1582 | + } |
| 1583 | + } |
| 1584 | + } |
| 1585 | + |
| 1586 | + $bestq = 0; |
| 1587 | + $besttype = NULL; |
| 1588 | + |
| 1589 | + foreach( array_keys( $combine ) as $type ) { |
| 1590 | + if( $combine[$type] > $bestq ) { |
| 1591 | + $besttype = $type; |
| 1592 | + $bestq = $combine[$type]; |
| 1593 | + } |
| 1594 | + } |
| 1595 | + |
| 1596 | + return $besttype; |
| 1597 | +} |
| 1598 | + |
| 1599 | +/** |
| 1600 | + * Array lookup |
| 1601 | + * Returns an array where the values in the first array are replaced by the |
| 1602 | + * values in the second array with the corresponding keys |
| 1603 | + * |
| 1604 | + * @return array |
| 1605 | + */ |
| 1606 | +function wfArrayLookup( $a, $b ) { |
| 1607 | + return array_flip( array_intersect( array_flip( $a ), array_keys( $b ) ) ); |
| 1608 | +} |
| 1609 | + |
| 1610 | +/** |
| 1611 | + * Convenience function; returns MediaWiki timestamp for the present time. |
| 1612 | + * @return string |
| 1613 | + */ |
| 1614 | +function wfTimestampNow() { |
| 1615 | + # return NOW |
| 1616 | + return wfTimestamp( TS_MW, time() ); |
| 1617 | +} |
| 1618 | + |
| 1619 | +/** |
| 1620 | + * Reference-counted warning suppression |
| 1621 | + */ |
| 1622 | +function wfSuppressWarnings( $end = false ) { |
| 1623 | + static $suppressCount = 0; |
| 1624 | + static $originalLevel = false; |
| 1625 | + |
| 1626 | + if ( $end ) { |
| 1627 | + if ( $suppressCount ) { |
| 1628 | + --$suppressCount; |
| 1629 | + if ( !$suppressCount ) { |
| 1630 | + error_reporting( $originalLevel ); |
| 1631 | + } |
| 1632 | + } |
| 1633 | + } else { |
| 1634 | + if ( !$suppressCount ) { |
| 1635 | + $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE ) ); |
| 1636 | + } |
| 1637 | + ++$suppressCount; |
| 1638 | + } |
| 1639 | +} |
| 1640 | + |
| 1641 | +/** |
| 1642 | + * Restore error level to previous value |
| 1643 | + */ |
| 1644 | +function wfRestoreWarnings() { |
| 1645 | + wfSuppressWarnings( true ); |
| 1646 | +} |
| 1647 | + |
| 1648 | +# Autodetect, convert and provide timestamps of various types |
| 1649 | + |
| 1650 | +/** |
| 1651 | + * Unix time - the number of seconds since 1970-01-01 00:00:00 UTC |
| 1652 | + */ |
| 1653 | +define('TS_UNIX', 0); |
| 1654 | + |
| 1655 | +/** |
| 1656 | + * MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS) |
| 1657 | + */ |
| 1658 | +define('TS_MW', 1); |
| 1659 | + |
| 1660 | +/** |
| 1661 | + * MySQL DATETIME (YYYY-MM-DD HH:MM:SS) |
| 1662 | + */ |
| 1663 | +define('TS_DB', 2); |
| 1664 | + |
| 1665 | +/** |
| 1666 | + * RFC 2822 format, for E-mail and HTTP headers |
| 1667 | + */ |
| 1668 | +define('TS_RFC2822', 3); |
| 1669 | + |
| 1670 | +/** |
| 1671 | + * ISO 8601 format with no timezone: 1986-02-09T20:00:00Z |
| 1672 | + * |
| 1673 | + * This is used by Special:Export |
| 1674 | + */ |
| 1675 | +define('TS_ISO_8601', 4); |
| 1676 | + |
| 1677 | +/** |
| 1678 | + * An Exif timestamp (YYYY:MM:DD HH:MM:SS) |
| 1679 | + * |
| 1680 | + * @see http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the |
| 1681 | + * DateTime tag and page 36 for the DateTimeOriginal and |
| 1682 | + * DateTimeDigitized tags. |
| 1683 | + */ |
| 1684 | +define('TS_EXIF', 5); |
| 1685 | + |
| 1686 | +/** |
| 1687 | + * Oracle format time. |
| 1688 | + */ |
| 1689 | +define('TS_ORACLE', 6); |
| 1690 | + |
| 1691 | +/** |
| 1692 | + * Postgres format time. |
| 1693 | + */ |
| 1694 | +define('TS_POSTGRES', 7); |
| 1695 | + |
| 1696 | +/** |
| 1697 | + * @param mixed $outputtype A timestamp in one of the supported formats, the |
| 1698 | + * function will autodetect which format is supplied |
| 1699 | + * and act accordingly. |
| 1700 | + * @return string Time in the format specified in $outputtype |
| 1701 | + */ |
| 1702 | +function wfTimestamp($outputtype=TS_UNIX,$ts=0) { |
| 1703 | + $uts = 0; |
| 1704 | + $da = array(); |
| 1705 | + if ($ts==0) { |
| 1706 | + $uts=time(); |
| 1707 | + } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D',$ts,$da)) { |
| 1708 | + # TS_DB |
| 1709 | + } elseif (preg_match('/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D',$ts,$da)) { |
| 1710 | + # TS_EXIF |
| 1711 | + } elseif (preg_match('/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/D',$ts,$da)) { |
| 1712 | + # TS_MW |
| 1713 | + } elseif (preg_match('/^\d{1,13}$/D',$ts)) { |
| 1714 | + # TS_UNIX |
| 1715 | + $uts = $ts; |
| 1716 | + } elseif (preg_match('/^\d{1,2}-...-\d\d(?:\d\d)? \d\d\.\d\d\.\d\d/', $ts)) { |
| 1717 | + # TS_ORACLE |
| 1718 | + $uts = strtotime(preg_replace('/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3", |
| 1719 | + str_replace("+00:00", "UTC", $ts))); |
| 1720 | + } elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.*\d*)?Z$/', $ts, $da)) { |
| 1721 | + # TS_ISO_8601 |
| 1722 | + } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d*[\+\- ](\d\d)$/',$ts,$da)) { |
| 1723 | + # TS_POSTGRES |
| 1724 | + } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d* GMT$/',$ts,$da)) { |
| 1725 | + # TS_POSTGRES |
| 1726 | + } else { |
| 1727 | + # Bogus value; fall back to the epoch... |
| 1728 | + wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n"); |
| 1729 | + $uts = 0; |
| 1730 | + } |
| 1731 | + |
| 1732 | + if (count( $da ) ) { |
| 1733 | + // Warning! gmmktime() acts oddly if the month or day is set to 0 |
| 1734 | + // We may want to handle that explicitly at some point |
| 1735 | + $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6], |
| 1736 | + (int)$da[2],(int)$da[3],(int)$da[1]); |
| 1737 | + } |
| 1738 | + |
| 1739 | + switch($outputtype) { |
| 1740 | + case TS_UNIX: |
| 1741 | + return $uts; |
| 1742 | + case TS_MW: |
| 1743 | + return gmdate( 'YmdHis', $uts ); |
| 1744 | + case TS_DB: |
| 1745 | + return gmdate( 'Y-m-d H:i:s', $uts ); |
| 1746 | + case TS_ISO_8601: |
| 1747 | + return gmdate( 'Y-m-d\TH:i:s\Z', $uts ); |
| 1748 | + // This shouldn't ever be used, but is included for completeness |
| 1749 | + case TS_EXIF: |
| 1750 | + return gmdate( 'Y:m:d H:i:s', $uts ); |
| 1751 | + case TS_RFC2822: |
| 1752 | + return gmdate( 'D, d M Y H:i:s', $uts ) . ' GMT'; |
| 1753 | + case TS_ORACLE: |
| 1754 | + return gmdate( 'd-M-y h.i.s A', $uts) . ' +00:00'; |
| 1755 | + case TS_POSTGRES: |
| 1756 | + return gmdate( 'Y-m-d H:i:s', $uts) . ' GMT'; |
| 1757 | + default: |
| 1758 | + throw new MWException( 'wfTimestamp() called with illegal output type.'); |
| 1759 | + } |
| 1760 | +} |
| 1761 | + |
| 1762 | +/** |
| 1763 | + * Return a formatted timestamp, or null if input is null. |
| 1764 | + * For dealing with nullable timestamp columns in the database. |
| 1765 | + * @param int $outputtype |
| 1766 | + * @param string $ts |
| 1767 | + * @return string |
| 1768 | + */ |
| 1769 | +function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) { |
| 1770 | + if( is_null( $ts ) ) { |
| 1771 | + return null; |
| 1772 | + } else { |
| 1773 | + return wfTimestamp( $outputtype, $ts ); |
| 1774 | + } |
| 1775 | +} |
| 1776 | + |
| 1777 | +/** |
| 1778 | + * Check if the operating system is Windows |
| 1779 | + * |
| 1780 | + * @return bool True if it's Windows, False otherwise. |
| 1781 | + */ |
| 1782 | +function wfIsWindows() { |
| 1783 | + if (substr(php_uname(), 0, 7) == 'Windows') { |
| 1784 | + return true; |
| 1785 | + } else { |
| 1786 | + return false; |
| 1787 | + } |
| 1788 | +} |
| 1789 | + |
| 1790 | +/** |
| 1791 | + * Swap two variables |
| 1792 | + */ |
| 1793 | +function swap( &$x, &$y ) { |
| 1794 | + $z = $x; |
| 1795 | + $x = $y; |
| 1796 | + $y = $z; |
| 1797 | +} |
| 1798 | + |
| 1799 | +function wfGetCachedNotice( $name ) { |
| 1800 | + global $wgOut, $wgRenderHashAppend, $parserMemc; |
| 1801 | + $fname = 'wfGetCachedNotice'; |
| 1802 | + wfProfileIn( $fname ); |
| 1803 | + |
| 1804 | + $needParse = false; |
| 1805 | + |
| 1806 | + if( $name === 'default' ) { |
| 1807 | + // special case |
| 1808 | + global $wgSiteNotice; |
| 1809 | + $notice = $wgSiteNotice; |
| 1810 | + if( empty( $notice ) ) { |
| 1811 | + wfProfileOut( $fname ); |
| 1812 | + return false; |
| 1813 | + } |
| 1814 | + } else { |
| 1815 | + $notice = wfMsgForContentNoTrans( $name ); |
| 1816 | + if( wfEmptyMsg( $name, $notice ) || $notice == '-' ) { |
| 1817 | + wfProfileOut( $fname ); |
| 1818 | + return( false ); |
| 1819 | + } |
| 1820 | + } |
| 1821 | + |
| 1822 | + // Use the extra hash appender to let eg SSL variants separately cache. |
| 1823 | + $key = wfMemcKey( $name . $wgRenderHashAppend ); |
| 1824 | + $cachedNotice = $parserMemc->get( $key ); |
| 1825 | + if( is_array( $cachedNotice ) ) { |
| 1826 | + if( md5( $notice ) == $cachedNotice['hash'] ) { |
| 1827 | + $notice = $cachedNotice['html']; |
| 1828 | + } else { |
| 1829 | + $needParse = true; |
| 1830 | + } |
| 1831 | + } else { |
| 1832 | + $needParse = true; |
| 1833 | + } |
| 1834 | + |
| 1835 | + if( $needParse ) { |
| 1836 | + if( is_object( $wgOut ) ) { |
| 1837 | + $parsed = $wgOut->parse( $notice ); |
| 1838 | + $parserMemc->set( $key, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 ); |
| 1839 | + $notice = $parsed; |
| 1840 | + } else { |
| 1841 | + wfDebug( 'wfGetCachedNotice called for ' . $name . ' with no $wgOut available' ); |
| 1842 | + $notice = ''; |
| 1843 | + } |
| 1844 | + } |
| 1845 | + |
| 1846 | + wfProfileOut( $fname ); |
| 1847 | + return $notice; |
| 1848 | +} |
| 1849 | + |
| 1850 | +function wfGetNamespaceNotice() { |
| 1851 | + global $wgTitle; |
| 1852 | + |
| 1853 | + # Paranoia |
| 1854 | + if ( !isset( $wgTitle ) || !is_object( $wgTitle ) ) |
| 1855 | + return ""; |
| 1856 | + |
| 1857 | + $fname = 'wfGetNamespaceNotice'; |
| 1858 | + wfProfileIn( $fname ); |
| 1859 | + |
| 1860 | + $key = "namespacenotice-" . $wgTitle->getNsText(); |
| 1861 | + $namespaceNotice = wfGetCachedNotice( $key ); |
| 1862 | + if ( $namespaceNotice && substr ( $namespaceNotice , 0 ,7 ) != "<p><" ) { |
| 1863 | + $namespaceNotice = '<div id="namespacebanner">' . $namespaceNotice . "</div>"; |
| 1864 | + } else { |
| 1865 | + $namespaceNotice = ""; |
| 1866 | + } |
| 1867 | + |
| 1868 | + wfProfileOut( $fname ); |
| 1869 | + return $namespaceNotice; |
| 1870 | +} |
| 1871 | + |
| 1872 | +function wfGetSiteNotice() { |
| 1873 | + global $wgUser, $wgSiteNotice; |
| 1874 | + $fname = 'wfGetSiteNotice'; |
| 1875 | + wfProfileIn( $fname ); |
| 1876 | + $siteNotice = ''; |
| 1877 | + |
| 1878 | + if( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice ) ) ) { |
| 1879 | + if( is_object( $wgUser ) && $wgUser->isLoggedIn() ) { |
| 1880 | + $siteNotice = wfGetCachedNotice( 'sitenotice' ); |
| 1881 | + } else { |
| 1882 | + $anonNotice = wfGetCachedNotice( 'anonnotice' ); |
| 1883 | + if( !$anonNotice ) { |
| 1884 | + $siteNotice = wfGetCachedNotice( 'sitenotice' ); |
| 1885 | + } else { |
| 1886 | + $siteNotice = $anonNotice; |
| 1887 | + } |
| 1888 | + } |
| 1889 | + if( !$siteNotice ) { |
| 1890 | + $siteNotice = wfGetCachedNotice( 'default' ); |
| 1891 | + } |
| 1892 | + } |
| 1893 | + |
| 1894 | + wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice ) ); |
| 1895 | + wfProfileOut( $fname ); |
| 1896 | + return $siteNotice; |
| 1897 | +} |
| 1898 | + |
| 1899 | +/** |
| 1900 | + * BC wrapper for MimeMagic::singleton() |
| 1901 | + * @deprecated |
| 1902 | + */ |
| 1903 | +function &wfGetMimeMagic() { |
| 1904 | + return MimeMagic::singleton(); |
| 1905 | +} |
| 1906 | + |
| 1907 | +/** |
| 1908 | + * Tries to get the system directory for temporary files. |
| 1909 | + * The TMPDIR, TMP, and TEMP environment variables are checked in sequence, |
| 1910 | + * and if none are set /tmp is returned as the generic Unix default. |
| 1911 | + * |
| 1912 | + * NOTE: When possible, use the tempfile() function to create temporary |
| 1913 | + * files to avoid race conditions on file creation, etc. |
| 1914 | + * |
| 1915 | + * @return string |
| 1916 | + */ |
| 1917 | +function wfTempDir() { |
| 1918 | + foreach( array( 'TMPDIR', 'TMP', 'TEMP' ) as $var ) { |
| 1919 | + $tmp = getenv( $var ); |
| 1920 | + if( $tmp && file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) { |
| 1921 | + return $tmp; |
| 1922 | + } |
| 1923 | + } |
| 1924 | + # Hope this is Unix of some kind! |
| 1925 | + return '/tmp'; |
| 1926 | +} |
| 1927 | + |
| 1928 | +/** |
| 1929 | + * Make directory, and make all parent directories if they don't exist |
| 1930 | + * |
| 1931 | + * @param string $dir Full path to directory to create |
| 1932 | + * @param int $mode Chmod value to use, default is $wgDirectoryMode |
| 1933 | + * @return bool |
| 1934 | + */ |
| 1935 | +function wfMkdirParents( $dir, $mode = null ) { |
| 1936 | + global $wgDirectoryMode; |
| 1937 | + |
| 1938 | + if( strval( $dir ) === '' || file_exists( $dir ) ) |
| 1939 | + return true; |
| 1940 | + |
| 1941 | + if ( is_null( $mode ) ) |
| 1942 | + $mode = $wgDirectoryMode; |
| 1943 | + |
| 1944 | + return mkdir( $dir, $mode, true ); // PHP5 <3 |
| 1945 | +} |
| 1946 | + |
| 1947 | +/** |
| 1948 | + * Increment a statistics counter |
| 1949 | + */ |
| 1950 | +function wfIncrStats( $key ) { |
| 1951 | + global $wgStatsMethod; |
| 1952 | + |
| 1953 | + if( $wgStatsMethod == 'udp' ) { |
| 1954 | + global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgDBname; |
| 1955 | + static $socket; |
| 1956 | + if (!$socket) { |
| 1957 | + $socket=socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); |
| 1958 | + $statline="stats/{$wgDBname} - 1 1 1 1 1 -total\n"; |
| 1959 | + socket_sendto($socket,$statline,strlen($statline),0,$wgUDPProfilerHost,$wgUDPProfilerPort); |
| 1960 | + } |
| 1961 | + $statline="stats/{$wgDBname} - 1 1 1 1 1 {$key}\n"; |
| 1962 | + @socket_sendto($socket,$statline,strlen($statline),0,$wgUDPProfilerHost,$wgUDPProfilerPort); |
| 1963 | + } elseif( $wgStatsMethod == 'cache' ) { |
| 1964 | + global $wgMemc; |
| 1965 | + $key = wfMemcKey( 'stats', $key ); |
| 1966 | + if ( is_null( $wgMemc->incr( $key ) ) ) { |
| 1967 | + $wgMemc->add( $key, 1 ); |
| 1968 | + } |
| 1969 | + } else { |
| 1970 | + // Disabled |
| 1971 | + } |
| 1972 | +} |
| 1973 | + |
| 1974 | +/** |
| 1975 | + * @param mixed $nr The number to format |
| 1976 | + * @param int $acc The number of digits after the decimal point, default 2 |
| 1977 | + * @param bool $round Whether or not to round the value, default true |
| 1978 | + * @return float |
| 1979 | + */ |
| 1980 | +function wfPercent( $nr, $acc = 2, $round = true ) { |
| 1981 | + $ret = sprintf( "%.${acc}f", $nr ); |
| 1982 | + return $round ? round( $ret, $acc ) . '%' : "$ret%"; |
| 1983 | +} |
| 1984 | + |
| 1985 | +/** |
| 1986 | + * Encrypt a username/password. |
| 1987 | + * |
| 1988 | + * @param string $userid ID of the user |
| 1989 | + * @param string $password Password of the user |
| 1990 | + * @return string Hashed password |
| 1991 | + * @deprecated Use User::crypt() or User::oldCrypt() instead |
| 1992 | + */ |
| 1993 | +function wfEncryptPassword( $userid, $password ) { |
| 1994 | + wfDeprecated(__FUNCTION__); |
| 1995 | + # Just wrap around User::oldCrypt() |
| 1996 | + return User::oldCrypt($password, $userid); |
| 1997 | +} |
| 1998 | + |
| 1999 | +/** |
| 2000 | + * Appends to second array if $value differs from that in $default |
| 2001 | + */ |
| 2002 | +function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) { |
| 2003 | + if ( is_null( $changed ) ) { |
| 2004 | + throw new MWException('GlobalFunctions::wfAppendToArrayIfNotDefault got null'); |
| 2005 | + } |
| 2006 | + if ( $default[$key] !== $value ) { |
| 2007 | + $changed[$key] = $value; |
| 2008 | + } |
| 2009 | +} |
| 2010 | + |
| 2011 | +/** |
| 2012 | + * Since wfMsg() and co suck, they don't return false if the message key they |
| 2013 | + * looked up didn't exist but a XHTML string, this function checks for the |
| 2014 | + * nonexistance of messages by looking at wfMsg() output |
| 2015 | + * |
| 2016 | + * @param $msg The message key looked up |
| 2017 | + * @param $wfMsgOut The output of wfMsg*() |
| 2018 | + * @return bool |
| 2019 | + */ |
| 2020 | +function wfEmptyMsg( $msg, $wfMsgOut ) { |
| 2021 | + return $wfMsgOut === htmlspecialchars( "<$msg>" ); |
| 2022 | +} |
| 2023 | + |
| 2024 | +/** |
| 2025 | + * Find out whether or not a mixed variable exists in a string |
| 2026 | + * |
| 2027 | + * @param mixed needle |
| 2028 | + * @param string haystack |
| 2029 | + * @return bool |
| 2030 | + */ |
| 2031 | +function in_string( $needle, $str ) { |
| 2032 | + return strpos( $str, $needle ) !== false; |
| 2033 | +} |
| 2034 | + |
| 2035 | +function wfSpecialList( $page, $details ) { |
| 2036 | + global $wgContLang; |
| 2037 | + $details = $details ? ' ' . $wgContLang->getDirMark() . "($details)" : ""; |
| 2038 | + return $page . $details; |
| 2039 | +} |
| 2040 | + |
| 2041 | +/** |
| 2042 | + * Returns a regular expression of url protocols |
| 2043 | + * |
| 2044 | + * @return string |
| 2045 | + */ |
| 2046 | +function wfUrlProtocols() { |
| 2047 | + global $wgUrlProtocols; |
| 2048 | + |
| 2049 | + // Support old-style $wgUrlProtocols strings, for backwards compatibility |
| 2050 | + // with LocalSettings files from 1.5 |
| 2051 | + if ( is_array( $wgUrlProtocols ) ) { |
| 2052 | + $protocols = array(); |
| 2053 | + foreach ($wgUrlProtocols as $protocol) |
| 2054 | + $protocols[] = preg_quote( $protocol, '/' ); |
| 2055 | + |
| 2056 | + return implode( '|', $protocols ); |
| 2057 | + } else { |
| 2058 | + return $wgUrlProtocols; |
| 2059 | + } |
| 2060 | +} |
| 2061 | + |
| 2062 | +/** |
| 2063 | + * Safety wrapper around ini_get() for boolean settings. |
| 2064 | + * The values returned from ini_get() are pre-normalized for settings |
| 2065 | + * set via php.ini or php_flag/php_admin_flag... but *not* |
| 2066 | + * for those set via php_value/php_admin_value. |
| 2067 | + * |
| 2068 | + * It's fairly common for people to use php_value instead of php_flag, |
| 2069 | + * which can leave you with an 'off' setting giving a false positive |
| 2070 | + * for code that just takes the ini_get() return value as a boolean. |
| 2071 | + * |
| 2072 | + * To make things extra interesting, setting via php_value accepts |
| 2073 | + * "true" and "yes" as true, but php.ini and php_flag consider them false. :) |
| 2074 | + * Unrecognized values go false... again opposite PHP's own coercion |
| 2075 | + * from string to bool. |
| 2076 | + * |
| 2077 | + * Luckily, 'properly' set settings will always come back as '0' or '1', |
| 2078 | + * so we only have to worry about them and the 'improper' settings. |
| 2079 | + * |
| 2080 | + * I frickin' hate PHP... :P |
| 2081 | + * |
| 2082 | + * @param string $setting |
| 2083 | + * @return bool |
| 2084 | + */ |
| 2085 | +function wfIniGetBool( $setting ) { |
| 2086 | + $val = ini_get( $setting ); |
| 2087 | + // 'on' and 'true' can't have whitespace around them, but '1' can. |
| 2088 | + return strtolower( $val ) == 'on' |
| 2089 | + || strtolower( $val ) == 'true' |
| 2090 | + || strtolower( $val ) == 'yes' |
| 2091 | + || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function |
| 2092 | +} |
| 2093 | + |
| 2094 | +/** |
| 2095 | + * Execute a shell command, with time and memory limits mirrored from the PHP |
| 2096 | + * configuration if supported. |
| 2097 | + * @param $cmd Command line, properly escaped for shell. |
| 2098 | + * @param &$retval optional, will receive the program's exit code. |
| 2099 | + * (non-zero is usually failure) |
| 2100 | + * @return collected stdout as a string (trailing newlines stripped) |
| 2101 | + */ |
| 2102 | +function wfShellExec( $cmd, &$retval=null ) { |
| 2103 | + global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime; |
| 2104 | + |
| 2105 | + if( wfIniGetBool( 'safe_mode' ) ) { |
| 2106 | + wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" ); |
| 2107 | + $retval = 1; |
| 2108 | + return "Unable to run external programs in safe mode."; |
| 2109 | + } |
| 2110 | + wfInitShellLocale(); |
| 2111 | + |
| 2112 | + if ( php_uname( 's' ) == 'Linux' ) { |
| 2113 | + $time = intval( $wgMaxShellTime ); |
| 2114 | + $mem = intval( $wgMaxShellMemory ); |
| 2115 | + $filesize = intval( $wgMaxShellFileSize ); |
| 2116 | + |
| 2117 | + if ( $time > 0 && $mem > 0 ) { |
| 2118 | + $script = "$IP/bin/ulimit4.sh"; |
| 2119 | + if ( is_executable( $script ) ) { |
| 2120 | + $cmd = escapeshellarg( $script ) . " $time $mem $filesize " . escapeshellarg( $cmd ); |
| 2121 | + } |
| 2122 | + } |
| 2123 | + } elseif ( php_uname( 's' ) == 'Windows NT' ) { |
| 2124 | + # This is a hack to work around PHP's flawed invocation of cmd.exe |
| 2125 | + # http://news.php.net/php.internals/21796 |
| 2126 | + $cmd = '"' . $cmd . '"'; |
| 2127 | + } |
| 2128 | + wfDebug( "wfShellExec: $cmd\n" ); |
| 2129 | + |
| 2130 | + $retval = 1; // error by default? |
| 2131 | + ob_start(); |
| 2132 | + passthru( $cmd, $retval ); |
| 2133 | + $output = ob_get_contents(); |
| 2134 | + ob_end_clean(); |
| 2135 | + |
| 2136 | + if ( $retval == 127 ) { |
| 2137 | + wfDebugLog( 'exec', "Possibly missing executable file: $cmd\n" ); |
| 2138 | + } |
| 2139 | + return $output; |
| 2140 | +} |
| 2141 | + |
| 2142 | +/** |
| 2143 | + * Workaround for http://bugs.php.net/bug.php?id=45132 |
| 2144 | + * escapeshellarg() destroys non-ASCII characters if LANG is not a UTF-8 locale |
| 2145 | + */ |
| 2146 | +function wfInitShellLocale() { |
| 2147 | + static $done = false; |
| 2148 | + if ( $done ) return; |
| 2149 | + $done = true; |
| 2150 | + global $wgShellLocale; |
| 2151 | + if ( !wfIniGetBool( 'safe_mode' ) ) { |
| 2152 | + putenv( "LC_CTYPE=$wgShellLocale" ); |
| 2153 | + setlocale( LC_CTYPE, $wgShellLocale ); |
| 2154 | + } |
| 2155 | +} |
| 2156 | + |
| 2157 | +/** |
| 2158 | + * This function works like "use VERSION" in Perl, the program will die with a |
| 2159 | + * backtrace if the current version of PHP is less than the version provided |
| 2160 | + * |
| 2161 | + * This is useful for extensions which due to their nature are not kept in sync |
| 2162 | + * with releases, and might depend on other versions of PHP than the main code |
| 2163 | + * |
| 2164 | + * Note: PHP might die due to parsing errors in some cases before it ever |
| 2165 | + * manages to call this function, such is life |
| 2166 | + * |
| 2167 | + * @see perldoc -f use |
| 2168 | + * |
| 2169 | + * @param mixed $version The version to check, can be a string, an integer, or |
| 2170 | + * a float |
| 2171 | + */ |
| 2172 | +function wfUsePHP( $req_ver ) { |
| 2173 | + $php_ver = PHP_VERSION; |
| 2174 | + |
| 2175 | + if ( version_compare( $php_ver, (string)$req_ver, '<' ) ) |
| 2176 | + throw new MWException( "PHP $req_ver required--this is only $php_ver" ); |
| 2177 | +} |
| 2178 | + |
| 2179 | +/** |
| 2180 | + * This function works like "use VERSION" in Perl except it checks the version |
| 2181 | + * of MediaWiki, the program will die with a backtrace if the current version |
| 2182 | + * of MediaWiki is less than the version provided. |
| 2183 | + * |
| 2184 | + * This is useful for extensions which due to their nature are not kept in sync |
| 2185 | + * with releases |
| 2186 | + * |
| 2187 | + * @see perldoc -f use |
| 2188 | + * |
| 2189 | + * @param mixed $version The version to check, can be a string, an integer, or |
| 2190 | + * a float |
| 2191 | + */ |
| 2192 | +function wfUseMW( $req_ver ) { |
| 2193 | + global $wgVersion; |
| 2194 | + |
| 2195 | + if ( version_compare( $wgVersion, (string)$req_ver, '<' ) ) |
| 2196 | + throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" ); |
| 2197 | +} |
| 2198 | + |
| 2199 | +/** |
| 2200 | + * @deprecated use StringUtils::escapeRegexReplacement |
| 2201 | + */ |
| 2202 | +function wfRegexReplacement( $string ) { |
| 2203 | + return StringUtils::escapeRegexReplacement( $string ); |
| 2204 | +} |
| 2205 | + |
| 2206 | +/** |
| 2207 | + * Return the final portion of a pathname. |
| 2208 | + * Reimplemented because PHP5's basename() is buggy with multibyte text. |
| 2209 | + * http://bugs.php.net/bug.php?id=33898 |
| 2210 | + * |
| 2211 | + * PHP's basename() only considers '\' a pathchar on Windows and Netware. |
| 2212 | + * We'll consider it so always, as we don't want \s in our Unix paths either. |
| 2213 | + * |
| 2214 | + * @param string $path |
| 2215 | + * @param string $suffix to remove if present |
| 2216 | + * @return string |
| 2217 | + */ |
| 2218 | +function wfBaseName( $path, $suffix='' ) { |
| 2219 | + $encSuffix = ($suffix == '') |
| 2220 | + ? '' |
| 2221 | + : ( '(?:' . preg_quote( $suffix, '#' ) . ')?' ); |
| 2222 | + $matches = array(); |
| 2223 | + if( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) { |
| 2224 | + return $matches[1]; |
| 2225 | + } else { |
| 2226 | + return ''; |
| 2227 | + } |
| 2228 | +} |
| 2229 | + |
| 2230 | +/** |
| 2231 | + * Generate a relative path name to the given file. |
| 2232 | + * May explode on non-matching case-insensitive paths, |
| 2233 | + * funky symlinks, etc. |
| 2234 | + * |
| 2235 | + * @param string $path Absolute destination path including target filename |
| 2236 | + * @param string $from Absolute source path, directory only |
| 2237 | + * @return string |
| 2238 | + */ |
| 2239 | +function wfRelativePath( $path, $from ) { |
| 2240 | + // Normalize mixed input on Windows... |
| 2241 | + $path = str_replace( '/', DIRECTORY_SEPARATOR, $path ); |
| 2242 | + $from = str_replace( '/', DIRECTORY_SEPARATOR, $from ); |
| 2243 | + |
| 2244 | + // Trim trailing slashes -- fix for drive root |
| 2245 | + $path = rtrim( $path, DIRECTORY_SEPARATOR ); |
| 2246 | + $from = rtrim( $from, DIRECTORY_SEPARATOR ); |
| 2247 | + |
| 2248 | + $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) ); |
| 2249 | + $against = explode( DIRECTORY_SEPARATOR, $from ); |
| 2250 | + |
| 2251 | + if( $pieces[0] !== $against[0] ) { |
| 2252 | + // Non-matching Windows drive letters? |
| 2253 | + // Return a full path. |
| 2254 | + return $path; |
| 2255 | + } |
| 2256 | + |
| 2257 | + // Trim off common prefix |
| 2258 | + while( count( $pieces ) && count( $against ) |
| 2259 | + && $pieces[0] == $against[0] ) { |
| 2260 | + array_shift( $pieces ); |
| 2261 | + array_shift( $against ); |
| 2262 | + } |
| 2263 | + |
| 2264 | + // relative dots to bump us to the parent |
| 2265 | + while( count( $against ) ) { |
| 2266 | + array_unshift( $pieces, '..' ); |
| 2267 | + array_shift( $against ); |
| 2268 | + } |
| 2269 | + |
| 2270 | + array_push( $pieces, wfBaseName( $path ) ); |
| 2271 | + |
| 2272 | + return implode( DIRECTORY_SEPARATOR, $pieces ); |
| 2273 | +} |
| 2274 | + |
| 2275 | +/** |
| 2276 | + * Backwards array plus for people who haven't bothered to read the PHP manual |
| 2277 | + * XXX: will not darn your socks for you. |
| 2278 | + * |
| 2279 | + * @param array $array1, [$array2, [...]] |
| 2280 | + * @return array |
| 2281 | + */ |
| 2282 | +function wfArrayMerge( $array1/* ... */ ) { |
| 2283 | + $args = func_get_args(); |
| 2284 | + $args = array_reverse( $args, true ); |
| 2285 | + $out = array(); |
| 2286 | + foreach ( $args as $arg ) { |
| 2287 | + $out += $arg; |
| 2288 | + } |
| 2289 | + return $out; |
| 2290 | +} |
| 2291 | + |
| 2292 | +/** |
| 2293 | + * Merge arrays in the style of getUserPermissionsErrors, with duplicate removal |
| 2294 | + * e.g. |
| 2295 | + * wfMergeErrorArrays( |
| 2296 | + * array( array( 'x' ) ), |
| 2297 | + * array( array( 'x', '2' ) ), |
| 2298 | + * array( array( 'x' ) ), |
| 2299 | + * array( array( 'y') ) |
| 2300 | + * ); |
| 2301 | + * returns: |
| 2302 | + * array( |
| 2303 | + * array( 'x', '2' ), |
| 2304 | + * array( 'x' ), |
| 2305 | + * array( 'y' ) |
| 2306 | + * ) |
| 2307 | + */ |
| 2308 | +function wfMergeErrorArrays(/*...*/) { |
| 2309 | + $args = func_get_args(); |
| 2310 | + $out = array(); |
| 2311 | + foreach ( $args as $errors ) { |
| 2312 | + foreach ( $errors as $params ) { |
| 2313 | + $spec = implode( "\t", $params ); |
| 2314 | + $out[$spec] = $params; |
| 2315 | + } |
| 2316 | + } |
| 2317 | + return array_values( $out ); |
| 2318 | +} |
| 2319 | + |
| 2320 | +/** |
| 2321 | + * Make a URL index, appropriate for the el_index field of externallinks. |
| 2322 | + */ |
| 2323 | +function wfMakeUrlIndex( $url ) { |
| 2324 | + global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php |
| 2325 | + wfSuppressWarnings(); |
| 2326 | + $bits = parse_url( $url ); |
| 2327 | + wfRestoreWarnings(); |
| 2328 | + if ( !$bits ) { |
| 2329 | + return false; |
| 2330 | + } |
| 2331 | + // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it |
| 2332 | + $delimiter = ''; |
| 2333 | + if ( in_array( $bits['scheme'] . '://' , $wgUrlProtocols ) ) { |
| 2334 | + $delimiter = '://'; |
| 2335 | + } elseif ( in_array( $bits['scheme'] .':' , $wgUrlProtocols ) ) { |
| 2336 | + $delimiter = ':'; |
| 2337 | + // parse_url detects for news: and mailto: the host part of an url as path |
| 2338 | + // We have to correct this wrong detection |
| 2339 | + if ( isset ( $bits['path'] ) ) { |
| 2340 | + $bits['host'] = $bits['path']; |
| 2341 | + $bits['path'] = ''; |
| 2342 | + } |
| 2343 | + } else { |
| 2344 | + return false; |
| 2345 | + } |
| 2346 | + |
| 2347 | + // Reverse the labels in the hostname, convert to lower case |
| 2348 | + // For emails reverse domainpart only |
| 2349 | + if ( $bits['scheme'] == 'mailto' ) { |
| 2350 | + $mailparts = explode( '@', $bits['host'], 2 ); |
| 2351 | + if ( count($mailparts) === 2 ) { |
| 2352 | + $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) ); |
| 2353 | + } else { |
| 2354 | + // No domain specified, don't mangle it |
| 2355 | + $domainpart = ''; |
| 2356 | + } |
| 2357 | + $reversedHost = $domainpart . '@' . $mailparts[0]; |
| 2358 | + } else { |
| 2359 | + $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) ); |
| 2360 | + } |
| 2361 | + // Add an extra dot to the end |
| 2362 | + // Why? Is it in wrong place in mailto links? |
| 2363 | + if ( substr( $reversedHost, -1, 1 ) !== '.' ) { |
| 2364 | + $reversedHost .= '.'; |
| 2365 | + } |
| 2366 | + // Reconstruct the pseudo-URL |
| 2367 | + $prot = $bits['scheme']; |
| 2368 | + $index = "$prot$delimiter$reversedHost"; |
| 2369 | + // Leave out user and password. Add the port, path, query and fragment |
| 2370 | + if ( isset( $bits['port'] ) ) $index .= ':' . $bits['port']; |
| 2371 | + if ( isset( $bits['path'] ) ) { |
| 2372 | + $index .= $bits['path']; |
| 2373 | + } else { |
| 2374 | + $index .= '/'; |
| 2375 | + } |
| 2376 | + if ( isset( $bits['query'] ) ) $index .= '?' . $bits['query']; |
| 2377 | + if ( isset( $bits['fragment'] ) ) $index .= '#' . $bits['fragment']; |
| 2378 | + return $index; |
| 2379 | +} |
| 2380 | + |
| 2381 | +/** |
| 2382 | + * Do any deferred updates and clear the list |
| 2383 | + * TODO: This could be in Wiki.php if that class made any sense at all |
| 2384 | + */ |
| 2385 | +function wfDoUpdates() |
| 2386 | +{ |
| 2387 | + global $wgPostCommitUpdateList, $wgDeferredUpdateList; |
| 2388 | + foreach ( $wgDeferredUpdateList as $update ) { |
| 2389 | + $update->doUpdate(); |
| 2390 | + } |
| 2391 | + foreach ( $wgPostCommitUpdateList as $update ) { |
| 2392 | + $update->doUpdate(); |
| 2393 | + } |
| 2394 | + $wgDeferredUpdateList = array(); |
| 2395 | + $wgPostCommitUpdateList = array(); |
| 2396 | +} |
| 2397 | + |
| 2398 | +/** |
| 2399 | + * @deprecated use StringUtils::explodeMarkup |
| 2400 | + */ |
| 2401 | +function wfExplodeMarkup( $separator, $text ) { |
| 2402 | + return StringUtils::explodeMarkup( $separator, $text ); |
| 2403 | +} |
| 2404 | + |
| 2405 | +/** |
| 2406 | + * Convert an arbitrarily-long digit string from one numeric base |
| 2407 | + * to another, optionally zero-padding to a minimum column width. |
| 2408 | + * |
| 2409 | + * Supports base 2 through 36; digit values 10-36 are represented |
| 2410 | + * as lowercase letters a-z. Input is case-insensitive. |
| 2411 | + * |
| 2412 | + * @param $input string of digits |
| 2413 | + * @param $sourceBase int 2-36 |
| 2414 | + * @param $destBase int 2-36 |
| 2415 | + * @param $pad int 1 or greater |
| 2416 | + * @param $lowercase bool |
| 2417 | + * @return string or false on invalid input |
| 2418 | + */ |
| 2419 | +function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true ) { |
| 2420 | + $input = strval( $input ); |
| 2421 | + if( $sourceBase < 2 || |
| 2422 | + $sourceBase > 36 || |
| 2423 | + $destBase < 2 || |
| 2424 | + $destBase > 36 || |
| 2425 | + $pad < 1 || |
| 2426 | + $sourceBase != intval( $sourceBase ) || |
| 2427 | + $destBase != intval( $destBase ) || |
| 2428 | + $pad != intval( $pad ) || |
| 2429 | + !is_string( $input ) || |
| 2430 | + $input == '' ) { |
| 2431 | + return false; |
| 2432 | + } |
| 2433 | + $digitChars = ( $lowercase ) ? '0123456789abcdefghijklmnopqrstuvwxyz' : '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
| 2434 | + $inDigits = array(); |
| 2435 | + $outChars = ''; |
| 2436 | + |
| 2437 | + // Decode and validate input string |
| 2438 | + $input = strtolower( $input ); |
| 2439 | + for( $i = 0; $i < strlen( $input ); $i++ ) { |
| 2440 | + $n = strpos( $digitChars, $input{$i} ); |
| 2441 | + if( $n === false || $n > $sourceBase ) { |
| 2442 | + return false; |
| 2443 | + } |
| 2444 | + $inDigits[] = $n; |
| 2445 | + } |
| 2446 | + |
| 2447 | + // Iterate over the input, modulo-ing out an output digit |
| 2448 | + // at a time until input is gone. |
| 2449 | + while( count( $inDigits ) ) { |
| 2450 | + $work = 0; |
| 2451 | + $workDigits = array(); |
| 2452 | + |
| 2453 | + // Long division... |
| 2454 | + foreach( $inDigits as $digit ) { |
| 2455 | + $work *= $sourceBase; |
| 2456 | + $work += $digit; |
| 2457 | + |
| 2458 | + if( $work < $destBase ) { |
| 2459 | + // Gonna need to pull another digit. |
| 2460 | + if( count( $workDigits ) ) { |
| 2461 | + // Avoid zero-padding; this lets us find |
| 2462 | + // the end of the input very easily when |
| 2463 | + // length drops to zero. |
| 2464 | + $workDigits[] = 0; |
| 2465 | + } |
| 2466 | + } else { |
| 2467 | + // Finally! Actual division! |
| 2468 | + $workDigits[] = intval( $work / $destBase ); |
| 2469 | + |
| 2470 | + // Isn't it annoying that most programming languages |
| 2471 | + // don't have a single divide-and-remainder operator, |
| 2472 | + // even though the CPU implements it that way? |
| 2473 | + $work = $work % $destBase; |
| 2474 | + } |
| 2475 | + } |
| 2476 | + |
| 2477 | + // All that division leaves us with a remainder, |
| 2478 | + // which is conveniently our next output digit. |
| 2479 | + $outChars .= $digitChars[$work]; |
| 2480 | + |
| 2481 | + // And we continue! |
| 2482 | + $inDigits = $workDigits; |
| 2483 | + } |
| 2484 | + |
| 2485 | + while( strlen( $outChars ) < $pad ) { |
| 2486 | + $outChars .= '0'; |
| 2487 | + } |
| 2488 | + |
| 2489 | + return strrev( $outChars ); |
| 2490 | +} |
| 2491 | + |
| 2492 | +/** |
| 2493 | + * Create an object with a given name and an array of construct parameters |
| 2494 | + * @param string $name |
| 2495 | + * @param array $p parameters |
| 2496 | + */ |
| 2497 | +function wfCreateObject( $name, $p ){ |
| 2498 | + $p = array_values( $p ); |
| 2499 | + switch ( count( $p ) ) { |
| 2500 | + case 0: |
| 2501 | + return new $name; |
| 2502 | + case 1: |
| 2503 | + return new $name( $p[0] ); |
| 2504 | + case 2: |
| 2505 | + return new $name( $p[0], $p[1] ); |
| 2506 | + case 3: |
| 2507 | + return new $name( $p[0], $p[1], $p[2] ); |
| 2508 | + case 4: |
| 2509 | + return new $name( $p[0], $p[1], $p[2], $p[3] ); |
| 2510 | + case 5: |
| 2511 | + return new $name( $p[0], $p[1], $p[2], $p[3], $p[4] ); |
| 2512 | + case 6: |
| 2513 | + return new $name( $p[0], $p[1], $p[2], $p[3], $p[4], $p[5] ); |
| 2514 | + default: |
| 2515 | + throw new MWException( "Too many arguments to construtor in wfCreateObject" ); |
| 2516 | + } |
| 2517 | +} |
| 2518 | + |
| 2519 | +/** |
| 2520 | + * Alias for modularized function |
| 2521 | + * @deprecated Use Http::get() instead |
| 2522 | + */ |
| 2523 | +function wfGetHTTP( $url, $timeout = 'default' ) { |
| 2524 | + wfDeprecated(__FUNCTION__); |
| 2525 | + return Http::get( $url, $timeout ); |
| 2526 | +} |
| 2527 | + |
| 2528 | +/** |
| 2529 | + * Alias for modularized function |
| 2530 | + * @deprecated Use Http::isLocalURL() instead |
| 2531 | + */ |
| 2532 | +function wfIsLocalURL( $url ) { |
| 2533 | + wfDeprecated(__FUNCTION__); |
| 2534 | + return Http::isLocalURL( $url ); |
| 2535 | +} |
| 2536 | + |
| 2537 | +function wfHttpOnlySafe() { |
| 2538 | + global $wgHttpOnlyBlacklist; |
| 2539 | + if( !version_compare("5.2", PHP_VERSION, "<") ) |
| 2540 | + return false; |
| 2541 | + |
| 2542 | + if( isset( $_SERVER['HTTP_USER_AGENT'] ) ) { |
| 2543 | + foreach( $wgHttpOnlyBlacklist as $regex ) { |
| 2544 | + if( preg_match( $regex, $_SERVER['HTTP_USER_AGENT'] ) ) { |
| 2545 | + return false; |
| 2546 | + } |
| 2547 | + } |
| 2548 | + } |
| 2549 | + |
| 2550 | + return true; |
| 2551 | +} |
| 2552 | + |
| 2553 | +/** |
| 2554 | + * Initialise php session |
| 2555 | + */ |
| 2556 | +function wfSetupSession() { |
| 2557 | + global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly; |
| 2558 | + if( $wgSessionsInMemcached ) { |
| 2559 | + require_once( 'MemcachedSessions.php' ); |
| 2560 | + } elseif( 'files' != ini_get( 'session.save_handler' ) ) { |
| 2561 | + # If it's left on 'user' or another setting from another |
| 2562 | + # application, it will end up failing. Try to recover. |
| 2563 | + ini_set ( 'session.save_handler', 'files' ); |
| 2564 | + } |
| 2565 | + $httpOnlySafe = wfHttpOnlySafe(); |
| 2566 | + wfDebugLog( 'cookie', |
| 2567 | + 'session_set_cookie_params: "' . implode( '", "', |
| 2568 | + array( |
| 2569 | + 0, |
| 2570 | + $wgCookiePath, |
| 2571 | + $wgCookieDomain, |
| 2572 | + $wgCookieSecure, |
| 2573 | + $httpOnlySafe && $wgCookieHttpOnly ) ) . '"' ); |
| 2574 | + if( $httpOnlySafe && $wgCookieHttpOnly ) { |
| 2575 | + session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly ); |
| 2576 | + } else { |
| 2577 | + // PHP 5.1 throws warnings if you pass the HttpOnly parameter for 5.2. |
| 2578 | + session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); |
| 2579 | + } |
| 2580 | + session_cache_limiter( 'private, must-revalidate' ); |
| 2581 | + wfSuppressWarnings(); |
| 2582 | + session_start(); |
| 2583 | + wfRestoreWarnings(); |
| 2584 | +} |
| 2585 | + |
| 2586 | +/** |
| 2587 | + * Get an object from the precompiled serialized directory |
| 2588 | + * |
| 2589 | + * @return mixed The variable on success, false on failure |
| 2590 | + */ |
| 2591 | +function wfGetPrecompiledData( $name ) { |
| 2592 | + global $IP; |
| 2593 | + |
| 2594 | + $file = "$IP/serialized/$name"; |
| 2595 | + if ( file_exists( $file ) ) { |
| 2596 | + $blob = file_get_contents( $file ); |
| 2597 | + if ( $blob ) { |
| 2598 | + return unserialize( $blob ); |
| 2599 | + } |
| 2600 | + } |
| 2601 | + return false; |
| 2602 | +} |
| 2603 | + |
| 2604 | +function wfGetCaller( $level = 2 ) { |
| 2605 | + $backtrace = wfDebugBacktrace(); |
| 2606 | + if ( isset( $backtrace[$level] ) ) { |
| 2607 | + return wfFormatStackFrame($backtrace[$level]); |
| 2608 | + } else { |
| 2609 | + $caller = 'unknown'; |
| 2610 | + } |
| 2611 | + return $caller; |
| 2612 | +} |
| 2613 | + |
| 2614 | +/** Return a string consisting all callers in stack, somewhat useful sometimes for profiling specific points */ |
| 2615 | +function wfGetAllCallers() { |
| 2616 | + return implode('/', array_map('wfFormatStackFrame',array_reverse(wfDebugBacktrace()))); |
| 2617 | +} |
| 2618 | + |
| 2619 | +/** Return a string representation of frame */ |
| 2620 | +function wfFormatStackFrame($frame) { |
| 2621 | + return isset( $frame["class"] )? |
| 2622 | + $frame["class"]."::".$frame["function"]: |
| 2623 | + $frame["function"]; |
| 2624 | +} |
| 2625 | + |
| 2626 | +/** |
| 2627 | + * Get a cache key |
| 2628 | + */ |
| 2629 | +function wfMemcKey( /*... */ ) { |
| 2630 | + $args = func_get_args(); |
| 2631 | + $key = wfWikiID() . ':' . implode( ':', $args ); |
| 2632 | + return $key; |
| 2633 | +} |
| 2634 | + |
| 2635 | +/** |
| 2636 | + * Get a cache key for a foreign DB |
| 2637 | + */ |
| 2638 | +function wfForeignMemcKey( $db, $prefix /*, ... */ ) { |
| 2639 | + $args = array_slice( func_get_args(), 2 ); |
| 2640 | + if ( $prefix ) { |
| 2641 | + $key = "$db-$prefix:" . implode( ':', $args ); |
| 2642 | + } else { |
| 2643 | + $key = $db . ':' . implode( ':', $args ); |
| 2644 | + } |
| 2645 | + return $key; |
| 2646 | +} |
| 2647 | + |
| 2648 | +/** |
| 2649 | + * Get an ASCII string identifying this wiki |
| 2650 | + * This is used as a prefix in memcached keys |
| 2651 | + */ |
| 2652 | +function wfWikiID( $db = null ) { |
| 2653 | + if( $db instanceof Database ) { |
| 2654 | + return $db->getWikiID(); |
| 2655 | + } else { |
| 2656 | + global $wgDBprefix, $wgDBname; |
| 2657 | + if ( $wgDBprefix ) { |
| 2658 | + return "$wgDBname-$wgDBprefix"; |
| 2659 | + } else { |
| 2660 | + return $wgDBname; |
| 2661 | + } |
| 2662 | + } |
| 2663 | +} |
| 2664 | + |
| 2665 | +/** |
| 2666 | + * Split a wiki ID into DB name and table prefix |
| 2667 | + */ |
| 2668 | +function wfSplitWikiID( $wiki ) { |
| 2669 | + $bits = explode( '-', $wiki, 2 ); |
| 2670 | + if ( count( $bits ) < 2 ) { |
| 2671 | + $bits[] = ''; |
| 2672 | + } |
| 2673 | + return $bits; |
| 2674 | +} |
| 2675 | + |
| 2676 | +/* |
| 2677 | + * Get a Database object. |
| 2678 | + * @param integer $db Index of the connection to get. May be DB_MASTER for the |
| 2679 | + * master (for write queries), DB_SLAVE for potentially lagged |
| 2680 | + * read queries, or an integer >= 0 for a particular server. |
| 2681 | + * |
| 2682 | + * @param mixed $groups Query groups. An array of group names that this query |
| 2683 | + * belongs to. May contain a single string if the query is only |
| 2684 | + * in one group. |
| 2685 | + * |
| 2686 | + * @param string $wiki The wiki ID, or false for the current wiki |
| 2687 | + * |
| 2688 | + * Note: multiple calls to wfGetDB(DB_SLAVE) during the course of one request |
| 2689 | + * will always return the same object, unless the underlying connection or load |
| 2690 | + * balancer is manually destroyed. |
| 2691 | + */ |
| 2692 | +function &wfGetDB( $db, $groups = array(), $wiki = false ) { |
| 2693 | + return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki ); |
| 2694 | +} |
| 2695 | + |
| 2696 | +/** |
| 2697 | + * Get a load balancer object. |
| 2698 | + * |
| 2699 | + * @param array $groups List of query groups |
| 2700 | + * @param string $wiki Wiki ID, or false for the current wiki |
| 2701 | + * @return LoadBalancer |
| 2702 | + */ |
| 2703 | +function wfGetLB( $wiki = false ) { |
| 2704 | + return wfGetLBFactory()->getMainLB( $wiki ); |
| 2705 | +} |
| 2706 | + |
| 2707 | +/** |
| 2708 | + * Get the load balancer factory object |
| 2709 | + */ |
| 2710 | +function &wfGetLBFactory() { |
| 2711 | + return LBFactory::singleton(); |
| 2712 | +} |
| 2713 | + |
| 2714 | +/** |
| 2715 | + * Find a file. |
| 2716 | + * Shortcut for RepoGroup::singleton()->findFile() |
| 2717 | + * @param mixed $title Title object or string. May be interwiki. |
| 2718 | + * @param mixed $time Requested time for an archived image, or false for the |
| 2719 | + * current version. An image object will be returned which |
| 2720 | + * was created at the specified time. |
| 2721 | + * @param mixed $flags FileRepo::FIND_ flags |
| 2722 | + * @param boolean $bypass Bypass the file cache even if it could be used |
| 2723 | + * @return File, or false if the file does not exist |
| 2724 | + */ |
| 2725 | +function wfFindFile( $title, $time = false, $flags = 0, $bypass = false ) { |
| 2726 | + if( !$time && !$flags && !$bypass ) { |
| 2727 | + return FileCache::singleton()->findFile( $title ); |
| 2728 | + } else { |
| 2729 | + return RepoGroup::singleton()->findFile( $title, $time, $flags ); |
| 2730 | + } |
| 2731 | +} |
| 2732 | + |
| 2733 | +/** |
| 2734 | + * Get an object referring to a locally registered file. |
| 2735 | + * Returns a valid placeholder object if the file does not exist. |
| 2736 | + */ |
| 2737 | +function wfLocalFile( $title ) { |
| 2738 | + return RepoGroup::singleton()->getLocalRepo()->newFile( $title ); |
| 2739 | +} |
| 2740 | + |
| 2741 | +/** |
| 2742 | + * Should low-performance queries be disabled? |
| 2743 | + * |
| 2744 | + * @return bool |
| 2745 | + */ |
| 2746 | +function wfQueriesMustScale() { |
| 2747 | + global $wgMiserMode; |
| 2748 | + return $wgMiserMode |
| 2749 | + || ( SiteStats::pages() > 100000 |
| 2750 | + && SiteStats::edits() > 1000000 |
| 2751 | + && SiteStats::users() > 10000 ); |
| 2752 | +} |
| 2753 | + |
| 2754 | +/** |
| 2755 | + * Get the path to a specified script file, respecting file |
| 2756 | + * extensions; this is a wrapper around $wgScriptExtension etc. |
| 2757 | + * |
| 2758 | + * @param string $script Script filename, sans extension |
| 2759 | + * @return string |
| 2760 | + */ |
| 2761 | +function wfScript( $script = 'index' ) { |
| 2762 | + global $wgScriptPath, $wgScriptExtension; |
| 2763 | + return "{$wgScriptPath}/{$script}{$wgScriptExtension}"; |
| 2764 | +} |
| 2765 | + |
| 2766 | +/** |
| 2767 | + * Convenience function converts boolean values into "true" |
| 2768 | + * or "false" (string) values |
| 2769 | + * |
| 2770 | + * @param bool $value |
| 2771 | + * @return string |
| 2772 | + */ |
| 2773 | +function wfBoolToStr( $value ) { |
| 2774 | + return $value ? 'true' : 'false'; |
| 2775 | +} |
| 2776 | + |
| 2777 | +/** |
| 2778 | + * Load an extension messages file |
| 2779 | + * |
| 2780 | + * @param string $extensionName Name of extension to load messages from\for. |
| 2781 | + * @param string $langcode Language to load messages for, or false for default |
| 2782 | + * behvaiour (en, content language and user language). |
| 2783 | + * @since r24808 (v1.11) Using this method of loading extension messages will not work |
| 2784 | + * on MediaWiki prior to that |
| 2785 | + */ |
| 2786 | +function wfLoadExtensionMessages( $extensionName, $langcode = false ) { |
| 2787 | + global $wgExtensionMessagesFiles, $wgMessageCache, $wgLang, $wgContLang; |
| 2788 | + |
| 2789 | + #For recording whether extension message files have been loaded in a given language. |
| 2790 | + static $loaded = array(); |
| 2791 | + |
| 2792 | + if( !array_key_exists( $extensionName, $loaded ) ) { |
| 2793 | + $loaded[$extensionName] = array(); |
| 2794 | + } |
| 2795 | + |
| 2796 | + if ( !isset($wgExtensionMessagesFiles[$extensionName]) ) { |
| 2797 | + throw new MWException( "Messages file for extensions $extensionName is not defined" ); |
| 2798 | + } |
| 2799 | + |
| 2800 | + if( !$langcode && !array_key_exists( '*', $loaded[$extensionName] ) ) { |
| 2801 | + # Just do en, content language and user language. |
| 2802 | + $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName], false ); |
| 2803 | + # Mark that they have been loaded. |
| 2804 | + $loaded[$extensionName]['en'] = true; |
| 2805 | + $loaded[$extensionName][$wgLang->getCode()] = true; |
| 2806 | + $loaded[$extensionName][$wgContLang->getCode()] = true; |
| 2807 | + # Mark that this part has been done to avoid weird if statements. |
| 2808 | + $loaded[$extensionName]['*'] = true; |
| 2809 | + } elseif( is_string( $langcode ) && !array_key_exists( $langcode, $loaded[$extensionName] ) ) { |
| 2810 | + # Load messages for specified language. |
| 2811 | + $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName], $langcode ); |
| 2812 | + # Mark that they have been loaded. |
| 2813 | + $loaded[$extensionName][$langcode] = true; |
| 2814 | + } |
| 2815 | +} |
| 2816 | + |
| 2817 | +/** |
| 2818 | + * Get a platform-independent path to the null file, e.g. |
| 2819 | + * /dev/null |
| 2820 | + * |
| 2821 | + * @return string |
| 2822 | + */ |
| 2823 | +function wfGetNull() { |
| 2824 | + return wfIsWindows() |
| 2825 | + ? 'NUL' |
| 2826 | + : '/dev/null'; |
| 2827 | +} |
| 2828 | + |
| 2829 | +/** |
| 2830 | + * Displays a maxlag error |
| 2831 | + * |
| 2832 | + * @param string $host Server that lags the most |
| 2833 | + * @param int $lag Maxlag (actual) |
| 2834 | + * @param int $maxLag Maxlag (requested) |
| 2835 | + */ |
| 2836 | +function wfMaxlagError( $host, $lag, $maxLag ) { |
| 2837 | + global $wgShowHostnames; |
| 2838 | + header( 'HTTP/1.1 503 Service Unavailable' ); |
| 2839 | + header( 'Retry-After: ' . max( intval( $maxLag ), 5 ) ); |
| 2840 | + header( 'X-Database-Lag: ' . intval( $lag ) ); |
| 2841 | + header( 'Content-Type: text/plain' ); |
| 2842 | + if( $wgShowHostnames ) { |
| 2843 | + echo "Waiting for $host: $lag seconds lagged\n"; |
| 2844 | + } else { |
| 2845 | + echo "Waiting for a database server: $lag seconds lagged\n"; |
| 2846 | + } |
| 2847 | +} |
| 2848 | + |
| 2849 | +/** |
| 2850 | + * Throws an E_USER_NOTICE saying that $function is deprecated |
| 2851 | + * @param string $function |
| 2852 | + * @return null |
| 2853 | + */ |
| 2854 | +function wfDeprecated( $function ) { |
| 2855 | + global $wgDebugLogFile; |
| 2856 | + if ( !$wgDebugLogFile ) { |
| 2857 | + return; |
| 2858 | + } |
| 2859 | + $callers = wfDebugBacktrace(); |
| 2860 | + if( isset( $callers[2] ) ){ |
| 2861 | + $callerfunc = $callers[2]; |
| 2862 | + $callerfile = $callers[1]; |
| 2863 | + if( isset( $callerfile['file'] ) && isset( $callerfile['line'] ) ){ |
| 2864 | + $file = $callerfile['file'] . ' at line ' . $callerfile['line']; |
| 2865 | + } else { |
| 2866 | + $file = '(internal function)'; |
| 2867 | + } |
| 2868 | + $func = ''; |
| 2869 | + if( isset( $callerfunc['class'] ) ) |
| 2870 | + $func .= $callerfunc['class'] . '::'; |
| 2871 | + $func .= @$callerfunc['function']; |
| 2872 | + $msg = "Use of $function is deprecated. Called from $func in $file"; |
| 2873 | + } else { |
| 2874 | + $msg = "Use of $function is deprecated."; |
| 2875 | + } |
| 2876 | + wfDebug( "$msg\n" ); |
| 2877 | +} |
| 2878 | + |
| 2879 | +/** |
| 2880 | + * Sleep until the worst slave's replication lag is less than or equal to |
| 2881 | + * $maxLag, in seconds. Use this when updating very large numbers of rows, as |
| 2882 | + * in maintenance scripts, to avoid causing too much lag. Of course, this is |
| 2883 | + * a no-op if there are no slaves. |
| 2884 | + * |
| 2885 | + * Every time the function has to wait for a slave, it will print a message to |
| 2886 | + * that effect (and then sleep for a little while), so it's probably not best |
| 2887 | + * to use this outside maintenance scripts in its present form. |
| 2888 | + * |
| 2889 | + * @param int $maxLag |
| 2890 | + * @return null |
| 2891 | + */ |
| 2892 | +function wfWaitForSlaves( $maxLag ) { |
| 2893 | + if( $maxLag ) { |
| 2894 | + $lb = wfGetLB(); |
| 2895 | + list( $host, $lag ) = $lb->getMaxLag(); |
| 2896 | + while( $lag > $maxLag ) { |
| 2897 | + $name = @gethostbyaddr( $host ); |
| 2898 | + if( $name !== false ) { |
| 2899 | + $host = $name; |
| 2900 | + } |
| 2901 | + print "Waiting for $host (lagged $lag seconds)...\n"; |
| 2902 | + sleep($maxLag); |
| 2903 | + list( $host, $lag ) = $lb->getMaxLag(); |
| 2904 | + } |
| 2905 | + } |
| 2906 | +} |
| 2907 | + |
| 2908 | +/** |
| 2909 | + * Output some plain text in command-line mode or in the installer (updaters.inc). |
| 2910 | + * Do not use it in any other context, its behaviour is subject to change. |
| 2911 | + */ |
| 2912 | +function wfOut( $s ) { |
| 2913 | + static $lineStarted = false; |
| 2914 | + global $wgCommandLineMode; |
| 2915 | + if ( $wgCommandLineMode && !defined( 'MEDIAWIKI_INSTALL' ) ) { |
| 2916 | + echo $s; |
| 2917 | + } else { |
| 2918 | + echo htmlspecialchars( $s ); |
| 2919 | + } |
| 2920 | + flush(); |
| 2921 | +} |
| 2922 | + |
| 2923 | +/** Generate a random 32-character hexadecimal token. |
| 2924 | + * @param mixed $salt Some sort of salt, if necessary, to add to random characters before hashing. |
| 2925 | + */ |
| 2926 | +function wfGenerateToken( $salt = '' ) { |
| 2927 | + $salt = serialize($salt); |
| 2928 | + |
| 2929 | + return md5( mt_rand( 0, 0x7fffffff ) . $salt ); |
| 2930 | +} |
| 2931 | + |
| 2932 | +/** |
| 2933 | + * Replace all invalid characters with - |
| 2934 | + * @param mixed $title Filename to process |
| 2935 | + */ |
| 2936 | +function wfStripIllegalFilenameChars( $name ) { |
| 2937 | + $name = wfBaseName( $name ); |
| 2938 | + $name = preg_replace ( "/[^".Title::legalChars()."]/", '-', $name ); |
| 2939 | + return $name; |
| 2940 | +} |
Property changes on: trunk/extensions/NSFileRepo/REL1_14_0/phase3/includes/GlobalFunctions.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 2941 | + native |
Index: trunk/extensions/NSFileRepo/REL1_14_0/phase3/includes/GlobalFunctions.patch |
— | — | @@ -0,0 +1,12 @@ |
| 2 | +Index: GlobalFunctions.php
|
| 3 | +===================================================================
|
| 4 | +--- GlobalFunctions.php (revision 53111)
|
| 5 | +@@ -2934,6 +2934,6 @@
|
| 6 | + */ |
| 7 | + function wfStripIllegalFilenameChars( $name ) { |
| 8 | + $name = wfBaseName( $name ); |
| 9 | +- $name = preg_replace ( "/[^".Title::legalChars()."]|:/", '-', $name ); |
| 10 | ++ $name = preg_replace ( "/[^".Title::legalChars()."]/", '-', $name ); |
| 11 | + return $name; |
| 12 | + } |
Index: trunk/extensions/NSFileRepo/REL1_15_0/phase3/img_auth.i18n.php |
— | — | @@ -0,0 +1,32 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Internationalisation file for img_auth script (see see http://www.mediawiki.org/wiki/Manual:Image_Authorization). |
| 5 | +*/ |
| 6 | + |
| 7 | +$messages = array(); |
| 8 | + |
| 9 | +/** English |
| 10 | + * @author Jack D. Pond |
| 11 | + */ |
| 12 | +$messages['en'] = array( |
| 13 | + 'image_auth-desc' => 'Image authorisation script', |
| 14 | + 'image_auth-nopathinfo' => "Missing PATH_INFO. Your server is not set up to pass this information - |
| 15 | +may be CGI-based and can't support img_auth. See `Image Authorization` on MediaWiki.", |
| 16 | + 'image_auth-notindir' => "Requested path not in upload directory.", |
| 17 | + 'image_auth-badtitle' => "Unable to construct a valid Title from `$1`.", |
| 18 | + 'image_auth-nologinnWL' => "Not logged in and `$1` not in whitelist.", |
| 19 | + 'image_auth-nofile' => "`$1` does not exist.", |
| 20 | + 'image_auth-isdir' => "`$1` is a directory.", |
| 21 | + 'image_auth-streaming' => "Streaming `$1`.", |
| 22 | + 'image_auth-public' => "The function of img_auth.php is to output files from a private wiki. This wiki |
| 23 | +is configured as a public wiki. For optimal security, img_auth.php is disabled for this case.", |
| 24 | + 'image_auth-noread' => "User does not have access to read `$1`." |
| 25 | +); |
| 26 | + |
| 27 | +/** Message documentation (Message documentation) |
| 28 | + * @author Jack D. Pond |
| 29 | + */ |
| 30 | +$messages['qqq'] = array( |
| 31 | + 'image_auth-desc' => 'Image authorisation script' |
| 32 | +); |
| 33 | + |
Property changes on: trunk/extensions/NSFileRepo/REL1_15_0/phase3/img_auth.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 34 | + native |
Index: trunk/extensions/NSFileRepo/REL1_15_0/phase3/img_auth.php |
— | — | @@ -0,0 +1,136 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Image authorisation script |
| 6 | + * |
| 7 | + * To use this, see http://www.mediawiki.org/wiki/Manual:Image_Authorization |
| 8 | + * |
| 9 | + * - Set $wgUploadDirectory to a non-public directory (not web accessible) |
| 10 | + * - Set $wgUploadPath to point to this file |
| 11 | + * |
| 12 | + * Your server needs to support PATH_INFO; CGI-based configurations usually don't. |
| 13 | + * |
| 14 | + * @file |
| 15 | + */ |
| 16 | + |
| 17 | + |
| 18 | +/** |
| 19 | + For security reasons, you usually don't want your user to know access was denied, just that it was. |
| 20 | + If you want to change this, you can set $wgImgAuthDetails to 'true' in localsettings.php and it will give the user the reason |
| 21 | + why access was denied. |
| 22 | +**/ |
| 23 | + |
| 24 | +global $wgImgAuthDetails; |
| 25 | +$wgImgAuthDetails = false; |
| 26 | + |
| 27 | +define( 'MW_NO_OUTPUT_COMPRESSION', 1 ); |
| 28 | +require_once( dirname( __FILE__ ) . '/includes/WebStart.php' ); |
| 29 | +wfProfileIn( 'img_auth.php' ); |
| 30 | +require_once( dirname( __FILE__ ) . '/includes/StreamFile.php' ); |
| 31 | + |
| 32 | +global $wgMessageCache, $messages; |
| 33 | +require_once( dirname( __FILE__ ) . '/img_auth.i18n.php' ); |
| 34 | +foreach( $messages as $lang => $LangMsg ) |
| 35 | + $wgMessageCache->addMessages( $LangMsg, $lang ); |
| 36 | + |
| 37 | +$perms = User::getGroupPermissions( array( '*' ) ); |
| 38 | + |
| 39 | +// See if this is a public Wiki (no protections) |
| 40 | +if ( in_array( 'read', $perms, true ) ) |
| 41 | + wfPublicError(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-public')); |
| 42 | + |
| 43 | +// Extract path and image information |
| 44 | +if( !isset( $_SERVER['PATH_INFO'] ) ) |
| 45 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-nopathinfo')); |
| 46 | + |
| 47 | +$path = $_SERVER['PATH_INFO']; |
| 48 | +$filename = realpath( $wgUploadDirectory . $_SERVER['PATH_INFO'] ); |
| 49 | +$realUpload = realpath( $wgUploadDirectory ); |
| 50 | + |
| 51 | +// Basic directory traversal check |
| 52 | +if( substr( $filename, 0, strlen( $realUpload ) ) != $realUpload ) |
| 53 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-notindir')); |
| 54 | + |
| 55 | +// Extract the file name and chop off the size specifier |
| 56 | +// (e.g. 120px-Foo.png => Foo.png) |
| 57 | +$name = wfBaseName( $path ); |
| 58 | +if( preg_match( '!\d+px-(.*)!i', $name, $m ) ) |
| 59 | + $name = $m[1]; |
| 60 | + |
| 61 | +// Check to see if the file exists |
| 62 | +if( !file_exists( $filename ) ) |
| 63 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-nofile',$filename)); |
| 64 | + |
| 65 | +// Check to see if tried to access a directory |
| 66 | +if( is_dir( $filename ) ) |
| 67 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-isdir',$filename)); |
| 68 | + |
| 69 | + |
| 70 | +$title = Title::makeTitleSafe( NS_FILE, $name ); |
| 71 | + |
| 72 | +// See if could create the title object |
| 73 | +if( !$title instanceof Title ) |
| 74 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-badtitle',$name)); |
| 75 | + |
| 76 | +// Run hook |
| 77 | +if (!wfRunHooks( 'ImgAuthBeforeStream', array( &$title, &$path, &$name, &$result ) ) ) |
| 78 | + wfForbidden($result[0],$result[1]); |
| 79 | + |
| 80 | +// Check the whitelist if needed, deprecated since usercan added |
| 81 | +// $pTitle = $title->getPrefixedText(); |
| 82 | +// if( !$wgUser->getId() && ( !is_array( $wgWhitelistRead ) || !in_array( $pTitle, $wgWhitelistRead ) ) ) |
| 83 | +// wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-nologinnWL',$pTitle)); |
| 84 | + |
| 85 | + |
| 86 | +// Check user authorization for this title |
| 87 | +if( !$title->userCanRead() ) |
| 88 | + wfForbidden(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-noread',$name)); |
| 89 | + |
| 90 | + |
| 91 | +// Stream the requested file |
| 92 | +wfDebugLog( 'img_auth', "Streaming `{$filename}`" ); |
| 93 | +wfStreamFile( $filename, array( 'Cache-Control: private', 'Vary: Cookie' ) ); |
| 94 | +wfLogProfilingData(); |
| 95 | + |
| 96 | +/** |
| 97 | + * Issue a standard HTTP 403 Forbidden header ($msg1) and an |
| 98 | + * error message ($msg2), then end the script |
| 99 | + */ |
| 100 | +function wfForbidden($msg1,$msg2) { |
| 101 | + global $wgImgAuthDetails; |
| 102 | + $detailMsg = $wgImgAuthDetails ? $msg2 : wfMsgHTML('badaccess-group0'); |
| 103 | + wfDebugLog( 'img_auth', "wfForbidden Msg: ".$msg2 ); |
| 104 | + header( 'HTTP/1.0 403 Forbidden' ); |
| 105 | + header( 'Vary: Cookie' ); |
| 106 | + header( 'Content-Type: text/html; charset=utf-8' ); |
| 107 | + echo <<<ENDS |
| 108 | +<html> |
| 109 | +<body> |
| 110 | +<h1>$msg1</h1> |
| 111 | +<p>$detailMsg</p> |
| 112 | +</body> |
| 113 | +</html> |
| 114 | +ENDS; |
| 115 | + wfLogProfilingData(); |
| 116 | + exit(); |
| 117 | +} |
| 118 | + |
| 119 | +/** |
| 120 | + * Show a 403 error for use when the wiki is public |
| 121 | + */ |
| 122 | +function wfPublicError($msg1,$msg2) { |
| 123 | + header( 'HTTP/1.0 403 Forbidden' ); |
| 124 | + header( 'Content-Type: text/html; charset=utf-8' ); |
| 125 | + wfDebugLog( 'img_auth', "wfPublicError Msg: ".$msg2 ); |
| 126 | + echo <<<ENDS |
| 127 | +<html> |
| 128 | +<body> |
| 129 | +<h1>$msg1</h1> |
| 130 | +<p>$msg2</p> |
| 131 | +</body> |
| 132 | +</html> |
| 133 | +ENDS; |
| 134 | + wfLogProfilingData(); |
| 135 | + exit; |
| 136 | +} |
| 137 | + |
Property changes on: trunk/extensions/NSFileRepo/REL1_15_0/phase3/img_auth.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 138 | + native |
Index: trunk/extensions/NSFileRepo/REL1_15_0/phase3/includes/GlobalFunctions.php |
— | — | @@ -0,0 +1,3039 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 5 | + die( "This file is part of MediaWiki, it is not a valid entry point" ); |
| 6 | +} |
| 7 | + |
| 8 | +/** |
| 9 | + * Global functions used everywhere |
| 10 | + */ |
| 11 | + |
| 12 | +require_once dirname(__FILE__) . '/normal/UtfNormalUtil.php'; |
| 13 | +require_once dirname(__FILE__) . '/XmlFunctions.php'; |
| 14 | + |
| 15 | +// Hide compatibility functions from Doxygen |
| 16 | +/// @cond |
| 17 | + |
| 18 | +/** |
| 19 | + * Compatibility functions |
| 20 | + * |
| 21 | + * We more or less support PHP 5.0.x and up. |
| 22 | + * Re-implementations of newer functions or functions in non-standard |
| 23 | + * PHP extensions may be included here. |
| 24 | + */ |
| 25 | +if( !function_exists('iconv') ) { |
| 26 | + # iconv support is not in the default configuration and so may not be present. |
| 27 | + # Assume will only ever use utf-8 and iso-8859-1. |
| 28 | + # This will *not* work in all circumstances. |
| 29 | + function iconv( $from, $to, $string ) { |
| 30 | + if(strcasecmp( $from, $to ) == 0) return $string; |
| 31 | + if(strcasecmp( $from, 'utf-8' ) == 0) return utf8_decode( $string ); |
| 32 | + if(strcasecmp( $to, 'utf-8' ) == 0) return utf8_encode( $string ); |
| 33 | + return $string; |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +# UTF-8 substr function based on a PHP manual comment |
| 38 | +if ( !function_exists( 'mb_substr' ) ) { |
| 39 | + function mb_substr( $str, $start ) { |
| 40 | + $ar = array(); |
| 41 | + preg_match_all( '/./us', $str, $ar ); |
| 42 | + |
| 43 | + if( func_num_args() >= 3 ) { |
| 44 | + $end = func_get_arg( 2 ); |
| 45 | + return join( '', array_slice( $ar[0], $start, $end ) ); |
| 46 | + } else { |
| 47 | + return join( '', array_slice( $ar[0], $start ) ); |
| 48 | + } |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +if ( !function_exists( 'mb_strlen' ) ) { |
| 53 | + /** |
| 54 | + * Fallback implementation of mb_strlen, hardcoded to UTF-8. |
| 55 | + * @param string $str |
| 56 | + * @param string $enc optional encoding; ignored |
| 57 | + * @return int |
| 58 | + */ |
| 59 | + function mb_strlen( $str, $enc="" ) { |
| 60 | + $counts = count_chars( $str ); |
| 61 | + $total = 0; |
| 62 | + |
| 63 | + // Count ASCII bytes |
| 64 | + for( $i = 0; $i < 0x80; $i++ ) { |
| 65 | + $total += $counts[$i]; |
| 66 | + } |
| 67 | + |
| 68 | + // Count multibyte sequence heads |
| 69 | + for( $i = 0xc0; $i < 0xff; $i++ ) { |
| 70 | + $total += $counts[$i]; |
| 71 | + } |
| 72 | + return $total; |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +if ( !function_exists( 'array_diff_key' ) ) { |
| 77 | + /** |
| 78 | + * Exists in PHP 5.1.0+ |
| 79 | + * Not quite compatible, two-argument version only |
| 80 | + * Null values will cause problems due to this use of isset() |
| 81 | + */ |
| 82 | + function array_diff_key( $left, $right ) { |
| 83 | + $result = $left; |
| 84 | + foreach ( $left as $key => $unused ) { |
| 85 | + if ( isset( $right[$key] ) ) { |
| 86 | + unset( $result[$key] ); |
| 87 | + } |
| 88 | + } |
| 89 | + return $result; |
| 90 | + } |
| 91 | +} |
| 92 | + |
| 93 | +// Support for Wietse Venema's taint feature |
| 94 | +if ( !function_exists( 'istainted' ) ) { |
| 95 | + function istainted( $var ) { |
| 96 | + return 0; |
| 97 | + } |
| 98 | + function taint( $var, $level = 0 ) {} |
| 99 | + function untaint( $var, $level = 0 ) {} |
| 100 | + define( 'TC_HTML', 1 ); |
| 101 | + define( 'TC_SHELL', 1 ); |
| 102 | + define( 'TC_MYSQL', 1 ); |
| 103 | + define( 'TC_PCRE', 1 ); |
| 104 | + define( 'TC_SELF', 1 ); |
| 105 | +} |
| 106 | +/// @endcond |
| 107 | + |
| 108 | + |
| 109 | +/** |
| 110 | + * Like array_diff( $a, $b ) except that it works with two-dimensional arrays. |
| 111 | + */ |
| 112 | +function wfArrayDiff2( $a, $b ) { |
| 113 | + return array_udiff( $a, $b, 'wfArrayDiff2_cmp' ); |
| 114 | +} |
| 115 | +function wfArrayDiff2_cmp( $a, $b ) { |
| 116 | + if ( !is_array( $a ) ) { |
| 117 | + return strcmp( $a, $b ); |
| 118 | + } elseif ( count( $a ) !== count( $b ) ) { |
| 119 | + return count( $a ) < count( $b ) ? -1 : 1; |
| 120 | + } else { |
| 121 | + reset( $a ); |
| 122 | + reset( $b ); |
| 123 | + while( ( list( $keyA, $valueA ) = each( $a ) ) && ( list( $keyB, $valueB ) = each( $b ) ) ) { |
| 124 | + $cmp = strcmp( $valueA, $valueB ); |
| 125 | + if ( $cmp !== 0 ) { |
| 126 | + return $cmp; |
| 127 | + } |
| 128 | + } |
| 129 | + return 0; |
| 130 | + } |
| 131 | +} |
| 132 | + |
| 133 | +/** |
| 134 | + * Wrapper for clone(), for compatibility with PHP4-friendly extensions. |
| 135 | + * PHP 5 won't let you declare a 'clone' function, even conditionally, |
| 136 | + * so it has to be a wrapper with a different name. |
| 137 | + */ |
| 138 | +function wfClone( $object ) { |
| 139 | + return clone( $object ); |
| 140 | +} |
| 141 | + |
| 142 | +/** |
| 143 | + * Seed Mersenne Twister |
| 144 | + * No-op for compatibility; only necessary in PHP < 4.2.0 |
| 145 | + */ |
| 146 | +function wfSeedRandom() { |
| 147 | + /* No-op */ |
| 148 | +} |
| 149 | + |
| 150 | +/** |
| 151 | + * Get a random decimal value between 0 and 1, in a way |
| 152 | + * not likely to give duplicate values for any realistic |
| 153 | + * number of articles. |
| 154 | + * |
| 155 | + * @return string |
| 156 | + */ |
| 157 | +function wfRandom() { |
| 158 | + # The maximum random value is "only" 2^31-1, so get two random |
| 159 | + # values to reduce the chance of dupes |
| 160 | + $max = mt_getrandmax() + 1; |
| 161 | + $rand = number_format( (mt_rand() * $max + mt_rand()) |
| 162 | + / $max / $max, 12, '.', '' ); |
| 163 | + return $rand; |
| 164 | +} |
| 165 | + |
| 166 | +/** |
| 167 | + * We want some things to be included as literal characters in our title URLs |
| 168 | + * for prettiness, which urlencode encodes by default. According to RFC 1738, |
| 169 | + * all of the following should be safe: |
| 170 | + * |
| 171 | + * ;:@&=$-_.+!*'(), |
| 172 | + * |
| 173 | + * But + is not safe because it's used to indicate a space; &= are only safe in |
| 174 | + * paths and not in queries (and we don't distinguish here); ' seems kind of |
| 175 | + * scary; and urlencode() doesn't touch -_. to begin with. Plus, although / |
| 176 | + * is reserved, we don't care. So the list we unescape is: |
| 177 | + * |
| 178 | + * ;:@$!*(),/ |
| 179 | + * |
| 180 | + * %2F in the page titles seems to fatally break for some reason. |
| 181 | + * |
| 182 | + * @param $s String: |
| 183 | + * @return string |
| 184 | +*/ |
| 185 | +function wfUrlencode( $s ) { |
| 186 | + $s = urlencode( $s ); |
| 187 | + $s = str_ireplace( |
| 188 | + array( '%3B','%3A','%40','%24','%21','%2A','%28','%29','%2C','%2F' ), |
| 189 | + array( ';', ':', '@', '$', '!', '*', '(', ')', ',', '/' ), |
| 190 | + $s |
| 191 | + ); |
| 192 | + |
| 193 | + return $s; |
| 194 | +} |
| 195 | + |
| 196 | +/** |
| 197 | + * Sends a line to the debug log if enabled or, optionally, to a comment in output. |
| 198 | + * In normal operation this is a NOP. |
| 199 | + * |
| 200 | + * Controlling globals: |
| 201 | + * $wgDebugLogFile - points to the log file |
| 202 | + * $wgProfileOnly - if set, normal debug messages will not be recorded. |
| 203 | + * $wgDebugRawPage - if false, 'action=raw' hits will not result in debug output. |
| 204 | + * $wgDebugComments - if on, some debug items may appear in comments in the HTML output. |
| 205 | + * |
| 206 | + * @param $text String |
| 207 | + * @param $logonly Bool: set true to avoid appearing in HTML when $wgDebugComments is set |
| 208 | + */ |
| 209 | +function wfDebug( $text, $logonly = false ) { |
| 210 | + global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage; |
| 211 | + global $wgDebugLogPrefix; |
| 212 | + static $recursion = 0; |
| 213 | + |
| 214 | + static $cache = array(); // Cache of unoutputted messages |
| 215 | + |
| 216 | + # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet |
| 217 | + if ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' && !$wgDebugRawPage ) { |
| 218 | + return; |
| 219 | + } |
| 220 | + |
| 221 | + if ( $wgDebugComments && !$logonly ) { |
| 222 | + $cache[] = $text; |
| 223 | + |
| 224 | + if ( !isset( $wgOut ) ) { |
| 225 | + return; |
| 226 | + } |
| 227 | + if ( !StubObject::isRealObject( $wgOut ) ) { |
| 228 | + if ( $recursion ) { |
| 229 | + return; |
| 230 | + } |
| 231 | + $recursion++; |
| 232 | + $wgOut->_unstub(); |
| 233 | + $recursion--; |
| 234 | + } |
| 235 | + |
| 236 | + // add the message and possible cached ones to the output |
| 237 | + array_map( array( $wgOut, 'debug' ), $cache ); |
| 238 | + $cache = array(); |
| 239 | + } |
| 240 | + if ( '' != $wgDebugLogFile && !$wgProfileOnly ) { |
| 241 | + # Strip unprintables; they can switch terminal modes when binary data |
| 242 | + # gets dumped, which is pretty annoying. |
| 243 | + $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text ); |
| 244 | + $text = $wgDebugLogPrefix . $text; |
| 245 | + wfErrorLog( $text, $wgDebugLogFile ); |
| 246 | + } |
| 247 | +} |
| 248 | + |
| 249 | +/** |
| 250 | + * Send a line giving PHP memory usage. |
| 251 | + * @param $exact Bool : print exact values instead of kilobytes (default: false) |
| 252 | + */ |
| 253 | +function wfDebugMem( $exact = false ) { |
| 254 | + $mem = memory_get_usage(); |
| 255 | + if( !$exact ) { |
| 256 | + $mem = floor( $mem / 1024 ) . ' kilobytes'; |
| 257 | + } else { |
| 258 | + $mem .= ' bytes'; |
| 259 | + } |
| 260 | + wfDebug( "Memory usage: $mem\n" ); |
| 261 | +} |
| 262 | + |
| 263 | +/** |
| 264 | + * Send a line to a supplementary debug log file, if configured, or main debug log if not. |
| 265 | + * $wgDebugLogGroups[$logGroup] should be set to a filename to send to a separate log. |
| 266 | + * |
| 267 | + * @param $logGroup String |
| 268 | + * @param $text String |
| 269 | + * @param $public Bool: whether to log the event in the public log if no private |
| 270 | + * log file is specified, (default true) |
| 271 | + */ |
| 272 | +function wfDebugLog( $logGroup, $text, $public = true ) { |
| 273 | + global $wgDebugLogGroups, $wgShowHostnames; |
| 274 | + $text = trim($text)."\n"; |
| 275 | + if( isset( $wgDebugLogGroups[$logGroup] ) ) { |
| 276 | + $time = wfTimestamp( TS_DB ); |
| 277 | + $wiki = wfWikiID(); |
| 278 | + if ( $wgShowHostnames ) { |
| 279 | + $host = wfHostname(); |
| 280 | + } else { |
| 281 | + $host = ''; |
| 282 | + } |
| 283 | + wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] ); |
| 284 | + } else if ( $public === true ) { |
| 285 | + wfDebug( $text, true ); |
| 286 | + } |
| 287 | +} |
| 288 | + |
| 289 | +/** |
| 290 | + * Log for database errors |
| 291 | + * @param $text String: database error message. |
| 292 | + */ |
| 293 | +function wfLogDBError( $text ) { |
| 294 | + global $wgDBerrorLog, $wgDBname; |
| 295 | + if ( $wgDBerrorLog ) { |
| 296 | + $host = trim(`hostname`); |
| 297 | + $text = date('D M j G:i:s T Y') . "\t$host\t$wgDBname\t$text"; |
| 298 | + wfErrorLog( $text, $wgDBerrorLog ); |
| 299 | + } |
| 300 | +} |
| 301 | + |
| 302 | +/** |
| 303 | + * Log to a file without getting "file size exceeded" signals. |
| 304 | + * |
| 305 | + * Can also log to TCP or UDP with the syntax udp://host:port/prefix. This will |
| 306 | + * send lines to the specified port, prefixed by the specified prefix and a space. |
| 307 | + */ |
| 308 | +function wfErrorLog( $text, $file ) { |
| 309 | + if ( substr( $file, 0, 4 ) == 'udp:' ) { |
| 310 | + if ( preg_match( '!^(tcp|udp):(?://)?\[([0-9a-fA-F:]+)\]:(\d+)(?:/(.*))?$!', $file, $m ) ) { |
| 311 | + // IPv6 bracketed host |
| 312 | + $protocol = $m[1]; |
| 313 | + $host = $m[2]; |
| 314 | + $port = $m[3]; |
| 315 | + $prefix = isset( $m[4] ) ? $m[4] : false; |
| 316 | + } elseif ( preg_match( '!^(tcp|udp):(?://)?([a-zA-Z0-9.-]+):(\d+)(?:/(.*))?$!', $file, $m ) ) { |
| 317 | + $protocol = $m[1]; |
| 318 | + $host = $m[2]; |
| 319 | + $port = $m[3]; |
| 320 | + $prefix = isset( $m[4] ) ? $m[4] : false; |
| 321 | + } else { |
| 322 | + throw new MWException( __METHOD__.": Invalid UDP specification" ); |
| 323 | + } |
| 324 | + // Clean it up for the multiplexer |
| 325 | + if ( strval( $prefix ) !== '' ) { |
| 326 | + $text = preg_replace( '/^/m', $prefix . ' ', $text ); |
| 327 | + if ( substr( $text, -1 ) != "\n" ) { |
| 328 | + $text .= "\n"; |
| 329 | + } |
| 330 | + } |
| 331 | + |
| 332 | + $sock = fsockopen( "$protocol://$host", $port ); |
| 333 | + if ( !$sock ) { |
| 334 | + return; |
| 335 | + } |
| 336 | + fwrite( $sock, $text ); |
| 337 | + fclose( $sock ); |
| 338 | + } else { |
| 339 | + wfSuppressWarnings(); |
| 340 | + $exists = file_exists( $file ); |
| 341 | + $size = $exists ? filesize( $file ) : false; |
| 342 | + if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) { |
| 343 | + error_log( $text, 3, $file ); |
| 344 | + } |
| 345 | + wfRestoreWarnings(); |
| 346 | + } |
| 347 | +} |
| 348 | + |
| 349 | +/** |
| 350 | + * @todo document |
| 351 | + */ |
| 352 | +function wfLogProfilingData() { |
| 353 | + global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest; |
| 354 | + global $wgProfiler, $wgProfileLimit, $wgUser; |
| 355 | + # Profiling must actually be enabled... |
| 356 | + if( !isset( $wgProfiler ) ) return; |
| 357 | + # Get total page request time |
| 358 | + $now = wfTime(); |
| 359 | + $elapsed = $now - $wgRequestTime; |
| 360 | + # Only show pages that longer than $wgProfileLimit time (default is 0) |
| 361 | + if( $elapsed <= $wgProfileLimit ) return; |
| 362 | + $prof = wfGetProfilingOutput( $wgRequestTime, $elapsed ); |
| 363 | + $forward = ''; |
| 364 | + if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) |
| 365 | + $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR']; |
| 366 | + if( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) |
| 367 | + $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP']; |
| 368 | + if( !empty( $_SERVER['HTTP_FROM'] ) ) |
| 369 | + $forward .= ' from ' . $_SERVER['HTTP_FROM']; |
| 370 | + if( $forward ) |
| 371 | + $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})"; |
| 372 | + // Don't unstub $wgUser at this late stage just for statistics purposes |
| 373 | + if( StubObject::isRealObject($wgUser) && $wgUser->isAnon() ) |
| 374 | + $forward .= ' anon'; |
| 375 | + $log = sprintf( "%s\t%04.3f\t%s\n", |
| 376 | + gmdate( 'YmdHis' ), $elapsed, |
| 377 | + urldecode( $wgRequest->getRequestURL() . $forward ) ); |
| 378 | + if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) { |
| 379 | + wfErrorLog( $log . $prof, $wgDebugLogFile ); |
| 380 | + } |
| 381 | +} |
| 382 | + |
| 383 | +/** |
| 384 | + * Check if the wiki read-only lock file is present. This can be used to lock |
| 385 | + * off editing functions, but doesn't guarantee that the database will not be |
| 386 | + * modified. |
| 387 | + * @return bool |
| 388 | + */ |
| 389 | +function wfReadOnly() { |
| 390 | + global $wgReadOnlyFile, $wgReadOnly; |
| 391 | + |
| 392 | + if ( !is_null( $wgReadOnly ) ) { |
| 393 | + return (bool)$wgReadOnly; |
| 394 | + } |
| 395 | + if ( '' == $wgReadOnlyFile ) { |
| 396 | + return false; |
| 397 | + } |
| 398 | + // Set $wgReadOnly for faster access next time |
| 399 | + if ( is_file( $wgReadOnlyFile ) ) { |
| 400 | + $wgReadOnly = file_get_contents( $wgReadOnlyFile ); |
| 401 | + } else { |
| 402 | + $wgReadOnly = false; |
| 403 | + } |
| 404 | + return (bool)$wgReadOnly; |
| 405 | +} |
| 406 | + |
| 407 | +function wfReadOnlyReason() { |
| 408 | + global $wgReadOnly; |
| 409 | + wfReadOnly(); |
| 410 | + return $wgReadOnly; |
| 411 | +} |
| 412 | + |
| 413 | +/** |
| 414 | + * Return a Language object from $langcode |
| 415 | + * @param $langcode Mixed: either: |
| 416 | + * - a Language object |
| 417 | + * - code of the language to get the message for, if it is |
| 418 | + * a valid code create a language for that language, if |
| 419 | + * it is a string but not a valid code then make a basic |
| 420 | + * language object |
| 421 | + * - a boolean: if it's false then use the current users |
| 422 | + * language (as a fallback for the old parameter |
| 423 | + * functionality), or if it is true then use the wikis |
| 424 | + * @return Language object |
| 425 | + */ |
| 426 | +function wfGetLangObj( $langcode = false ){ |
| 427 | + # Identify which language to get or create a language object for. |
| 428 | + if( $langcode instanceof Language ) |
| 429 | + # Great, we already have the object! |
| 430 | + return $langcode; |
| 431 | + |
| 432 | + global $wgContLang; |
| 433 | + if( $langcode === $wgContLang->getCode() || $langcode === true ) |
| 434 | + # $langcode is the language code of the wikis content language object. |
| 435 | + # or it is a boolean and value is true |
| 436 | + return $wgContLang; |
| 437 | + |
| 438 | + global $wgLang; |
| 439 | + if( $langcode === $wgLang->getCode() || $langcode === false ) |
| 440 | + # $langcode is the language code of user language object. |
| 441 | + # or it was a boolean and value is false |
| 442 | + return $wgLang; |
| 443 | + |
| 444 | + $validCodes = array_keys( Language::getLanguageNames() ); |
| 445 | + if( in_array( $langcode, $validCodes ) ) |
| 446 | + # $langcode corresponds to a valid language. |
| 447 | + return Language::factory( $langcode ); |
| 448 | + |
| 449 | + # $langcode is a string, but not a valid language code; use content language. |
| 450 | + wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" ); |
| 451 | + return $wgContLang; |
| 452 | +} |
| 453 | + |
| 454 | +/** |
| 455 | + * Get a message from anywhere, for the current user language. |
| 456 | + * |
| 457 | + * Use wfMsgForContent() instead if the message should NOT |
| 458 | + * change depending on the user preferences. |
| 459 | + * |
| 460 | + * @param $key String: lookup key for the message, usually |
| 461 | + * defined in languages/Language.php |
| 462 | + * |
| 463 | + * This function also takes extra optional parameters (not |
| 464 | + * shown in the function definition), which can by used to |
| 465 | + * insert variable text into the predefined message. |
| 466 | + */ |
| 467 | +function wfMsg( $key ) { |
| 468 | + $args = func_get_args(); |
| 469 | + array_shift( $args ); |
| 470 | + return wfMsgReal( $key, $args, true ); |
| 471 | +} |
| 472 | + |
| 473 | +/** |
| 474 | + * Same as above except doesn't transform the message |
| 475 | + */ |
| 476 | +function wfMsgNoTrans( $key ) { |
| 477 | + $args = func_get_args(); |
| 478 | + array_shift( $args ); |
| 479 | + return wfMsgReal( $key, $args, true, false, false ); |
| 480 | +} |
| 481 | + |
| 482 | +/** |
| 483 | + * Get a message from anywhere, for the current global language |
| 484 | + * set with $wgLanguageCode. |
| 485 | + * |
| 486 | + * Use this if the message should NOT change dependent on the |
| 487 | + * language set in the user's preferences. This is the case for |
| 488 | + * most text written into logs, as well as link targets (such as |
| 489 | + * the name of the copyright policy page). Link titles, on the |
| 490 | + * other hand, should be shown in the UI language. |
| 491 | + * |
| 492 | + * Note that MediaWiki allows users to change the user interface |
| 493 | + * language in their preferences, but a single installation |
| 494 | + * typically only contains content in one language. |
| 495 | + * |
| 496 | + * Be wary of this distinction: If you use wfMsg() where you should |
| 497 | + * use wfMsgForContent(), a user of the software may have to |
| 498 | + * customize over 70 messages in order to, e.g., fix a link in every |
| 499 | + * possible language. |
| 500 | + * |
| 501 | + * @param $key String: lookup key for the message, usually |
| 502 | + * defined in languages/Language.php |
| 503 | + */ |
| 504 | +function wfMsgForContent( $key ) { |
| 505 | + global $wgForceUIMsgAsContentMsg; |
| 506 | + $args = func_get_args(); |
| 507 | + array_shift( $args ); |
| 508 | + $forcontent = true; |
| 509 | + if( is_array( $wgForceUIMsgAsContentMsg ) && |
| 510 | + in_array( $key, $wgForceUIMsgAsContentMsg ) ) |
| 511 | + $forcontent = false; |
| 512 | + return wfMsgReal( $key, $args, true, $forcontent ); |
| 513 | +} |
| 514 | + |
| 515 | +/** |
| 516 | + * Same as above except doesn't transform the message |
| 517 | + */ |
| 518 | +function wfMsgForContentNoTrans( $key ) { |
| 519 | + global $wgForceUIMsgAsContentMsg; |
| 520 | + $args = func_get_args(); |
| 521 | + array_shift( $args ); |
| 522 | + $forcontent = true; |
| 523 | + if( is_array( $wgForceUIMsgAsContentMsg ) && |
| 524 | + in_array( $key, $wgForceUIMsgAsContentMsg ) ) |
| 525 | + $forcontent = false; |
| 526 | + return wfMsgReal( $key, $args, true, $forcontent, false ); |
| 527 | +} |
| 528 | + |
| 529 | +/** |
| 530 | + * Get a message from the language file, for the UI elements |
| 531 | + */ |
| 532 | +function wfMsgNoDB( $key ) { |
| 533 | + $args = func_get_args(); |
| 534 | + array_shift( $args ); |
| 535 | + return wfMsgReal( $key, $args, false ); |
| 536 | +} |
| 537 | + |
| 538 | +/** |
| 539 | + * Get a message from the language file, for the content |
| 540 | + */ |
| 541 | +function wfMsgNoDBForContent( $key ) { |
| 542 | + global $wgForceUIMsgAsContentMsg; |
| 543 | + $args = func_get_args(); |
| 544 | + array_shift( $args ); |
| 545 | + $forcontent = true; |
| 546 | + if( is_array( $wgForceUIMsgAsContentMsg ) && |
| 547 | + in_array( $key, $wgForceUIMsgAsContentMsg ) ) |
| 548 | + $forcontent = false; |
| 549 | + return wfMsgReal( $key, $args, false, $forcontent ); |
| 550 | +} |
| 551 | + |
| 552 | + |
| 553 | +/** |
| 554 | + * Really get a message |
| 555 | + * @param $key String: key to get. |
| 556 | + * @param $args |
| 557 | + * @param $useDB Boolean |
| 558 | + * @param $transform Boolean: Whether or not to transform the message. |
| 559 | + * @param $forContent Boolean |
| 560 | + * @return String: the requested message. |
| 561 | + */ |
| 562 | +function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform = true ) { |
| 563 | + wfProfileIn( __METHOD__ ); |
| 564 | + $message = wfMsgGetKey( $key, $useDB, $forContent, $transform ); |
| 565 | + $message = wfMsgReplaceArgs( $message, $args ); |
| 566 | + wfProfileOut( __METHOD__ ); |
| 567 | + return $message; |
| 568 | +} |
| 569 | + |
| 570 | +/** |
| 571 | + * This function provides the message source for messages to be edited which are *not* stored in the database. |
| 572 | + * @param $key String: |
| 573 | + */ |
| 574 | +function wfMsgWeirdKey ( $key ) { |
| 575 | + $source = wfMsgGetKey( $key, false, true, false ); |
| 576 | + if ( wfEmptyMsg( $key, $source ) ) |
| 577 | + return ""; |
| 578 | + else |
| 579 | + return $source; |
| 580 | +} |
| 581 | + |
| 582 | +/** |
| 583 | + * Fetch a message string value, but don't replace any keys yet. |
| 584 | + * @param string $key |
| 585 | + * @param bool $useDB |
| 586 | + * @param string $langcode Code of the language to get the message for, or |
| 587 | + * behaves as a content language switch if it is a |
| 588 | + * boolean. |
| 589 | + * @return string |
| 590 | + * @private |
| 591 | + */ |
| 592 | +function wfMsgGetKey( $key, $useDB, $langCode = false, $transform = true ) { |
| 593 | + global $wgContLang, $wgMessageCache; |
| 594 | + |
| 595 | + wfRunHooks('NormalizeMessageKey', array(&$key, &$useDB, &$langCode, &$transform)); |
| 596 | + |
| 597 | + # If $wgMessageCache isn't initialised yet, try to return something sensible. |
| 598 | + if( is_object( $wgMessageCache ) ) { |
| 599 | + $message = $wgMessageCache->get( $key, $useDB, $langCode ); |
| 600 | + if ( $transform ) { |
| 601 | + $message = $wgMessageCache->transform( $message ); |
| 602 | + } |
| 603 | + } else { |
| 604 | + $lang = wfGetLangObj( $langCode ); |
| 605 | + |
| 606 | + # MessageCache::get() does this already, Language::getMessage() doesn't |
| 607 | + # ISSUE: Should we try to handle "message/lang" here too? |
| 608 | + $key = str_replace( ' ' , '_' , $wgContLang->lcfirst( $key ) ); |
| 609 | + |
| 610 | + if( is_object( $lang ) ) { |
| 611 | + $message = $lang->getMessage( $key ); |
| 612 | + } else { |
| 613 | + $message = false; |
| 614 | + } |
| 615 | + } |
| 616 | + |
| 617 | + return $message; |
| 618 | +} |
| 619 | + |
| 620 | +/** |
| 621 | + * Replace message parameter keys on the given formatted output. |
| 622 | + * |
| 623 | + * @param string $message |
| 624 | + * @param array $args |
| 625 | + * @return string |
| 626 | + * @private |
| 627 | + */ |
| 628 | +function wfMsgReplaceArgs( $message, $args ) { |
| 629 | + # Fix windows line-endings |
| 630 | + # Some messages are split with explode("\n", $msg) |
| 631 | + $message = str_replace( "\r", '', $message ); |
| 632 | + |
| 633 | + // Replace arguments |
| 634 | + if ( count( $args ) ) { |
| 635 | + if ( is_array( $args[0] ) ) { |
| 636 | + $args = array_values( $args[0] ); |
| 637 | + } |
| 638 | + $replacementKeys = array(); |
| 639 | + foreach( $args as $n => $param ) { |
| 640 | + $replacementKeys['$' . ($n + 1)] = $param; |
| 641 | + } |
| 642 | + $message = strtr( $message, $replacementKeys ); |
| 643 | + } |
| 644 | + |
| 645 | + return $message; |
| 646 | +} |
| 647 | + |
| 648 | +/** |
| 649 | + * Return an HTML-escaped version of a message. |
| 650 | + * Parameter replacements, if any, are done *after* the HTML-escaping, |
| 651 | + * so parameters may contain HTML (eg links or form controls). Be sure |
| 652 | + * to pre-escape them if you really do want plaintext, or just wrap |
| 653 | + * the whole thing in htmlspecialchars(). |
| 654 | + * |
| 655 | + * @param string $key |
| 656 | + * @param string ... parameters |
| 657 | + * @return string |
| 658 | + */ |
| 659 | +function wfMsgHtml( $key ) { |
| 660 | + $args = func_get_args(); |
| 661 | + array_shift( $args ); |
| 662 | + return wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $key, true ) ), $args ); |
| 663 | +} |
| 664 | + |
| 665 | +/** |
| 666 | + * Return an HTML version of message |
| 667 | + * Parameter replacements, if any, are done *after* parsing the wiki-text message, |
| 668 | + * so parameters may contain HTML (eg links or form controls). Be sure |
| 669 | + * to pre-escape them if you really do want plaintext, or just wrap |
| 670 | + * the whole thing in htmlspecialchars(). |
| 671 | + * |
| 672 | + * @param string $key |
| 673 | + * @param string ... parameters |
| 674 | + * @return string |
| 675 | + */ |
| 676 | +function wfMsgWikiHtml( $key ) { |
| 677 | + global $wgOut; |
| 678 | + $args = func_get_args(); |
| 679 | + array_shift( $args ); |
| 680 | + return wfMsgReplaceArgs( $wgOut->parse( wfMsgGetKey( $key, true ), /* can't be set to false */ true ), $args ); |
| 681 | +} |
| 682 | + |
| 683 | +/** |
| 684 | + * Returns message in the requested format |
| 685 | + * @param string $key Key of the message |
| 686 | + * @param array $options Processing rules. Can take the following options: |
| 687 | + * <i>parse</i>: parses wikitext to html |
| 688 | + * <i>parseinline</i>: parses wikitext to html and removes the surrounding |
| 689 | + * p's added by parser or tidy |
| 690 | + * <i>escape</i>: filters message through htmlspecialchars |
| 691 | + * <i>escapenoentities</i>: same, but allows entity references like through |
| 692 | + * <i>replaceafter</i>: parameters are substituted after parsing or escaping |
| 693 | + * <i>parsemag</i>: transform the message using magic phrases |
| 694 | + * <i>content</i>: fetch message for content language instead of interface |
| 695 | + * Also can accept a single associative argument, of the form 'language' => 'xx': |
| 696 | + * <i>language</i>: Language object or language code to fetch message for |
| 697 | + * (overriden by <i>content</i>), its behaviour with parser, parseinline |
| 698 | + * and parsemag is undefined. |
| 699 | + * Behavior for conflicting options (e.g., parse+parseinline) is undefined. |
| 700 | + */ |
| 701 | +function wfMsgExt( $key, $options ) { |
| 702 | + global $wgOut; |
| 703 | + |
| 704 | + $args = func_get_args(); |
| 705 | + array_shift( $args ); |
| 706 | + array_shift( $args ); |
| 707 | + $options = (array)$options; |
| 708 | + |
| 709 | + foreach( $options as $arrayKey => $option ) { |
| 710 | + if( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) { |
| 711 | + # An unknown index, neither numeric nor "language" |
| 712 | + trigger_error( "wfMsgExt called with incorrect parameter key $arrayKey", E_USER_WARNING ); |
| 713 | + } elseif( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option, |
| 714 | + array( 'parse', 'parseinline', 'escape', 'escapenoentities', |
| 715 | + 'replaceafter', 'parsemag', 'content' ) ) ) { |
| 716 | + # A numeric index with unknown value |
| 717 | + trigger_error( "wfMsgExt called with incorrect parameter $option", E_USER_WARNING ); |
| 718 | + } |
| 719 | + } |
| 720 | + |
| 721 | + if( in_array('content', $options, true ) ) { |
| 722 | + $forContent = true; |
| 723 | + $langCode = true; |
| 724 | + } elseif( array_key_exists('language', $options) ) { |
| 725 | + $forContent = false; |
| 726 | + $langCode = wfGetLangObj( $options['language'] ); |
| 727 | + } else { |
| 728 | + $forContent = false; |
| 729 | + $langCode = false; |
| 730 | + } |
| 731 | + |
| 732 | + $string = wfMsgGetKey( $key, /*DB*/true, $langCode, /*Transform*/false ); |
| 733 | + |
| 734 | + if( !in_array('replaceafter', $options, true ) ) { |
| 735 | + $string = wfMsgReplaceArgs( $string, $args ); |
| 736 | + } |
| 737 | + |
| 738 | + if( in_array('parse', $options, true ) ) { |
| 739 | + $string = $wgOut->parse( $string, true, !$forContent ); |
| 740 | + } elseif ( in_array('parseinline', $options, true ) ) { |
| 741 | + $string = $wgOut->parse( $string, true, !$forContent ); |
| 742 | + $m = array(); |
| 743 | + if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) { |
| 744 | + $string = $m[1]; |
| 745 | + } |
| 746 | + } elseif ( in_array('parsemag', $options, true ) ) { |
| 747 | + global $wgMessageCache; |
| 748 | + if ( isset( $wgMessageCache ) ) { |
| 749 | + $string = $wgMessageCache->transform( $string, |
| 750 | + !$forContent, |
| 751 | + is_object( $langCode ) ? $langCode : null ); |
| 752 | + } |
| 753 | + } |
| 754 | + |
| 755 | + if ( in_array('escape', $options, true ) ) { |
| 756 | + $string = htmlspecialchars ( $string ); |
| 757 | + } elseif ( in_array( 'escapenoentities', $options, true ) ) { |
| 758 | + $string = Sanitizer::escapeHtmlAllowEntities( $string ); |
| 759 | + } |
| 760 | + |
| 761 | + if( in_array('replaceafter', $options, true ) ) { |
| 762 | + $string = wfMsgReplaceArgs( $string, $args ); |
| 763 | + } |
| 764 | + |
| 765 | + return $string; |
| 766 | +} |
| 767 | + |
| 768 | + |
| 769 | +/** |
| 770 | + * Just like exit() but makes a note of it. |
| 771 | + * Commits open transactions except if the error parameter is set |
| 772 | + * |
| 773 | + * @deprecated Please return control to the caller or throw an exception |
| 774 | + */ |
| 775 | +function wfAbruptExit( $error = false ){ |
| 776 | + static $called = false; |
| 777 | + if ( $called ){ |
| 778 | + exit( -1 ); |
| 779 | + } |
| 780 | + $called = true; |
| 781 | + |
| 782 | + $bt = wfDebugBacktrace(); |
| 783 | + if( $bt ) { |
| 784 | + for($i = 0; $i < count($bt) ; $i++){ |
| 785 | + $file = isset($bt[$i]['file']) ? $bt[$i]['file'] : "unknown"; |
| 786 | + $line = isset($bt[$i]['line']) ? $bt[$i]['line'] : "unknown"; |
| 787 | + wfDebug("WARNING: Abrupt exit in $file at line $line\n"); |
| 788 | + } |
| 789 | + } else { |
| 790 | + wfDebug("WARNING: Abrupt exit\n"); |
| 791 | + } |
| 792 | + |
| 793 | + wfLogProfilingData(); |
| 794 | + |
| 795 | + if ( !$error ) { |
| 796 | + wfGetLB()->closeAll(); |
| 797 | + } |
| 798 | + exit( -1 ); |
| 799 | +} |
| 800 | + |
| 801 | +/** |
| 802 | + * @deprecated Please return control the caller or throw an exception |
| 803 | + */ |
| 804 | +function wfErrorExit() { |
| 805 | + wfAbruptExit( true ); |
| 806 | +} |
| 807 | + |
| 808 | +/** |
| 809 | + * Print a simple message and die, returning nonzero to the shell if any. |
| 810 | + * Plain die() fails to return nonzero to the shell if you pass a string. |
| 811 | + * @param string $msg |
| 812 | + */ |
| 813 | +function wfDie( $msg='' ) { |
| 814 | + echo $msg; |
| 815 | + die( 1 ); |
| 816 | +} |
| 817 | + |
| 818 | +/** |
| 819 | + * Throw a debugging exception. This function previously once exited the process, |
| 820 | + * but now throws an exception instead, with similar results. |
| 821 | + * |
| 822 | + * @param string $msg Message shown when dieing. |
| 823 | + */ |
| 824 | +function wfDebugDieBacktrace( $msg = '' ) { |
| 825 | + throw new MWException( $msg ); |
| 826 | +} |
| 827 | + |
| 828 | +/** |
| 829 | + * Fetch server name for use in error reporting etc. |
| 830 | + * Use real server name if available, so we know which machine |
| 831 | + * in a server farm generated the current page. |
| 832 | + * @return string |
| 833 | + */ |
| 834 | +function wfHostname() { |
| 835 | + static $host; |
| 836 | + if ( is_null( $host ) ) { |
| 837 | + if ( function_exists( 'posix_uname' ) ) { |
| 838 | + // This function not present on Windows |
| 839 | + $uname = @posix_uname(); |
| 840 | + } else { |
| 841 | + $uname = false; |
| 842 | + } |
| 843 | + if( is_array( $uname ) && isset( $uname['nodename'] ) ) { |
| 844 | + $host = $uname['nodename']; |
| 845 | + } elseif ( getenv( 'COMPUTERNAME' ) ) { |
| 846 | + # Windows computer name |
| 847 | + $host = getenv( 'COMPUTERNAME' ); |
| 848 | + } else { |
| 849 | + # This may be a virtual server. |
| 850 | + $host = $_SERVER['SERVER_NAME']; |
| 851 | + } |
| 852 | + } |
| 853 | + return $host; |
| 854 | +} |
| 855 | + |
| 856 | + /** |
| 857 | + * Returns a HTML comment with the elapsed time since request. |
| 858 | + * This method has no side effects. |
| 859 | + * @return string |
| 860 | + */ |
| 861 | + function wfReportTime() { |
| 862 | + global $wgRequestTime, $wgShowHostnames; |
| 863 | + |
| 864 | + $now = wfTime(); |
| 865 | + $elapsed = $now - $wgRequestTime; |
| 866 | + |
| 867 | + return $wgShowHostnames |
| 868 | + ? sprintf( "<!-- Served by %s in %01.3f secs. -->", wfHostname(), $elapsed ) |
| 869 | + : sprintf( "<!-- Served in %01.3f secs. -->", $elapsed ); |
| 870 | + } |
| 871 | + |
| 872 | +/** |
| 873 | + * Safety wrapper for debug_backtrace(). |
| 874 | + * |
| 875 | + * With Zend Optimizer 3.2.0 loaded, this causes segfaults under somewhat |
| 876 | + * murky circumstances, which may be triggered in part by stub objects |
| 877 | + * or other fancy talkin'. |
| 878 | + * |
| 879 | + * Will return an empty array if Zend Optimizer is detected or if |
| 880 | + * debug_backtrace is disabled, otherwise the output from |
| 881 | + * debug_backtrace() (trimmed). |
| 882 | + * |
| 883 | + * @return array of backtrace information |
| 884 | + */ |
| 885 | +function wfDebugBacktrace() { |
| 886 | + static $disabled = null; |
| 887 | + |
| 888 | + if( extension_loaded( 'Zend Optimizer' ) ) { |
| 889 | + wfDebug( "Zend Optimizer detected; skipping debug_backtrace for safety.\n" ); |
| 890 | + return array(); |
| 891 | + } |
| 892 | + |
| 893 | + if ( is_null( $disabled ) ) { |
| 894 | + $disabled = false; |
| 895 | + $functions = explode( ',', ini_get( 'disable_functions' ) ); |
| 896 | + $functions = array_map( 'trim', $functions ); |
| 897 | + $functions = array_map( 'strtolower', $functions ); |
| 898 | + if ( in_array( 'debug_backtrace', $functions ) ) { |
| 899 | + wfDebug( "debug_backtrace is in disabled_functions\n" ); |
| 900 | + $disabled = true; |
| 901 | + } |
| 902 | + } |
| 903 | + if ( $disabled ) { |
| 904 | + return array(); |
| 905 | + } |
| 906 | + |
| 907 | + return array_slice( debug_backtrace(), 1 ); |
| 908 | +} |
| 909 | + |
| 910 | +function wfBacktrace() { |
| 911 | + global $wgCommandLineMode; |
| 912 | + |
| 913 | + if ( $wgCommandLineMode ) { |
| 914 | + $msg = ''; |
| 915 | + } else { |
| 916 | + $msg = "<ul>\n"; |
| 917 | + } |
| 918 | + $backtrace = wfDebugBacktrace(); |
| 919 | + foreach( $backtrace as $call ) { |
| 920 | + if( isset( $call['file'] ) ) { |
| 921 | + $f = explode( DIRECTORY_SEPARATOR, $call['file'] ); |
| 922 | + $file = $f[count($f)-1]; |
| 923 | + } else { |
| 924 | + $file = '-'; |
| 925 | + } |
| 926 | + if( isset( $call['line'] ) ) { |
| 927 | + $line = $call['line']; |
| 928 | + } else { |
| 929 | + $line = '-'; |
| 930 | + } |
| 931 | + if ( $wgCommandLineMode ) { |
| 932 | + $msg .= "$file line $line calls "; |
| 933 | + } else { |
| 934 | + $msg .= '<li>' . $file . ' line ' . $line . ' calls '; |
| 935 | + } |
| 936 | + if( !empty( $call['class'] ) ) $msg .= $call['class'] . '::'; |
| 937 | + $msg .= $call['function'] . '()'; |
| 938 | + |
| 939 | + if ( $wgCommandLineMode ) { |
| 940 | + $msg .= "\n"; |
| 941 | + } else { |
| 942 | + $msg .= "</li>\n"; |
| 943 | + } |
| 944 | + } |
| 945 | + if ( $wgCommandLineMode ) { |
| 946 | + $msg .= "\n"; |
| 947 | + } else { |
| 948 | + $msg .= "</ul>\n"; |
| 949 | + } |
| 950 | + |
| 951 | + return $msg; |
| 952 | +} |
| 953 | + |
| 954 | + |
| 955 | +/* Some generic result counters, pulled out of SearchEngine */ |
| 956 | + |
| 957 | + |
| 958 | +/** |
| 959 | + * @todo document |
| 960 | + */ |
| 961 | +function wfShowingResults( $offset, $limit ) { |
| 962 | + global $wgLang; |
| 963 | + return wfMsgExt( 'showingresults', array( 'parseinline' ), $wgLang->formatNum( $limit ), |
| 964 | + $wgLang->formatNum( $offset+1 ) ); |
| 965 | +} |
| 966 | + |
| 967 | +/** |
| 968 | + * @todo document |
| 969 | + */ |
| 970 | +function wfShowingResultsNum( $offset, $limit, $num ) { |
| 971 | + global $wgLang; |
| 972 | + return wfMsgExt( 'showingresultsnum', array( 'parseinline' ), $wgLang->formatNum( $limit ), |
| 973 | + $wgLang->formatNum( $offset+1 ), $wgLang->formatNum( $num ) ); |
| 974 | +} |
| 975 | + |
| 976 | +/** |
| 977 | + * Generate (prev x| next x) (20|50|100...) type links for paging |
| 978 | + * @param $offset string |
| 979 | + * @param $limit int |
| 980 | + * @param $link string |
| 981 | + * @param $query string, optional URL query parameter string |
| 982 | + * @param $atend bool, optional param for specified if this is the last page |
| 983 | + */ |
| 984 | +function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { |
| 985 | + global $wgLang; |
| 986 | + $fmtLimit = $wgLang->formatNum( $limit ); |
| 987 | + # Get prev/next link display text |
| 988 | + $prev = wfMsgHtml( 'prevn', $fmtLimit ); |
| 989 | + $next = wfMsgHtml( 'nextn', $fmtLimit ); |
| 990 | + # Get prev/next link title text |
| 991 | + $pTitle = wfMsgExt( 'prevn-title', array('parsemag','escape'), $fmtLimit ); |
| 992 | + $nTitle = wfMsgExt( 'nextn-title', array('parsemag','escape'), $fmtLimit ); |
| 993 | + # Fetch the title object |
| 994 | + if( is_object( $link ) ) { |
| 995 | + $title =& $link; |
| 996 | + } else { |
| 997 | + $title = Title::newFromText( $link ); |
| 998 | + if( is_null( $title ) ) { |
| 999 | + return false; |
| 1000 | + } |
| 1001 | + } |
| 1002 | + # Make 'previous' link |
| 1003 | + if( 0 != $offset ) { |
| 1004 | + $po = $offset - $limit; |
| 1005 | + $po = max($po,0); |
| 1006 | + $q = "limit={$limit}&offset={$po}"; |
| 1007 | + if( $query != '' ) { |
| 1008 | + $q .= '&'.$query; |
| 1009 | + } |
| 1010 | + $plink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" title=\"{$pTitle}\" class=\"mw-prevlink\">{$prev}</a>"; |
| 1011 | + } else { |
| 1012 | + $plink = $prev; |
| 1013 | + } |
| 1014 | + # Make 'next' link |
| 1015 | + $no = $offset + $limit; |
| 1016 | + $q = "limit={$limit}&offset={$no}"; |
| 1017 | + if( $query != '' ) { |
| 1018 | + $q .= '&'.$query; |
| 1019 | + } |
| 1020 | + if( $atend ) { |
| 1021 | + $nlink = $next; |
| 1022 | + } else { |
| 1023 | + $nlink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" title=\"{$nTitle}\" class=\"mw-nextlink\">{$next}</a>"; |
| 1024 | + } |
| 1025 | + # Make links to set number of items per page |
| 1026 | + $nums = $wgLang->pipeList( array( |
| 1027 | + wfNumLink( $offset, 20, $title, $query ), |
| 1028 | + wfNumLink( $offset, 50, $title, $query ), |
| 1029 | + wfNumLink( $offset, 100, $title, $query ), |
| 1030 | + wfNumLink( $offset, 250, $title, $query ), |
| 1031 | + wfNumLink( $offset, 500, $title, $query ) |
| 1032 | + ) ); |
| 1033 | + return wfMsg( 'viewprevnext', $plink, $nlink, $nums ); |
| 1034 | +} |
| 1035 | + |
| 1036 | +/** |
| 1037 | + * Generate links for (20|50|100...) items-per-page links |
| 1038 | + * @param $offset string |
| 1039 | + * @param $limit int |
| 1040 | + * @param $title Title |
| 1041 | + * @param $query string, optional URL query parameter string |
| 1042 | + */ |
| 1043 | +function wfNumLink( $offset, $limit, $title, $query = '' ) { |
| 1044 | + global $wgLang; |
| 1045 | + if( $query == '' ) { |
| 1046 | + $q = ''; |
| 1047 | + } else { |
| 1048 | + $q = $query.'&'; |
| 1049 | + } |
| 1050 | + $q .= "limit={$limit}&offset={$offset}"; |
| 1051 | + $fmtLimit = $wgLang->formatNum( $limit ); |
| 1052 | + $lTitle = wfMsgExt('shown-title',array('parsemag','escape'),$limit); |
| 1053 | + $s = '<a href="' . $title->escapeLocalUrl( $q ) . "\" title=\"{$lTitle}\" class=\"mw-numlink\">{$fmtLimit}</a>"; |
| 1054 | + return $s; |
| 1055 | +} |
| 1056 | + |
| 1057 | +/** |
| 1058 | + * @todo document |
| 1059 | + * @todo FIXME: we may want to blacklist some broken browsers |
| 1060 | + * |
| 1061 | + * @return bool Whereas client accept gzip compression |
| 1062 | + */ |
| 1063 | +function wfClientAcceptsGzip() { |
| 1064 | + global $wgUseGzip; |
| 1065 | + if( $wgUseGzip ) { |
| 1066 | + # FIXME: we may want to blacklist some broken browsers |
| 1067 | + $m = array(); |
| 1068 | + if( preg_match( |
| 1069 | + '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/', |
| 1070 | + $_SERVER['HTTP_ACCEPT_ENCODING'], |
| 1071 | + $m ) ) { |
| 1072 | + if( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) return false; |
| 1073 | + wfDebug( " accepts gzip\n" ); |
| 1074 | + return true; |
| 1075 | + } |
| 1076 | + } |
| 1077 | + return false; |
| 1078 | +} |
| 1079 | + |
| 1080 | +/** |
| 1081 | + * Obtain the offset and limit values from the request string; |
| 1082 | + * used in special pages |
| 1083 | + * |
| 1084 | + * @param $deflimit Default limit if none supplied |
| 1085 | + * @param $optionname Name of a user preference to check against |
| 1086 | + * @return array |
| 1087 | + * |
| 1088 | + */ |
| 1089 | +function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) { |
| 1090 | + global $wgRequest; |
| 1091 | + return $wgRequest->getLimitOffset( $deflimit, $optionname ); |
| 1092 | +} |
| 1093 | + |
| 1094 | +/** |
| 1095 | + * Escapes the given text so that it may be output using addWikiText() |
| 1096 | + * without any linking, formatting, etc. making its way through. This |
| 1097 | + * is achieved by substituting certain characters with HTML entities. |
| 1098 | + * As required by the callers, <nowiki> is not used. It currently does |
| 1099 | + * not filter out characters which have special meaning only at the |
| 1100 | + * start of a line, such as "*". |
| 1101 | + * |
| 1102 | + * @param string $text Text to be escaped |
| 1103 | + */ |
| 1104 | +function wfEscapeWikiText( $text ) { |
| 1105 | + $text = str_replace( |
| 1106 | + array( '[', '|', ']', '\'', 'ISBN ', 'RFC ', '://', "\n=", '{{' ), # }} |
| 1107 | + array( '[', '|', ']', ''', 'ISBN ', 'RFC ', '://', "\n=", '{{' ), |
| 1108 | + htmlspecialchars($text) ); |
| 1109 | + return $text; |
| 1110 | +} |
| 1111 | + |
| 1112 | +/** |
| 1113 | + * @todo document |
| 1114 | + */ |
| 1115 | +function wfQuotedPrintable( $string, $charset = '' ) { |
| 1116 | + # Probably incomplete; see RFC 2045 |
| 1117 | + if( empty( $charset ) ) { |
| 1118 | + global $wgInputEncoding; |
| 1119 | + $charset = $wgInputEncoding; |
| 1120 | + } |
| 1121 | + $charset = strtoupper( $charset ); |
| 1122 | + $charset = str_replace( 'ISO-8859', 'ISO8859', $charset ); // ? |
| 1123 | + |
| 1124 | + $illegal = '\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff='; |
| 1125 | + $replace = $illegal . '\t ?_'; |
| 1126 | + if( !preg_match( "/[$illegal]/", $string ) ) return $string; |
| 1127 | + $out = "=?$charset?Q?"; |
| 1128 | + $out .= preg_replace( "/([$replace])/e", 'sprintf("=%02X",ord("$1"))', $string ); |
| 1129 | + $out .= '?='; |
| 1130 | + return $out; |
| 1131 | +} |
| 1132 | + |
| 1133 | + |
| 1134 | +/** |
| 1135 | + * @todo document |
| 1136 | + * @return float |
| 1137 | + */ |
| 1138 | +function wfTime() { |
| 1139 | + return microtime(true); |
| 1140 | +} |
| 1141 | + |
| 1142 | +/** |
| 1143 | + * Sets dest to source and returns the original value of dest |
| 1144 | + * If source is NULL, it just returns the value, it doesn't set the variable |
| 1145 | + */ |
| 1146 | +function wfSetVar( &$dest, $source ) { |
| 1147 | + $temp = $dest; |
| 1148 | + if ( !is_null( $source ) ) { |
| 1149 | + $dest = $source; |
| 1150 | + } |
| 1151 | + return $temp; |
| 1152 | +} |
| 1153 | + |
| 1154 | +/** |
| 1155 | + * As for wfSetVar except setting a bit |
| 1156 | + */ |
| 1157 | +function wfSetBit( &$dest, $bit, $state = true ) { |
| 1158 | + $temp = (bool)($dest & $bit ); |
| 1159 | + if ( !is_null( $state ) ) { |
| 1160 | + if ( $state ) { |
| 1161 | + $dest |= $bit; |
| 1162 | + } else { |
| 1163 | + $dest &= ~$bit; |
| 1164 | + } |
| 1165 | + } |
| 1166 | + return $temp; |
| 1167 | +} |
| 1168 | + |
| 1169 | +/** |
| 1170 | + * This function takes two arrays as input, and returns a CGI-style string, e.g. |
| 1171 | + * "days=7&limit=100". Options in the first array override options in the second. |
| 1172 | + * Options set to "" will not be output. |
| 1173 | + */ |
| 1174 | +function wfArrayToCGI( $array1, $array2 = NULL ) |
| 1175 | +{ |
| 1176 | + if ( !is_null( $array2 ) ) { |
| 1177 | + $array1 = $array1 + $array2; |
| 1178 | + } |
| 1179 | + |
| 1180 | + $cgi = ''; |
| 1181 | + foreach ( $array1 as $key => $value ) { |
| 1182 | + if ( '' !== $value ) { |
| 1183 | + if ( '' != $cgi ) { |
| 1184 | + $cgi .= '&'; |
| 1185 | + } |
| 1186 | + if(is_array($value)) |
| 1187 | + { |
| 1188 | + $firstTime = true; |
| 1189 | + foreach($value as $v) |
| 1190 | + { |
| 1191 | + $cgi .= ($firstTime ? '' : '&') . |
| 1192 | + urlencode( $key . '[]' ) . '=' . |
| 1193 | + urlencode( $v ); |
| 1194 | + $firstTime = false; |
| 1195 | + } |
| 1196 | + } |
| 1197 | + else |
| 1198 | + $cgi .= urlencode( $key ) . '=' . |
| 1199 | + urlencode( $value ); |
| 1200 | + } |
| 1201 | + } |
| 1202 | + return $cgi; |
| 1203 | +} |
| 1204 | + |
| 1205 | +/** |
| 1206 | + * This is the logical opposite of wfArrayToCGI(): it accepts a query string as |
| 1207 | + * its argument and returns the same string in array form. This allows compa- |
| 1208 | + * tibility with legacy functions that accept raw query strings instead of nice |
| 1209 | + * arrays. Of course, keys and values are urldecode()d. Don't try passing in- |
| 1210 | + * valid query strings, or it will explode. |
| 1211 | + * |
| 1212 | + * @param $query string Query string |
| 1213 | + * @return array Array version of input |
| 1214 | + */ |
| 1215 | +function wfCgiToArray( $query ) { |
| 1216 | + if( isset( $query[0] ) and $query[0] == '?' ) { |
| 1217 | + $query = substr( $query, 1 ); |
| 1218 | + } |
| 1219 | + $bits = explode( '&', $query ); |
| 1220 | + $ret = array(); |
| 1221 | + foreach( $bits as $bit ) { |
| 1222 | + if( $bit === '' ) { |
| 1223 | + continue; |
| 1224 | + } |
| 1225 | + list( $key, $value ) = explode( '=', $bit ); |
| 1226 | + $key = urldecode( $key ); |
| 1227 | + $value = urldecode( $value ); |
| 1228 | + $ret[$key] = $value; |
| 1229 | + } |
| 1230 | + return $ret; |
| 1231 | +} |
| 1232 | + |
| 1233 | +/** |
| 1234 | + * Append a query string to an existing URL, which may or may not already |
| 1235 | + * have query string parameters already. If so, they will be combined. |
| 1236 | + * |
| 1237 | + * @param string $url |
| 1238 | + * @param string $query |
| 1239 | + * @return string |
| 1240 | + */ |
| 1241 | +function wfAppendQuery( $url, $query ) { |
| 1242 | + if( $query != '' ) { |
| 1243 | + if( false === strpos( $url, '?' ) ) { |
| 1244 | + $url .= '?'; |
| 1245 | + } else { |
| 1246 | + $url .= '&'; |
| 1247 | + } |
| 1248 | + $url .= $query; |
| 1249 | + } |
| 1250 | + return $url; |
| 1251 | +} |
| 1252 | + |
| 1253 | +/** |
| 1254 | + * Expand a potentially local URL to a fully-qualified URL. |
| 1255 | + * Assumes $wgServer is correct. :) |
| 1256 | + * @param string $url, either fully-qualified or a local path + query |
| 1257 | + * @return string Fully-qualified URL |
| 1258 | + */ |
| 1259 | +function wfExpandUrl( $url ) { |
| 1260 | + if( substr( $url, 0, 1 ) == '/' ) { |
| 1261 | + global $wgServer; |
| 1262 | + return $wgServer . $url; |
| 1263 | + } else { |
| 1264 | + return $url; |
| 1265 | + } |
| 1266 | +} |
| 1267 | + |
| 1268 | +/** |
| 1269 | + * This is obsolete, use SquidUpdate::purge() |
| 1270 | + * @deprecated |
| 1271 | + */ |
| 1272 | +function wfPurgeSquidServers ($urlArr) { |
| 1273 | + SquidUpdate::purge( $urlArr ); |
| 1274 | +} |
| 1275 | + |
| 1276 | +/** |
| 1277 | + * Windows-compatible version of escapeshellarg() |
| 1278 | + * Windows doesn't recognise single-quotes in the shell, but the escapeshellarg() |
| 1279 | + * function puts single quotes in regardless of OS. |
| 1280 | + * |
| 1281 | + * Also fixes the locale problems on Linux in PHP 5.2.6+ (bug backported to |
| 1282 | + * earlier distro releases of PHP) |
| 1283 | + */ |
| 1284 | +function wfEscapeShellArg( ) { |
| 1285 | + wfInitShellLocale(); |
| 1286 | + |
| 1287 | + $args = func_get_args(); |
| 1288 | + $first = true; |
| 1289 | + $retVal = ''; |
| 1290 | + foreach ( $args as $arg ) { |
| 1291 | + if ( !$first ) { |
| 1292 | + $retVal .= ' '; |
| 1293 | + } else { |
| 1294 | + $first = false; |
| 1295 | + } |
| 1296 | + |
| 1297 | + if ( wfIsWindows() ) { |
| 1298 | + // Escaping for an MSVC-style command line parser |
| 1299 | + // Ref: http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html |
| 1300 | + // Double the backslashes before any double quotes. Escape the double quotes. |
| 1301 | + $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE ); |
| 1302 | + $arg = ''; |
| 1303 | + $delim = false; |
| 1304 | + foreach ( $tokens as $token ) { |
| 1305 | + if ( $delim ) { |
| 1306 | + $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"'; |
| 1307 | + } else { |
| 1308 | + $arg .= $token; |
| 1309 | + } |
| 1310 | + $delim = !$delim; |
| 1311 | + } |
| 1312 | + // Double the backslashes before the end of the string, because |
| 1313 | + // we will soon add a quote |
| 1314 | + $m = array(); |
| 1315 | + if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) { |
| 1316 | + $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] ); |
| 1317 | + } |
| 1318 | + |
| 1319 | + // Add surrounding quotes |
| 1320 | + $retVal .= '"' . $arg . '"'; |
| 1321 | + } else { |
| 1322 | + $retVal .= escapeshellarg( $arg ); |
| 1323 | + } |
| 1324 | + } |
| 1325 | + return $retVal; |
| 1326 | +} |
| 1327 | + |
| 1328 | +/** |
| 1329 | + * wfMerge attempts to merge differences between three texts. |
| 1330 | + * Returns true for a clean merge and false for failure or a conflict. |
| 1331 | + */ |
| 1332 | +function wfMerge( $old, $mine, $yours, &$result ){ |
| 1333 | + global $wgDiff3; |
| 1334 | + |
| 1335 | + # This check may also protect against code injection in |
| 1336 | + # case of broken installations. |
| 1337 | + if( !$wgDiff3 || !file_exists( $wgDiff3 ) ) { |
| 1338 | + wfDebug( "diff3 not found\n" ); |
| 1339 | + return false; |
| 1340 | + } |
| 1341 | + |
| 1342 | + # Make temporary files |
| 1343 | + $td = wfTempDir(); |
| 1344 | + $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' ); |
| 1345 | + $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' ); |
| 1346 | + $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' ); |
| 1347 | + |
| 1348 | + fwrite( $oldtextFile, $old ); fclose( $oldtextFile ); |
| 1349 | + fwrite( $mytextFile, $mine ); fclose( $mytextFile ); |
| 1350 | + fwrite( $yourtextFile, $yours ); fclose( $yourtextFile ); |
| 1351 | + |
| 1352 | + # Check for a conflict |
| 1353 | + $cmd = $wgDiff3 . ' -a --overlap-only ' . |
| 1354 | + wfEscapeShellArg( $mytextName ) . ' ' . |
| 1355 | + wfEscapeShellArg( $oldtextName ) . ' ' . |
| 1356 | + wfEscapeShellArg( $yourtextName ); |
| 1357 | + $handle = popen( $cmd, 'r' ); |
| 1358 | + |
| 1359 | + if( fgets( $handle, 1024 ) ){ |
| 1360 | + $conflict = true; |
| 1361 | + } else { |
| 1362 | + $conflict = false; |
| 1363 | + } |
| 1364 | + pclose( $handle ); |
| 1365 | + |
| 1366 | + # Merge differences |
| 1367 | + $cmd = $wgDiff3 . ' -a -e --merge ' . |
| 1368 | + wfEscapeShellArg( $mytextName, $oldtextName, $yourtextName ); |
| 1369 | + $handle = popen( $cmd, 'r' ); |
| 1370 | + $result = ''; |
| 1371 | + do { |
| 1372 | + $data = fread( $handle, 8192 ); |
| 1373 | + if ( strlen( $data ) == 0 ) { |
| 1374 | + break; |
| 1375 | + } |
| 1376 | + $result .= $data; |
| 1377 | + } while ( true ); |
| 1378 | + pclose( $handle ); |
| 1379 | + unlink( $mytextName ); unlink( $oldtextName ); unlink( $yourtextName ); |
| 1380 | + |
| 1381 | + if ( $result === '' && $old !== '' && $conflict == false ) { |
| 1382 | + wfDebug( "Unexpected null result from diff3. Command: $cmd\n" ); |
| 1383 | + $conflict = true; |
| 1384 | + } |
| 1385 | + return ! $conflict; |
| 1386 | +} |
| 1387 | + |
| 1388 | +/** |
| 1389 | + * Returns unified plain-text diff of two texts. |
| 1390 | + * Useful for machine processing of diffs. |
| 1391 | + * @param $before string The text before the changes. |
| 1392 | + * @param $after string The text after the changes. |
| 1393 | + * @param $params string Command-line options for the diff command. |
| 1394 | + * @return string Unified diff of $before and $after |
| 1395 | + */ |
| 1396 | +function wfDiff( $before, $after, $params = '-u' ) { |
| 1397 | + global $wgDiff; |
| 1398 | + |
| 1399 | + # This check may also protect against code injection in |
| 1400 | + # case of broken installations. |
| 1401 | + if( !file_exists( $wgDiff ) ){ |
| 1402 | + wfDebug( "diff executable not found\n" ); |
| 1403 | + $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) ); |
| 1404 | + $format = new UnifiedDiffFormatter(); |
| 1405 | + return $format->format( $diffs ); |
| 1406 | + } |
| 1407 | + |
| 1408 | + # Make temporary files |
| 1409 | + $td = wfTempDir(); |
| 1410 | + $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' ); |
| 1411 | + $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' ); |
| 1412 | + |
| 1413 | + fwrite( $oldtextFile, $before ); fclose( $oldtextFile ); |
| 1414 | + fwrite( $newtextFile, $after ); fclose( $newtextFile ); |
| 1415 | + |
| 1416 | + // Get the diff of the two files |
| 1417 | + $cmd = "$wgDiff " . $params . ' ' .wfEscapeShellArg( $oldtextName, $newtextName ); |
| 1418 | + |
| 1419 | + $h = popen( $cmd, 'r' ); |
| 1420 | + |
| 1421 | + $diff = ''; |
| 1422 | + |
| 1423 | + do { |
| 1424 | + $data = fread( $h, 8192 ); |
| 1425 | + if ( strlen( $data ) == 0 ) { |
| 1426 | + break; |
| 1427 | + } |
| 1428 | + $diff .= $data; |
| 1429 | + } while ( true ); |
| 1430 | + |
| 1431 | + // Clean up |
| 1432 | + pclose( $h ); |
| 1433 | + unlink( $oldtextName ); |
| 1434 | + unlink( $newtextName ); |
| 1435 | + |
| 1436 | + // Kill the --- and +++ lines. They're not useful. |
| 1437 | + $diff_lines = explode( "\n", $diff ); |
| 1438 | + if (strpos( $diff_lines[0], '---' ) === 0) { |
| 1439 | + unset($diff_lines[0]); |
| 1440 | + } |
| 1441 | + if (strpos( $diff_lines[1], '+++' ) === 0) { |
| 1442 | + unset($diff_lines[1]); |
| 1443 | + } |
| 1444 | + |
| 1445 | + $diff = implode( "\n", $diff_lines ); |
| 1446 | + |
| 1447 | + return $diff; |
| 1448 | +} |
| 1449 | + |
| 1450 | +/** |
| 1451 | + * A wrapper around the PHP function var_export(). |
| 1452 | + * Either print it or add it to the regular output ($wgOut). |
| 1453 | + * |
| 1454 | + * @param $var A PHP variable to dump. |
| 1455 | + */ |
| 1456 | +function wfVarDump( $var ) { |
| 1457 | + global $wgOut; |
| 1458 | + $s = str_replace("\n","<br />\n", var_export( $var, true ) . "\n"); |
| 1459 | + if ( headers_sent() || !@is_object( $wgOut ) ) { |
| 1460 | + print $s; |
| 1461 | + } else { |
| 1462 | + $wgOut->addHTML( $s ); |
| 1463 | + } |
| 1464 | +} |
| 1465 | + |
| 1466 | +/** |
| 1467 | + * Provide a simple HTTP error. |
| 1468 | + */ |
| 1469 | +function wfHttpError( $code, $label, $desc ) { |
| 1470 | + global $wgOut; |
| 1471 | + $wgOut->disable(); |
| 1472 | + header( "HTTP/1.0 $code $label" ); |
| 1473 | + header( "Status: $code $label" ); |
| 1474 | + $wgOut->sendCacheControl(); |
| 1475 | + |
| 1476 | + header( 'Content-type: text/html; charset=utf-8' ); |
| 1477 | + print "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">". |
| 1478 | + "<html><head><title>" . |
| 1479 | + htmlspecialchars( $label ) . |
| 1480 | + "</title></head><body><h1>" . |
| 1481 | + htmlspecialchars( $label ) . |
| 1482 | + "</h1><p>" . |
| 1483 | + nl2br( htmlspecialchars( $desc ) ) . |
| 1484 | + "</p></body></html>\n"; |
| 1485 | +} |
| 1486 | + |
| 1487 | +/** |
| 1488 | + * Clear away any user-level output buffers, discarding contents. |
| 1489 | + * |
| 1490 | + * Suitable for 'starting afresh', for instance when streaming |
| 1491 | + * relatively large amounts of data without buffering, or wanting to |
| 1492 | + * output image files without ob_gzhandler's compression. |
| 1493 | + * |
| 1494 | + * The optional $resetGzipEncoding parameter controls suppression of |
| 1495 | + * the Content-Encoding header sent by ob_gzhandler; by default it |
| 1496 | + * is left. See comments for wfClearOutputBuffers() for why it would |
| 1497 | + * be used. |
| 1498 | + * |
| 1499 | + * Note that some PHP configuration options may add output buffer |
| 1500 | + * layers which cannot be removed; these are left in place. |
| 1501 | + * |
| 1502 | + * @param bool $resetGzipEncoding |
| 1503 | + */ |
| 1504 | +function wfResetOutputBuffers( $resetGzipEncoding=true ) { |
| 1505 | + if( $resetGzipEncoding ) { |
| 1506 | + // Suppress Content-Encoding and Content-Length |
| 1507 | + // headers from 1.10+s wfOutputHandler |
| 1508 | + global $wgDisableOutputCompression; |
| 1509 | + $wgDisableOutputCompression = true; |
| 1510 | + } |
| 1511 | + while( $status = ob_get_status() ) { |
| 1512 | + if( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) { |
| 1513 | + // Probably from zlib.output_compression or other |
| 1514 | + // PHP-internal setting which can't be removed. |
| 1515 | + // |
| 1516 | + // Give up, and hope the result doesn't break |
| 1517 | + // output behavior. |
| 1518 | + break; |
| 1519 | + } |
| 1520 | + if( !ob_end_clean() ) { |
| 1521 | + // Could not remove output buffer handler; abort now |
| 1522 | + // to avoid getting in some kind of infinite loop. |
| 1523 | + break; |
| 1524 | + } |
| 1525 | + if( $resetGzipEncoding ) { |
| 1526 | + if( $status['name'] == 'ob_gzhandler' ) { |
| 1527 | + // Reset the 'Content-Encoding' field set by this handler |
| 1528 | + // so we can start fresh. |
| 1529 | + header( 'Content-Encoding:' ); |
| 1530 | + break; |
| 1531 | + } |
| 1532 | + } |
| 1533 | + } |
| 1534 | +} |
| 1535 | + |
| 1536 | +/** |
| 1537 | + * More legible than passing a 'false' parameter to wfResetOutputBuffers(): |
| 1538 | + * |
| 1539 | + * Clear away output buffers, but keep the Content-Encoding header |
| 1540 | + * produced by ob_gzhandler, if any. |
| 1541 | + * |
| 1542 | + * This should be used for HTTP 304 responses, where you need to |
| 1543 | + * preserve the Content-Encoding header of the real result, but |
| 1544 | + * also need to suppress the output of ob_gzhandler to keep to spec |
| 1545 | + * and avoid breaking Firefox in rare cases where the headers and |
| 1546 | + * body are broken over two packets. |
| 1547 | + */ |
| 1548 | +function wfClearOutputBuffers() { |
| 1549 | + wfResetOutputBuffers( false ); |
| 1550 | +} |
| 1551 | + |
| 1552 | +/** |
| 1553 | + * Converts an Accept-* header into an array mapping string values to quality |
| 1554 | + * factors |
| 1555 | + */ |
| 1556 | +function wfAcceptToPrefs( $accept, $def = '*/*' ) { |
| 1557 | + # No arg means accept anything (per HTTP spec) |
| 1558 | + if( !$accept ) { |
| 1559 | + return array( $def => 1.0 ); |
| 1560 | + } |
| 1561 | + |
| 1562 | + $prefs = array(); |
| 1563 | + |
| 1564 | + $parts = explode( ',', $accept ); |
| 1565 | + |
| 1566 | + foreach( $parts as $part ) { |
| 1567 | + # FIXME: doesn't deal with params like 'text/html; level=1' |
| 1568 | + @list( $value, $qpart ) = explode( ';', trim( $part ) ); |
| 1569 | + $match = array(); |
| 1570 | + if( !isset( $qpart ) ) { |
| 1571 | + $prefs[$value] = 1.0; |
| 1572 | + } elseif( preg_match( '/q\s*=\s*(\d*\.\d+)/', $qpart, $match ) ) { |
| 1573 | + $prefs[$value] = floatval($match[1]); |
| 1574 | + } |
| 1575 | + } |
| 1576 | + |
| 1577 | + return $prefs; |
| 1578 | +} |
| 1579 | + |
| 1580 | +/** |
| 1581 | + * Checks if a given MIME type matches any of the keys in the given |
| 1582 | + * array. Basic wildcards are accepted in the array keys. |
| 1583 | + * |
| 1584 | + * Returns the matching MIME type (or wildcard) if a match, otherwise |
| 1585 | + * NULL if no match. |
| 1586 | + * |
| 1587 | + * @param string $type |
| 1588 | + * @param array $avail |
| 1589 | + * @return string |
| 1590 | + * @private |
| 1591 | + */ |
| 1592 | +function mimeTypeMatch( $type, $avail ) { |
| 1593 | + if( array_key_exists($type, $avail) ) { |
| 1594 | + return $type; |
| 1595 | + } else { |
| 1596 | + $parts = explode( '/', $type ); |
| 1597 | + if( array_key_exists( $parts[0] . '/*', $avail ) ) { |
| 1598 | + return $parts[0] . '/*'; |
| 1599 | + } elseif( array_key_exists( '*/*', $avail ) ) { |
| 1600 | + return '*/*'; |
| 1601 | + } else { |
| 1602 | + return NULL; |
| 1603 | + } |
| 1604 | + } |
| 1605 | +} |
| 1606 | + |
| 1607 | +/** |
| 1608 | + * Returns the 'best' match between a client's requested internet media types |
| 1609 | + * and the server's list of available types. Each list should be an associative |
| 1610 | + * array of type to preference (preference is a float between 0.0 and 1.0). |
| 1611 | + * Wildcards in the types are acceptable. |
| 1612 | + * |
| 1613 | + * @param array $cprefs Client's acceptable type list |
| 1614 | + * @param array $sprefs Server's offered types |
| 1615 | + * @return string |
| 1616 | + * |
| 1617 | + * @todo FIXME: doesn't handle params like 'text/plain; charset=UTF-8' |
| 1618 | + * XXX: generalize to negotiate other stuff |
| 1619 | + */ |
| 1620 | +function wfNegotiateType( $cprefs, $sprefs ) { |
| 1621 | + $combine = array(); |
| 1622 | + |
| 1623 | + foreach( array_keys($sprefs) as $type ) { |
| 1624 | + $parts = explode( '/', $type ); |
| 1625 | + if( $parts[1] != '*' ) { |
| 1626 | + $ckey = mimeTypeMatch( $type, $cprefs ); |
| 1627 | + if( $ckey ) { |
| 1628 | + $combine[$type] = $sprefs[$type] * $cprefs[$ckey]; |
| 1629 | + } |
| 1630 | + } |
| 1631 | + } |
| 1632 | + |
| 1633 | + foreach( array_keys( $cprefs ) as $type ) { |
| 1634 | + $parts = explode( '/', $type ); |
| 1635 | + if( $parts[1] != '*' && !array_key_exists( $type, $sprefs ) ) { |
| 1636 | + $skey = mimeTypeMatch( $type, $sprefs ); |
| 1637 | + if( $skey ) { |
| 1638 | + $combine[$type] = $sprefs[$skey] * $cprefs[$type]; |
| 1639 | + } |
| 1640 | + } |
| 1641 | + } |
| 1642 | + |
| 1643 | + $bestq = 0; |
| 1644 | + $besttype = NULL; |
| 1645 | + |
| 1646 | + foreach( array_keys( $combine ) as $type ) { |
| 1647 | + if( $combine[$type] > $bestq ) { |
| 1648 | + $besttype = $type; |
| 1649 | + $bestq = $combine[$type]; |
| 1650 | + } |
| 1651 | + } |
| 1652 | + |
| 1653 | + return $besttype; |
| 1654 | +} |
| 1655 | + |
| 1656 | +/** |
| 1657 | + * Array lookup |
| 1658 | + * Returns an array where the values in the first array are replaced by the |
| 1659 | + * values in the second array with the corresponding keys |
| 1660 | + * |
| 1661 | + * @return array |
| 1662 | + */ |
| 1663 | +function wfArrayLookup( $a, $b ) { |
| 1664 | + return array_flip( array_intersect( array_flip( $a ), array_keys( $b ) ) ); |
| 1665 | +} |
| 1666 | + |
| 1667 | +/** |
| 1668 | + * Convenience function; returns MediaWiki timestamp for the present time. |
| 1669 | + * @return string |
| 1670 | + */ |
| 1671 | +function wfTimestampNow() { |
| 1672 | + # return NOW |
| 1673 | + return wfTimestamp( TS_MW, time() ); |
| 1674 | +} |
| 1675 | + |
| 1676 | +/** |
| 1677 | + * Reference-counted warning suppression |
| 1678 | + */ |
| 1679 | +function wfSuppressWarnings( $end = false ) { |
| 1680 | + static $suppressCount = 0; |
| 1681 | + static $originalLevel = false; |
| 1682 | + |
| 1683 | + if ( $end ) { |
| 1684 | + if ( $suppressCount ) { |
| 1685 | + --$suppressCount; |
| 1686 | + if ( !$suppressCount ) { |
| 1687 | + error_reporting( $originalLevel ); |
| 1688 | + } |
| 1689 | + } |
| 1690 | + } else { |
| 1691 | + if ( !$suppressCount ) { |
| 1692 | + $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE ) ); |
| 1693 | + } |
| 1694 | + ++$suppressCount; |
| 1695 | + } |
| 1696 | +} |
| 1697 | + |
| 1698 | +/** |
| 1699 | + * Restore error level to previous value |
| 1700 | + */ |
| 1701 | +function wfRestoreWarnings() { |
| 1702 | + wfSuppressWarnings( true ); |
| 1703 | +} |
| 1704 | + |
| 1705 | +# Autodetect, convert and provide timestamps of various types |
| 1706 | + |
| 1707 | +/** |
| 1708 | + * Unix time - the number of seconds since 1970-01-01 00:00:00 UTC |
| 1709 | + */ |
| 1710 | +define('TS_UNIX', 0); |
| 1711 | + |
| 1712 | +/** |
| 1713 | + * MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS) |
| 1714 | + */ |
| 1715 | +define('TS_MW', 1); |
| 1716 | + |
| 1717 | +/** |
| 1718 | + * MySQL DATETIME (YYYY-MM-DD HH:MM:SS) |
| 1719 | + */ |
| 1720 | +define('TS_DB', 2); |
| 1721 | + |
| 1722 | +/** |
| 1723 | + * RFC 2822 format, for E-mail and HTTP headers |
| 1724 | + */ |
| 1725 | +define('TS_RFC2822', 3); |
| 1726 | + |
| 1727 | +/** |
| 1728 | + * ISO 8601 format with no timezone: 1986-02-09T20:00:00Z |
| 1729 | + * |
| 1730 | + * This is used by Special:Export |
| 1731 | + */ |
| 1732 | +define('TS_ISO_8601', 4); |
| 1733 | + |
| 1734 | +/** |
| 1735 | + * An Exif timestamp (YYYY:MM:DD HH:MM:SS) |
| 1736 | + * |
| 1737 | + * @see http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the |
| 1738 | + * DateTime tag and page 36 for the DateTimeOriginal and |
| 1739 | + * DateTimeDigitized tags. |
| 1740 | + */ |
| 1741 | +define('TS_EXIF', 5); |
| 1742 | + |
| 1743 | +/** |
| 1744 | + * Oracle format time. |
| 1745 | + */ |
| 1746 | +define('TS_ORACLE', 6); |
| 1747 | + |
| 1748 | +/** |
| 1749 | + * Postgres format time. |
| 1750 | + */ |
| 1751 | +define('TS_POSTGRES', 7); |
| 1752 | + |
| 1753 | +/** |
| 1754 | + * DB2 format time |
| 1755 | + */ |
| 1756 | +define('TS_DB2', 8); |
| 1757 | + |
| 1758 | +/** |
| 1759 | + * @param mixed $outputtype A timestamp in one of the supported formats, the |
| 1760 | + * function will autodetect which format is supplied |
| 1761 | + * and act accordingly. |
| 1762 | + * @return string Time in the format specified in $outputtype |
| 1763 | + */ |
| 1764 | +function wfTimestamp($outputtype=TS_UNIX,$ts=0) { |
| 1765 | + $uts = 0; |
| 1766 | + $da = array(); |
| 1767 | + if ($ts==0) { |
| 1768 | + $uts=time(); |
| 1769 | + } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D',$ts,$da)) { |
| 1770 | + # TS_DB |
| 1771 | + } elseif (preg_match('/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D',$ts,$da)) { |
| 1772 | + # TS_EXIF |
| 1773 | + } elseif (preg_match('/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/D',$ts,$da)) { |
| 1774 | + # TS_MW |
| 1775 | + } elseif (preg_match('/^\d{1,13}$/D',$ts)) { |
| 1776 | + # TS_UNIX |
| 1777 | + $uts = $ts; |
| 1778 | + } elseif (preg_match('/^\d{1,2}-...-\d\d(?:\d\d)? \d\d\.\d\d\.\d\d/', $ts)) { |
| 1779 | + # TS_ORACLE |
| 1780 | + $uts = strtotime(preg_replace('/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3", |
| 1781 | + str_replace("+00:00", "UTC", $ts))); |
| 1782 | + } elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.*\d*)?Z$/', $ts, $da)) { |
| 1783 | + # TS_ISO_8601 |
| 1784 | + } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d*[\+\- ](\d\d)$/',$ts,$da)) { |
| 1785 | + # TS_POSTGRES |
| 1786 | + } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d* GMT$/',$ts,$da)) { |
| 1787 | + # TS_POSTGRES |
| 1788 | + } else { |
| 1789 | + # Bogus value; fall back to the epoch... |
| 1790 | + wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n"); |
| 1791 | + $uts = 0; |
| 1792 | + } |
| 1793 | + |
| 1794 | + if (count( $da ) ) { |
| 1795 | + // Warning! gmmktime() acts oddly if the month or day is set to 0 |
| 1796 | + // We may want to handle that explicitly at some point |
| 1797 | + $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6], |
| 1798 | + (int)$da[2],(int)$da[3],(int)$da[1]); |
| 1799 | + } |
| 1800 | + |
| 1801 | + switch($outputtype) { |
| 1802 | + case TS_UNIX: |
| 1803 | + return $uts; |
| 1804 | + case TS_MW: |
| 1805 | + return gmdate( 'YmdHis', $uts ); |
| 1806 | + case TS_DB: |
| 1807 | + return gmdate( 'Y-m-d H:i:s', $uts ); |
| 1808 | + case TS_ISO_8601: |
| 1809 | + return gmdate( 'Y-m-d\TH:i:s\Z', $uts ); |
| 1810 | + // This shouldn't ever be used, but is included for completeness |
| 1811 | + case TS_EXIF: |
| 1812 | + return gmdate( 'Y:m:d H:i:s', $uts ); |
| 1813 | + case TS_RFC2822: |
| 1814 | + return gmdate( 'D, d M Y H:i:s', $uts ) . ' GMT'; |
| 1815 | + case TS_ORACLE: |
| 1816 | + return gmdate( 'd-M-y h.i.s A', $uts) . ' +00:00'; |
| 1817 | + case TS_POSTGRES: |
| 1818 | + return gmdate( 'Y-m-d H:i:s', $uts) . ' GMT'; |
| 1819 | + case TS_DB2: |
| 1820 | + return gmdate( 'Y-m-d H:i:s', $uts); |
| 1821 | + default: |
| 1822 | + throw new MWException( 'wfTimestamp() called with illegal output type.'); |
| 1823 | + } |
| 1824 | +} |
| 1825 | + |
| 1826 | +/** |
| 1827 | + * Return a formatted timestamp, or null if input is null. |
| 1828 | + * For dealing with nullable timestamp columns in the database. |
| 1829 | + * @param int $outputtype |
| 1830 | + * @param string $ts |
| 1831 | + * @return string |
| 1832 | + */ |
| 1833 | +function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) { |
| 1834 | + if( is_null( $ts ) ) { |
| 1835 | + return null; |
| 1836 | + } else { |
| 1837 | + return wfTimestamp( $outputtype, $ts ); |
| 1838 | + } |
| 1839 | +} |
| 1840 | + |
| 1841 | +/** |
| 1842 | + * Check if the operating system is Windows |
| 1843 | + * |
| 1844 | + * @return bool True if it's Windows, False otherwise. |
| 1845 | + */ |
| 1846 | +function wfIsWindows() { |
| 1847 | + if (substr(php_uname(), 0, 7) == 'Windows') { |
| 1848 | + return true; |
| 1849 | + } else { |
| 1850 | + return false; |
| 1851 | + } |
| 1852 | +} |
| 1853 | + |
| 1854 | +/** |
| 1855 | + * Swap two variables |
| 1856 | + */ |
| 1857 | +function swap( &$x, &$y ) { |
| 1858 | + $z = $x; |
| 1859 | + $x = $y; |
| 1860 | + $y = $z; |
| 1861 | +} |
| 1862 | + |
| 1863 | +function wfGetCachedNotice( $name ) { |
| 1864 | + global $wgOut, $wgRenderHashAppend, $parserMemc; |
| 1865 | + $fname = 'wfGetCachedNotice'; |
| 1866 | + wfProfileIn( $fname ); |
| 1867 | + |
| 1868 | + $needParse = false; |
| 1869 | + |
| 1870 | + if( $name === 'default' ) { |
| 1871 | + // special case |
| 1872 | + global $wgSiteNotice; |
| 1873 | + $notice = $wgSiteNotice; |
| 1874 | + if( empty( $notice ) ) { |
| 1875 | + wfProfileOut( $fname ); |
| 1876 | + return false; |
| 1877 | + } |
| 1878 | + } else { |
| 1879 | + $notice = wfMsgForContentNoTrans( $name ); |
| 1880 | + if( wfEmptyMsg( $name, $notice ) || $notice == '-' ) { |
| 1881 | + wfProfileOut( $fname ); |
| 1882 | + return( false ); |
| 1883 | + } |
| 1884 | + } |
| 1885 | + |
| 1886 | + // Use the extra hash appender to let eg SSL variants separately cache. |
| 1887 | + $key = wfMemcKey( $name . $wgRenderHashAppend ); |
| 1888 | + $cachedNotice = $parserMemc->get( $key ); |
| 1889 | + if( is_array( $cachedNotice ) ) { |
| 1890 | + if( md5( $notice ) == $cachedNotice['hash'] ) { |
| 1891 | + $notice = $cachedNotice['html']; |
| 1892 | + } else { |
| 1893 | + $needParse = true; |
| 1894 | + } |
| 1895 | + } else { |
| 1896 | + $needParse = true; |
| 1897 | + } |
| 1898 | + |
| 1899 | + if( $needParse ) { |
| 1900 | + if( is_object( $wgOut ) ) { |
| 1901 | + $parsed = $wgOut->parse( $notice ); |
| 1902 | + $parserMemc->set( $key, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 ); |
| 1903 | + $notice = $parsed; |
| 1904 | + } else { |
| 1905 | + wfDebug( 'wfGetCachedNotice called for ' . $name . ' with no $wgOut available'."\n" ); |
| 1906 | + $notice = ''; |
| 1907 | + } |
| 1908 | + } |
| 1909 | + |
| 1910 | + wfProfileOut( $fname ); |
| 1911 | + return $notice; |
| 1912 | +} |
| 1913 | + |
| 1914 | +function wfGetNamespaceNotice() { |
| 1915 | + global $wgTitle; |
| 1916 | + |
| 1917 | + # Paranoia |
| 1918 | + if ( !isset( $wgTitle ) || !is_object( $wgTitle ) ) |
| 1919 | + return ""; |
| 1920 | + |
| 1921 | + $fname = 'wfGetNamespaceNotice'; |
| 1922 | + wfProfileIn( $fname ); |
| 1923 | + |
| 1924 | + $key = "namespacenotice-" . $wgTitle->getNsText(); |
| 1925 | + $namespaceNotice = wfGetCachedNotice( $key ); |
| 1926 | + if ( $namespaceNotice && substr ( $namespaceNotice , 0 ,7 ) != "<p><" ) { |
| 1927 | + $namespaceNotice = '<div id="namespacebanner">' . $namespaceNotice . "</div>"; |
| 1928 | + } else { |
| 1929 | + $namespaceNotice = ""; |
| 1930 | + } |
| 1931 | + |
| 1932 | + wfProfileOut( $fname ); |
| 1933 | + return $namespaceNotice; |
| 1934 | +} |
| 1935 | + |
| 1936 | +function wfGetSiteNotice() { |
| 1937 | + global $wgUser, $wgSiteNotice; |
| 1938 | + $fname = 'wfGetSiteNotice'; |
| 1939 | + wfProfileIn( $fname ); |
| 1940 | + $siteNotice = ''; |
| 1941 | + |
| 1942 | + if( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice ) ) ) { |
| 1943 | + if( is_object( $wgUser ) && $wgUser->isLoggedIn() ) { |
| 1944 | + $siteNotice = wfGetCachedNotice( 'sitenotice' ); |
| 1945 | + } else { |
| 1946 | + $anonNotice = wfGetCachedNotice( 'anonnotice' ); |
| 1947 | + if( !$anonNotice ) { |
| 1948 | + $siteNotice = wfGetCachedNotice( 'sitenotice' ); |
| 1949 | + } else { |
| 1950 | + $siteNotice = $anonNotice; |
| 1951 | + } |
| 1952 | + } |
| 1953 | + if( !$siteNotice ) { |
| 1954 | + $siteNotice = wfGetCachedNotice( 'default' ); |
| 1955 | + } |
| 1956 | + } |
| 1957 | + |
| 1958 | + wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice ) ); |
| 1959 | + wfProfileOut( $fname ); |
| 1960 | + return $siteNotice; |
| 1961 | +} |
| 1962 | + |
| 1963 | +/** |
| 1964 | + * BC wrapper for MimeMagic::singleton() |
| 1965 | + * @deprecated |
| 1966 | + */ |
| 1967 | +function &wfGetMimeMagic() { |
| 1968 | + return MimeMagic::singleton(); |
| 1969 | +} |
| 1970 | + |
| 1971 | +/** |
| 1972 | + * Tries to get the system directory for temporary files. |
| 1973 | + * The TMPDIR, TMP, and TEMP environment variables are checked in sequence, |
| 1974 | + * and if none are set /tmp is returned as the generic Unix default. |
| 1975 | + * |
| 1976 | + * NOTE: When possible, use the tempfile() function to create temporary |
| 1977 | + * files to avoid race conditions on file creation, etc. |
| 1978 | + * |
| 1979 | + * @return string |
| 1980 | + */ |
| 1981 | +function wfTempDir() { |
| 1982 | + foreach( array( 'TMPDIR', 'TMP', 'TEMP' ) as $var ) { |
| 1983 | + $tmp = getenv( $var ); |
| 1984 | + if( $tmp && file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) { |
| 1985 | + return $tmp; |
| 1986 | + } |
| 1987 | + } |
| 1988 | + # Hope this is Unix of some kind! |
| 1989 | + return '/tmp'; |
| 1990 | +} |
| 1991 | + |
| 1992 | +/** |
| 1993 | + * Make directory, and make all parent directories if they don't exist |
| 1994 | + * |
| 1995 | + * @param string $dir Full path to directory to create |
| 1996 | + * @param int $mode Chmod value to use, default is $wgDirectoryMode |
| 1997 | + * @param string $caller Optional caller param for debugging. |
| 1998 | + * @return bool |
| 1999 | + */ |
| 2000 | +function wfMkdirParents( $dir, $mode = null, $caller = null ) { |
| 2001 | + global $wgDirectoryMode; |
| 2002 | + |
| 2003 | + if ( !is_null( $caller ) ) { |
| 2004 | + wfDebug( "$caller: called wfMkdirParents($dir)" ); |
| 2005 | + } |
| 2006 | + |
| 2007 | + if( strval( $dir ) === '' || file_exists( $dir ) ) |
| 2008 | + return true; |
| 2009 | + |
| 2010 | + if ( is_null( $mode ) ) |
| 2011 | + $mode = $wgDirectoryMode; |
| 2012 | + |
| 2013 | + return mkdir( $dir, $mode, true ); // PHP5 <3 |
| 2014 | +} |
| 2015 | + |
| 2016 | +/** |
| 2017 | + * Increment a statistics counter |
| 2018 | + */ |
| 2019 | +function wfIncrStats( $key ) { |
| 2020 | + global $wgStatsMethod; |
| 2021 | + |
| 2022 | + if( $wgStatsMethod == 'udp' ) { |
| 2023 | + global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgDBname; |
| 2024 | + static $socket; |
| 2025 | + if (!$socket) { |
| 2026 | + $socket=socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); |
| 2027 | + $statline="stats/{$wgDBname} - 1 1 1 1 1 -total\n"; |
| 2028 | + socket_sendto($socket,$statline,strlen($statline),0,$wgUDPProfilerHost,$wgUDPProfilerPort); |
| 2029 | + } |
| 2030 | + $statline="stats/{$wgDBname} - 1 1 1 1 1 {$key}\n"; |
| 2031 | + @socket_sendto($socket,$statline,strlen($statline),0,$wgUDPProfilerHost,$wgUDPProfilerPort); |
| 2032 | + } elseif( $wgStatsMethod == 'cache' ) { |
| 2033 | + global $wgMemc; |
| 2034 | + $key = wfMemcKey( 'stats', $key ); |
| 2035 | + if ( is_null( $wgMemc->incr( $key ) ) ) { |
| 2036 | + $wgMemc->add( $key, 1 ); |
| 2037 | + } |
| 2038 | + } else { |
| 2039 | + // Disabled |
| 2040 | + } |
| 2041 | +} |
| 2042 | + |
| 2043 | +/** |
| 2044 | + * @param mixed $nr The number to format |
| 2045 | + * @param int $acc The number of digits after the decimal point, default 2 |
| 2046 | + * @param bool $round Whether or not to round the value, default true |
| 2047 | + * @return float |
| 2048 | + */ |
| 2049 | +function wfPercent( $nr, $acc = 2, $round = true ) { |
| 2050 | + $ret = sprintf( "%.${acc}f", $nr ); |
| 2051 | + return $round ? round( $ret, $acc ) . '%' : "$ret%"; |
| 2052 | +} |
| 2053 | + |
| 2054 | +/** |
| 2055 | + * Encrypt a username/password. |
| 2056 | + * |
| 2057 | + * @param string $userid ID of the user |
| 2058 | + * @param string $password Password of the user |
| 2059 | + * @return string Hashed password |
| 2060 | + * @deprecated Use User::crypt() or User::oldCrypt() instead |
| 2061 | + */ |
| 2062 | +function wfEncryptPassword( $userid, $password ) { |
| 2063 | + wfDeprecated(__FUNCTION__); |
| 2064 | + # Just wrap around User::oldCrypt() |
| 2065 | + return User::oldCrypt($password, $userid); |
| 2066 | +} |
| 2067 | + |
| 2068 | +/** |
| 2069 | + * Appends to second array if $value differs from that in $default |
| 2070 | + */ |
| 2071 | +function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) { |
| 2072 | + if ( is_null( $changed ) ) { |
| 2073 | + throw new MWException('GlobalFunctions::wfAppendToArrayIfNotDefault got null'); |
| 2074 | + } |
| 2075 | + if ( $default[$key] !== $value ) { |
| 2076 | + $changed[$key] = $value; |
| 2077 | + } |
| 2078 | +} |
| 2079 | + |
| 2080 | +/** |
| 2081 | + * Since wfMsg() and co suck, they don't return false if the message key they |
| 2082 | + * looked up didn't exist but a XHTML string, this function checks for the |
| 2083 | + * nonexistance of messages by looking at wfMsg() output |
| 2084 | + * |
| 2085 | + * @param $msg The message key looked up |
| 2086 | + * @param $wfMsgOut The output of wfMsg*() |
| 2087 | + * @return bool |
| 2088 | + */ |
| 2089 | +function wfEmptyMsg( $msg, $wfMsgOut ) { |
| 2090 | + return $wfMsgOut === htmlspecialchars( "<$msg>" ); |
| 2091 | +} |
| 2092 | + |
| 2093 | +/** |
| 2094 | + * Find out whether or not a mixed variable exists in a string |
| 2095 | + * |
| 2096 | + * @param mixed needle |
| 2097 | + * @param string haystack |
| 2098 | + * @return bool |
| 2099 | + */ |
| 2100 | +function in_string( $needle, $str ) { |
| 2101 | + return strpos( $str, $needle ) !== false; |
| 2102 | +} |
| 2103 | + |
| 2104 | +function wfSpecialList( $page, $details ) { |
| 2105 | + global $wgContLang; |
| 2106 | + $details = $details ? ' ' . $wgContLang->getDirMark() . "($details)" : ""; |
| 2107 | + return $page . $details; |
| 2108 | +} |
| 2109 | + |
| 2110 | +/** |
| 2111 | + * Returns a regular expression of url protocols |
| 2112 | + * |
| 2113 | + * @return string |
| 2114 | + */ |
| 2115 | +function wfUrlProtocols() { |
| 2116 | + global $wgUrlProtocols; |
| 2117 | + |
| 2118 | + // Support old-style $wgUrlProtocols strings, for backwards compatibility |
| 2119 | + // with LocalSettings files from 1.5 |
| 2120 | + if ( is_array( $wgUrlProtocols ) ) { |
| 2121 | + $protocols = array(); |
| 2122 | + foreach ($wgUrlProtocols as $protocol) |
| 2123 | + $protocols[] = preg_quote( $protocol, '/' ); |
| 2124 | + |
| 2125 | + return implode( '|', $protocols ); |
| 2126 | + } else { |
| 2127 | + return $wgUrlProtocols; |
| 2128 | + } |
| 2129 | +} |
| 2130 | + |
| 2131 | +/** |
| 2132 | + * Safety wrapper around ini_get() for boolean settings. |
| 2133 | + * The values returned from ini_get() are pre-normalized for settings |
| 2134 | + * set via php.ini or php_flag/php_admin_flag... but *not* |
| 2135 | + * for those set via php_value/php_admin_value. |
| 2136 | + * |
| 2137 | + * It's fairly common for people to use php_value instead of php_flag, |
| 2138 | + * which can leave you with an 'off' setting giving a false positive |
| 2139 | + * for code that just takes the ini_get() return value as a boolean. |
| 2140 | + * |
| 2141 | + * To make things extra interesting, setting via php_value accepts |
| 2142 | + * "true" and "yes" as true, but php.ini and php_flag consider them false. :) |
| 2143 | + * Unrecognized values go false... again opposite PHP's own coercion |
| 2144 | + * from string to bool. |
| 2145 | + * |
| 2146 | + * Luckily, 'properly' set settings will always come back as '0' or '1', |
| 2147 | + * so we only have to worry about them and the 'improper' settings. |
| 2148 | + * |
| 2149 | + * I frickin' hate PHP... :P |
| 2150 | + * |
| 2151 | + * @param string $setting |
| 2152 | + * @return bool |
| 2153 | + */ |
| 2154 | +function wfIniGetBool( $setting ) { |
| 2155 | + $val = ini_get( $setting ); |
| 2156 | + // 'on' and 'true' can't have whitespace around them, but '1' can. |
| 2157 | + return strtolower( $val ) == 'on' |
| 2158 | + || strtolower( $val ) == 'true' |
| 2159 | + || strtolower( $val ) == 'yes' |
| 2160 | + || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function |
| 2161 | +} |
| 2162 | + |
| 2163 | +/** |
| 2164 | + * Execute a shell command, with time and memory limits mirrored from the PHP |
| 2165 | + * configuration if supported. |
| 2166 | + * @param $cmd Command line, properly escaped for shell. |
| 2167 | + * @param &$retval optional, will receive the program's exit code. |
| 2168 | + * (non-zero is usually failure) |
| 2169 | + * @return collected stdout as a string (trailing newlines stripped) |
| 2170 | + */ |
| 2171 | +function wfShellExec( $cmd, &$retval=null ) { |
| 2172 | + global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime; |
| 2173 | + |
| 2174 | + static $disabled; |
| 2175 | + if ( is_null( $disabled ) ) { |
| 2176 | + $disabled = false; |
| 2177 | + if( wfIniGetBool( 'safe_mode' ) ) { |
| 2178 | + wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" ); |
| 2179 | + $disabled = true; |
| 2180 | + } |
| 2181 | + $functions = explode( ',', ini_get( 'disable_functions' ) ); |
| 2182 | + $functions = array_map( 'trim', $functions ); |
| 2183 | + $functions = array_map( 'strtolower', $functions ); |
| 2184 | + if ( in_array( 'passthru', $functions ) ) { |
| 2185 | + wfDebug( "passthru is in disabled_functions\n" ); |
| 2186 | + $disabled = true; |
| 2187 | + } |
| 2188 | + } |
| 2189 | + if ( $disabled ) { |
| 2190 | + $retval = 1; |
| 2191 | + return "Unable to run external programs in safe mode."; |
| 2192 | + } |
| 2193 | + |
| 2194 | + wfInitShellLocale(); |
| 2195 | + |
| 2196 | + if ( php_uname( 's' ) == 'Linux' ) { |
| 2197 | + $time = intval( $wgMaxShellTime ); |
| 2198 | + $mem = intval( $wgMaxShellMemory ); |
| 2199 | + $filesize = intval( $wgMaxShellFileSize ); |
| 2200 | + |
| 2201 | + if ( $time > 0 && $mem > 0 ) { |
| 2202 | + $script = "$IP/bin/ulimit4.sh"; |
| 2203 | + if ( is_executable( $script ) ) { |
| 2204 | + $cmd = escapeshellarg( $script ) . " $time $mem $filesize " . escapeshellarg( $cmd ); |
| 2205 | + } |
| 2206 | + } |
| 2207 | + } elseif ( php_uname( 's' ) == 'Windows NT' ) { |
| 2208 | + # This is a hack to work around PHP's flawed invocation of cmd.exe |
| 2209 | + # http://news.php.net/php.internals/21796 |
| 2210 | + $cmd = '"' . $cmd . '"'; |
| 2211 | + } |
| 2212 | + wfDebug( "wfShellExec: $cmd\n" ); |
| 2213 | + |
| 2214 | + $retval = 1; // error by default? |
| 2215 | + ob_start(); |
| 2216 | + passthru( $cmd, $retval ); |
| 2217 | + $output = ob_get_contents(); |
| 2218 | + ob_end_clean(); |
| 2219 | + |
| 2220 | + if ( $retval == 127 ) { |
| 2221 | + wfDebugLog( 'exec', "Possibly missing executable file: $cmd\n" ); |
| 2222 | + } |
| 2223 | + return $output; |
| 2224 | +} |
| 2225 | + |
| 2226 | +/** |
| 2227 | + * Workaround for http://bugs.php.net/bug.php?id=45132 |
| 2228 | + * escapeshellarg() destroys non-ASCII characters if LANG is not a UTF-8 locale |
| 2229 | + */ |
| 2230 | +function wfInitShellLocale() { |
| 2231 | + static $done = false; |
| 2232 | + if ( $done ) return; |
| 2233 | + $done = true; |
| 2234 | + global $wgShellLocale; |
| 2235 | + if ( !wfIniGetBool( 'safe_mode' ) ) { |
| 2236 | + putenv( "LC_CTYPE=$wgShellLocale" ); |
| 2237 | + setlocale( LC_CTYPE, $wgShellLocale ); |
| 2238 | + } |
| 2239 | +} |
| 2240 | + |
| 2241 | +/** |
| 2242 | + * This function works like "use VERSION" in Perl, the program will die with a |
| 2243 | + * backtrace if the current version of PHP is less than the version provided |
| 2244 | + * |
| 2245 | + * This is useful for extensions which due to their nature are not kept in sync |
| 2246 | + * with releases, and might depend on other versions of PHP than the main code |
| 2247 | + * |
| 2248 | + * Note: PHP might die due to parsing errors in some cases before it ever |
| 2249 | + * manages to call this function, such is life |
| 2250 | + * |
| 2251 | + * @see perldoc -f use |
| 2252 | + * |
| 2253 | + * @param mixed $version The version to check, can be a string, an integer, or |
| 2254 | + * a float |
| 2255 | + */ |
| 2256 | +function wfUsePHP( $req_ver ) { |
| 2257 | + $php_ver = PHP_VERSION; |
| 2258 | + |
| 2259 | + if ( version_compare( $php_ver, (string)$req_ver, '<' ) ) |
| 2260 | + throw new MWException( "PHP $req_ver required--this is only $php_ver" ); |
| 2261 | +} |
| 2262 | + |
| 2263 | +/** |
| 2264 | + * This function works like "use VERSION" in Perl except it checks the version |
| 2265 | + * of MediaWiki, the program will die with a backtrace if the current version |
| 2266 | + * of MediaWiki is less than the version provided. |
| 2267 | + * |
| 2268 | + * This is useful for extensions which due to their nature are not kept in sync |
| 2269 | + * with releases |
| 2270 | + * |
| 2271 | + * @see perldoc -f use |
| 2272 | + * |
| 2273 | + * @param mixed $version The version to check, can be a string, an integer, or |
| 2274 | + * a float |
| 2275 | + */ |
| 2276 | +function wfUseMW( $req_ver ) { |
| 2277 | + global $wgVersion; |
| 2278 | + |
| 2279 | + if ( version_compare( $wgVersion, (string)$req_ver, '<' ) ) |
| 2280 | + throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" ); |
| 2281 | +} |
| 2282 | + |
| 2283 | +/** |
| 2284 | + * @deprecated use StringUtils::escapeRegexReplacement |
| 2285 | + */ |
| 2286 | +function wfRegexReplacement( $string ) { |
| 2287 | + return StringUtils::escapeRegexReplacement( $string ); |
| 2288 | +} |
| 2289 | + |
| 2290 | +/** |
| 2291 | + * Return the final portion of a pathname. |
| 2292 | + * Reimplemented because PHP5's basename() is buggy with multibyte text. |
| 2293 | + * http://bugs.php.net/bug.php?id=33898 |
| 2294 | + * |
| 2295 | + * PHP's basename() only considers '\' a pathchar on Windows and Netware. |
| 2296 | + * We'll consider it so always, as we don't want \s in our Unix paths either. |
| 2297 | + * |
| 2298 | + * @param string $path |
| 2299 | + * @param string $suffix to remove if present |
| 2300 | + * @return string |
| 2301 | + */ |
| 2302 | +function wfBaseName( $path, $suffix='' ) { |
| 2303 | + $encSuffix = ($suffix == '') |
| 2304 | + ? '' |
| 2305 | + : ( '(?:' . preg_quote( $suffix, '#' ) . ')?' ); |
| 2306 | + $matches = array(); |
| 2307 | + if( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) { |
| 2308 | + return $matches[1]; |
| 2309 | + } else { |
| 2310 | + return ''; |
| 2311 | + } |
| 2312 | +} |
| 2313 | + |
| 2314 | +/** |
| 2315 | + * Generate a relative path name to the given file. |
| 2316 | + * May explode on non-matching case-insensitive paths, |
| 2317 | + * funky symlinks, etc. |
| 2318 | + * |
| 2319 | + * @param string $path Absolute destination path including target filename |
| 2320 | + * @param string $from Absolute source path, directory only |
| 2321 | + * @return string |
| 2322 | + */ |
| 2323 | +function wfRelativePath( $path, $from ) { |
| 2324 | + // Normalize mixed input on Windows... |
| 2325 | + $path = str_replace( '/', DIRECTORY_SEPARATOR, $path ); |
| 2326 | + $from = str_replace( '/', DIRECTORY_SEPARATOR, $from ); |
| 2327 | + |
| 2328 | + // Trim trailing slashes -- fix for drive root |
| 2329 | + $path = rtrim( $path, DIRECTORY_SEPARATOR ); |
| 2330 | + $from = rtrim( $from, DIRECTORY_SEPARATOR ); |
| 2331 | + |
| 2332 | + $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) ); |
| 2333 | + $against = explode( DIRECTORY_SEPARATOR, $from ); |
| 2334 | + |
| 2335 | + if( $pieces[0] !== $against[0] ) { |
| 2336 | + // Non-matching Windows drive letters? |
| 2337 | + // Return a full path. |
| 2338 | + return $path; |
| 2339 | + } |
| 2340 | + |
| 2341 | + // Trim off common prefix |
| 2342 | + while( count( $pieces ) && count( $against ) |
| 2343 | + && $pieces[0] == $against[0] ) { |
| 2344 | + array_shift( $pieces ); |
| 2345 | + array_shift( $against ); |
| 2346 | + } |
| 2347 | + |
| 2348 | + // relative dots to bump us to the parent |
| 2349 | + while( count( $against ) ) { |
| 2350 | + array_unshift( $pieces, '..' ); |
| 2351 | + array_shift( $against ); |
| 2352 | + } |
| 2353 | + |
| 2354 | + array_push( $pieces, wfBaseName( $path ) ); |
| 2355 | + |
| 2356 | + return implode( DIRECTORY_SEPARATOR, $pieces ); |
| 2357 | +} |
| 2358 | + |
| 2359 | +/** |
| 2360 | + * Backwards array plus for people who haven't bothered to read the PHP manual |
| 2361 | + * XXX: will not darn your socks for you. |
| 2362 | + * |
| 2363 | + * @param array $array1, [$array2, [...]] |
| 2364 | + * @return array |
| 2365 | + */ |
| 2366 | +function wfArrayMerge( $array1/* ... */ ) { |
| 2367 | + $args = func_get_args(); |
| 2368 | + $args = array_reverse( $args, true ); |
| 2369 | + $out = array(); |
| 2370 | + foreach ( $args as $arg ) { |
| 2371 | + $out += $arg; |
| 2372 | + } |
| 2373 | + return $out; |
| 2374 | +} |
| 2375 | + |
| 2376 | +/** |
| 2377 | + * Merge arrays in the style of getUserPermissionsErrors, with duplicate removal |
| 2378 | + * e.g. |
| 2379 | + * wfMergeErrorArrays( |
| 2380 | + * array( array( 'x' ) ), |
| 2381 | + * array( array( 'x', '2' ) ), |
| 2382 | + * array( array( 'x' ) ), |
| 2383 | + * array( array( 'y') ) |
| 2384 | + * ); |
| 2385 | + * returns: |
| 2386 | + * array( |
| 2387 | + * array( 'x', '2' ), |
| 2388 | + * array( 'x' ), |
| 2389 | + * array( 'y' ) |
| 2390 | + * ) |
| 2391 | + */ |
| 2392 | +function wfMergeErrorArrays(/*...*/) { |
| 2393 | + $args = func_get_args(); |
| 2394 | + $out = array(); |
| 2395 | + foreach ( $args as $errors ) { |
| 2396 | + foreach ( $errors as $params ) { |
| 2397 | + $spec = implode( "\t", $params ); |
| 2398 | + $out[$spec] = $params; |
| 2399 | + } |
| 2400 | + } |
| 2401 | + return array_values( $out ); |
| 2402 | +} |
| 2403 | + |
| 2404 | +/** |
| 2405 | + * parse_url() work-alike, but non-broken. Differences: |
| 2406 | + * |
| 2407 | + * 1) Does not raise warnings on bad URLs (just returns false) |
| 2408 | + * 2) Handles protocols that don't use :// (e.g., mailto: and news:) correctly |
| 2409 | + * 3) Adds a "delimiter" element to the array, either '://' or ':' (see (2)) |
| 2410 | + * |
| 2411 | + * @param string $url A URL to parse |
| 2412 | + * @return array Bits of the URL in an associative array, per PHP docs |
| 2413 | + */ |
| 2414 | +function wfParseUrl( $url ) { |
| 2415 | + global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php |
| 2416 | + wfSuppressWarnings(); |
| 2417 | + $bits = parse_url( $url ); |
| 2418 | + wfRestoreWarnings(); |
| 2419 | + if ( !$bits ) { |
| 2420 | + return false; |
| 2421 | + } |
| 2422 | + |
| 2423 | + // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it |
| 2424 | + if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) { |
| 2425 | + $bits['delimiter'] = '://'; |
| 2426 | + } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) { |
| 2427 | + $bits['delimiter'] = ':'; |
| 2428 | + // parse_url detects for news: and mailto: the host part of an url as path |
| 2429 | + // We have to correct this wrong detection |
| 2430 | + if ( isset ( $bits['path'] ) ) { |
| 2431 | + $bits['host'] = $bits['path']; |
| 2432 | + $bits['path'] = ''; |
| 2433 | + } |
| 2434 | + } else { |
| 2435 | + return false; |
| 2436 | + } |
| 2437 | + |
| 2438 | + return $bits; |
| 2439 | +} |
| 2440 | + |
| 2441 | +/** |
| 2442 | + * Make a URL index, appropriate for the el_index field of externallinks. |
| 2443 | + */ |
| 2444 | +function wfMakeUrlIndex( $url ) { |
| 2445 | + $bits = wfParseUrl( $url ); |
| 2446 | + |
| 2447 | + // Reverse the labels in the hostname, convert to lower case |
| 2448 | + // For emails reverse domainpart only |
| 2449 | + if ( $bits['scheme'] == 'mailto' ) { |
| 2450 | + $mailparts = explode( '@', $bits['host'], 2 ); |
| 2451 | + if ( count($mailparts) === 2 ) { |
| 2452 | + $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) ); |
| 2453 | + } else { |
| 2454 | + // No domain specified, don't mangle it |
| 2455 | + $domainpart = ''; |
| 2456 | + } |
| 2457 | + $reversedHost = $domainpart . '@' . $mailparts[0]; |
| 2458 | + } else { |
| 2459 | + $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) ); |
| 2460 | + } |
| 2461 | + // Add an extra dot to the end |
| 2462 | + // Why? Is it in wrong place in mailto links? |
| 2463 | + if ( substr( $reversedHost, -1, 1 ) !== '.' ) { |
| 2464 | + $reversedHost .= '.'; |
| 2465 | + } |
| 2466 | + // Reconstruct the pseudo-URL |
| 2467 | + $prot = $bits['scheme']; |
| 2468 | + $index = $prot . $bits['delimiter'] . $reversedHost; |
| 2469 | + // Leave out user and password. Add the port, path, query and fragment |
| 2470 | + if ( isset( $bits['port'] ) ) $index .= ':' . $bits['port']; |
| 2471 | + if ( isset( $bits['path'] ) ) { |
| 2472 | + $index .= $bits['path']; |
| 2473 | + } else { |
| 2474 | + $index .= '/'; |
| 2475 | + } |
| 2476 | + if ( isset( $bits['query'] ) ) $index .= '?' . $bits['query']; |
| 2477 | + if ( isset( $bits['fragment'] ) ) $index .= '#' . $bits['fragment']; |
| 2478 | + return $index; |
| 2479 | +} |
| 2480 | + |
| 2481 | +/** |
| 2482 | + * Do any deferred updates and clear the list |
| 2483 | + * TODO: This could be in Wiki.php if that class made any sense at all |
| 2484 | + */ |
| 2485 | +function wfDoUpdates() |
| 2486 | +{ |
| 2487 | + global $wgPostCommitUpdateList, $wgDeferredUpdateList; |
| 2488 | + foreach ( $wgDeferredUpdateList as $update ) { |
| 2489 | + $update->doUpdate(); |
| 2490 | + } |
| 2491 | + foreach ( $wgPostCommitUpdateList as $update ) { |
| 2492 | + $update->doUpdate(); |
| 2493 | + } |
| 2494 | + $wgDeferredUpdateList = array(); |
| 2495 | + $wgPostCommitUpdateList = array(); |
| 2496 | +} |
| 2497 | + |
| 2498 | +/** |
| 2499 | + * @deprecated use StringUtils::explodeMarkup |
| 2500 | + */ |
| 2501 | +function wfExplodeMarkup( $separator, $text ) { |
| 2502 | + return StringUtils::explodeMarkup( $separator, $text ); |
| 2503 | +} |
| 2504 | + |
| 2505 | +/** |
| 2506 | + * Convert an arbitrarily-long digit string from one numeric base |
| 2507 | + * to another, optionally zero-padding to a minimum column width. |
| 2508 | + * |
| 2509 | + * Supports base 2 through 36; digit values 10-36 are represented |
| 2510 | + * as lowercase letters a-z. Input is case-insensitive. |
| 2511 | + * |
| 2512 | + * @param $input string of digits |
| 2513 | + * @param $sourceBase int 2-36 |
| 2514 | + * @param $destBase int 2-36 |
| 2515 | + * @param $pad int 1 or greater |
| 2516 | + * @param $lowercase bool |
| 2517 | + * @return string or false on invalid input |
| 2518 | + */ |
| 2519 | +function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true ) { |
| 2520 | + $input = strval( $input ); |
| 2521 | + if( $sourceBase < 2 || |
| 2522 | + $sourceBase > 36 || |
| 2523 | + $destBase < 2 || |
| 2524 | + $destBase > 36 || |
| 2525 | + $pad < 1 || |
| 2526 | + $sourceBase != intval( $sourceBase ) || |
| 2527 | + $destBase != intval( $destBase ) || |
| 2528 | + $pad != intval( $pad ) || |
| 2529 | + !is_string( $input ) || |
| 2530 | + $input == '' ) { |
| 2531 | + return false; |
| 2532 | + } |
| 2533 | + $digitChars = ( $lowercase ) ? '0123456789abcdefghijklmnopqrstuvwxyz' : '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
| 2534 | + $inDigits = array(); |
| 2535 | + $outChars = ''; |
| 2536 | + |
| 2537 | + // Decode and validate input string |
| 2538 | + $input = strtolower( $input ); |
| 2539 | + for( $i = 0; $i < strlen( $input ); $i++ ) { |
| 2540 | + $n = strpos( $digitChars, $input{$i} ); |
| 2541 | + if( $n === false || $n > $sourceBase ) { |
| 2542 | + return false; |
| 2543 | + } |
| 2544 | + $inDigits[] = $n; |
| 2545 | + } |
| 2546 | + |
| 2547 | + // Iterate over the input, modulo-ing out an output digit |
| 2548 | + // at a time until input is gone. |
| 2549 | + while( count( $inDigits ) ) { |
| 2550 | + $work = 0; |
| 2551 | + $workDigits = array(); |
| 2552 | + |
| 2553 | + // Long division... |
| 2554 | + foreach( $inDigits as $digit ) { |
| 2555 | + $work *= $sourceBase; |
| 2556 | + $work += $digit; |
| 2557 | + |
| 2558 | + if( $work < $destBase ) { |
| 2559 | + // Gonna need to pull another digit. |
| 2560 | + if( count( $workDigits ) ) { |
| 2561 | + // Avoid zero-padding; this lets us find |
| 2562 | + // the end of the input very easily when |
| 2563 | + // length drops to zero. |
| 2564 | + $workDigits[] = 0; |
| 2565 | + } |
| 2566 | + } else { |
| 2567 | + // Finally! Actual division! |
| 2568 | + $workDigits[] = intval( $work / $destBase ); |
| 2569 | + |
| 2570 | + // Isn't it annoying that most programming languages |
| 2571 | + // don't have a single divide-and-remainder operator, |
| 2572 | + // even though the CPU implements it that way? |
| 2573 | + $work = $work % $destBase; |
| 2574 | + } |
| 2575 | + } |
| 2576 | + |
| 2577 | + // All that division leaves us with a remainder, |
| 2578 | + // which is conveniently our next output digit. |
| 2579 | + $outChars .= $digitChars[$work]; |
| 2580 | + |
| 2581 | + // And we continue! |
| 2582 | + $inDigits = $workDigits; |
| 2583 | + } |
| 2584 | + |
| 2585 | + while( strlen( $outChars ) < $pad ) { |
| 2586 | + $outChars .= '0'; |
| 2587 | + } |
| 2588 | + |
| 2589 | + return strrev( $outChars ); |
| 2590 | +} |
| 2591 | + |
| 2592 | +/** |
| 2593 | + * Create an object with a given name and an array of construct parameters |
| 2594 | + * @param string $name |
| 2595 | + * @param array $p parameters |
| 2596 | + */ |
| 2597 | +function wfCreateObject( $name, $p ){ |
| 2598 | + $p = array_values( $p ); |
| 2599 | + switch ( count( $p ) ) { |
| 2600 | + case 0: |
| 2601 | + return new $name; |
| 2602 | + case 1: |
| 2603 | + return new $name( $p[0] ); |
| 2604 | + case 2: |
| 2605 | + return new $name( $p[0], $p[1] ); |
| 2606 | + case 3: |
| 2607 | + return new $name( $p[0], $p[1], $p[2] ); |
| 2608 | + case 4: |
| 2609 | + return new $name( $p[0], $p[1], $p[2], $p[3] ); |
| 2610 | + case 5: |
| 2611 | + return new $name( $p[0], $p[1], $p[2], $p[3], $p[4] ); |
| 2612 | + case 6: |
| 2613 | + return new $name( $p[0], $p[1], $p[2], $p[3], $p[4], $p[5] ); |
| 2614 | + default: |
| 2615 | + throw new MWException( "Too many arguments to construtor in wfCreateObject" ); |
| 2616 | + } |
| 2617 | +} |
| 2618 | + |
| 2619 | +/** |
| 2620 | + * Alias for modularized function |
| 2621 | + * @deprecated Use Http::get() instead |
| 2622 | + */ |
| 2623 | +function wfGetHTTP( $url, $timeout = 'default' ) { |
| 2624 | + wfDeprecated(__FUNCTION__); |
| 2625 | + return Http::get( $url, $timeout ); |
| 2626 | +} |
| 2627 | + |
| 2628 | +/** |
| 2629 | + * Alias for modularized function |
| 2630 | + * @deprecated Use Http::isLocalURL() instead |
| 2631 | + */ |
| 2632 | +function wfIsLocalURL( $url ) { |
| 2633 | + wfDeprecated(__FUNCTION__); |
| 2634 | + return Http::isLocalURL( $url ); |
| 2635 | +} |
| 2636 | + |
| 2637 | +function wfHttpOnlySafe() { |
| 2638 | + global $wgHttpOnlyBlacklist; |
| 2639 | + if( !version_compare("5.2", PHP_VERSION, "<") ) |
| 2640 | + return false; |
| 2641 | + |
| 2642 | + if( isset( $_SERVER['HTTP_USER_AGENT'] ) ) { |
| 2643 | + foreach( $wgHttpOnlyBlacklist as $regex ) { |
| 2644 | + if( preg_match( $regex, $_SERVER['HTTP_USER_AGENT'] ) ) { |
| 2645 | + return false; |
| 2646 | + } |
| 2647 | + } |
| 2648 | + } |
| 2649 | + |
| 2650 | + return true; |
| 2651 | +} |
| 2652 | + |
| 2653 | +/** |
| 2654 | + * Initialise php session |
| 2655 | + */ |
| 2656 | +function wfSetupSession() { |
| 2657 | + global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly; |
| 2658 | + if( $wgSessionsInMemcached ) { |
| 2659 | + require_once( 'MemcachedSessions.php' ); |
| 2660 | + } elseif( 'files' != ini_get( 'session.save_handler' ) ) { |
| 2661 | + # If it's left on 'user' or another setting from another |
| 2662 | + # application, it will end up failing. Try to recover. |
| 2663 | + ini_set ( 'session.save_handler', 'files' ); |
| 2664 | + } |
| 2665 | + $httpOnlySafe = wfHttpOnlySafe(); |
| 2666 | + wfDebugLog( 'cookie', |
| 2667 | + 'session_set_cookie_params: "' . implode( '", "', |
| 2668 | + array( |
| 2669 | + 0, |
| 2670 | + $wgCookiePath, |
| 2671 | + $wgCookieDomain, |
| 2672 | + $wgCookieSecure, |
| 2673 | + $httpOnlySafe && $wgCookieHttpOnly ) ) . '"' ); |
| 2674 | + if( $httpOnlySafe && $wgCookieHttpOnly ) { |
| 2675 | + session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly ); |
| 2676 | + } else { |
| 2677 | + // PHP 5.1 throws warnings if you pass the HttpOnly parameter for 5.2. |
| 2678 | + session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); |
| 2679 | + } |
| 2680 | + session_cache_limiter( 'private, must-revalidate' ); |
| 2681 | + wfSuppressWarnings(); |
| 2682 | + session_start(); |
| 2683 | + wfRestoreWarnings(); |
| 2684 | +} |
| 2685 | + |
| 2686 | +/** |
| 2687 | + * Get an object from the precompiled serialized directory |
| 2688 | + * |
| 2689 | + * @return mixed The variable on success, false on failure |
| 2690 | + */ |
| 2691 | +function wfGetPrecompiledData( $name ) { |
| 2692 | + global $IP; |
| 2693 | + |
| 2694 | + $file = "$IP/serialized/$name"; |
| 2695 | + if ( file_exists( $file ) ) { |
| 2696 | + $blob = file_get_contents( $file ); |
| 2697 | + if ( $blob ) { |
| 2698 | + return unserialize( $blob ); |
| 2699 | + } |
| 2700 | + } |
| 2701 | + return false; |
| 2702 | +} |
| 2703 | + |
| 2704 | +function wfGetCaller( $level = 2 ) { |
| 2705 | + $backtrace = wfDebugBacktrace(); |
| 2706 | + if ( isset( $backtrace[$level] ) ) { |
| 2707 | + return wfFormatStackFrame($backtrace[$level]); |
| 2708 | + } else { |
| 2709 | + $caller = 'unknown'; |
| 2710 | + } |
| 2711 | + return $caller; |
| 2712 | +} |
| 2713 | + |
| 2714 | +/** Return a string consisting all callers in stack, somewhat useful sometimes for profiling specific points */ |
| 2715 | +function wfGetAllCallers() { |
| 2716 | + return implode('/', array_map('wfFormatStackFrame',array_reverse(wfDebugBacktrace()))); |
| 2717 | +} |
| 2718 | + |
| 2719 | +/** Return a string representation of frame */ |
| 2720 | +function wfFormatStackFrame($frame) { |
| 2721 | + return isset( $frame["class"] )? |
| 2722 | + $frame["class"]."::".$frame["function"]: |
| 2723 | + $frame["function"]; |
| 2724 | +} |
| 2725 | + |
| 2726 | +/** |
| 2727 | + * Get a cache key |
| 2728 | + */ |
| 2729 | +function wfMemcKey( /*... */ ) { |
| 2730 | + $args = func_get_args(); |
| 2731 | + $key = wfWikiID() . ':' . implode( ':', $args ); |
| 2732 | + return $key; |
| 2733 | +} |
| 2734 | + |
| 2735 | +/** |
| 2736 | + * Get a cache key for a foreign DB |
| 2737 | + */ |
| 2738 | +function wfForeignMemcKey( $db, $prefix /*, ... */ ) { |
| 2739 | + $args = array_slice( func_get_args(), 2 ); |
| 2740 | + if ( $prefix ) { |
| 2741 | + $key = "$db-$prefix:" . implode( ':', $args ); |
| 2742 | + } else { |
| 2743 | + $key = $db . ':' . implode( ':', $args ); |
| 2744 | + } |
| 2745 | + return $key; |
| 2746 | +} |
| 2747 | + |
| 2748 | +/** |
| 2749 | + * Get an ASCII string identifying this wiki |
| 2750 | + * This is used as a prefix in memcached keys |
| 2751 | + */ |
| 2752 | +function wfWikiID( $db = null ) { |
| 2753 | + if( $db instanceof Database ) { |
| 2754 | + return $db->getWikiID(); |
| 2755 | + } else { |
| 2756 | + global $wgDBprefix, $wgDBname; |
| 2757 | + if ( $wgDBprefix ) { |
| 2758 | + return "$wgDBname-$wgDBprefix"; |
| 2759 | + } else { |
| 2760 | + return $wgDBname; |
| 2761 | + } |
| 2762 | + } |
| 2763 | +} |
| 2764 | + |
| 2765 | +/** |
| 2766 | + * Split a wiki ID into DB name and table prefix |
| 2767 | + */ |
| 2768 | +function wfSplitWikiID( $wiki ) { |
| 2769 | + $bits = explode( '-', $wiki, 2 ); |
| 2770 | + if ( count( $bits ) < 2 ) { |
| 2771 | + $bits[] = ''; |
| 2772 | + } |
| 2773 | + return $bits; |
| 2774 | +} |
| 2775 | + |
| 2776 | +/* |
| 2777 | + * Get a Database object. |
| 2778 | + * @param integer $db Index of the connection to get. May be DB_MASTER for the |
| 2779 | + * master (for write queries), DB_SLAVE for potentially lagged |
| 2780 | + * read queries, or an integer >= 0 for a particular server. |
| 2781 | + * |
| 2782 | + * @param mixed $groups Query groups. An array of group names that this query |
| 2783 | + * belongs to. May contain a single string if the query is only |
| 2784 | + * in one group. |
| 2785 | + * |
| 2786 | + * @param string $wiki The wiki ID, or false for the current wiki |
| 2787 | + * |
| 2788 | + * Note: multiple calls to wfGetDB(DB_SLAVE) during the course of one request |
| 2789 | + * will always return the same object, unless the underlying connection or load |
| 2790 | + * balancer is manually destroyed. |
| 2791 | + */ |
| 2792 | +function &wfGetDB( $db, $groups = array(), $wiki = false ) { |
| 2793 | + return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki ); |
| 2794 | +} |
| 2795 | + |
| 2796 | +/** |
| 2797 | + * Get a load balancer object. |
| 2798 | + * |
| 2799 | + * @param array $groups List of query groups |
| 2800 | + * @param string $wiki Wiki ID, or false for the current wiki |
| 2801 | + * @return LoadBalancer |
| 2802 | + */ |
| 2803 | +function wfGetLB( $wiki = false ) { |
| 2804 | + return wfGetLBFactory()->getMainLB( $wiki ); |
| 2805 | +} |
| 2806 | + |
| 2807 | +/** |
| 2808 | + * Get the load balancer factory object |
| 2809 | + */ |
| 2810 | +function &wfGetLBFactory() { |
| 2811 | + return LBFactory::singleton(); |
| 2812 | +} |
| 2813 | + |
| 2814 | +/** |
| 2815 | + * Find a file. |
| 2816 | + * Shortcut for RepoGroup::singleton()->findFile() |
| 2817 | + * @param mixed $title Title object or string. May be interwiki. |
| 2818 | + * @param mixed $time Requested time for an archived image, or false for the |
| 2819 | + * current version. An image object will be returned which |
| 2820 | + * was created at the specified time. |
| 2821 | + * @param mixed $flags FileRepo::FIND_ flags |
| 2822 | + * @param boolean $bypass Bypass the file cache even if it could be used |
| 2823 | + * @return File, or false if the file does not exist |
| 2824 | + */ |
| 2825 | +function wfFindFile( $title, $time = false, $flags = 0, $bypass = false ) { |
| 2826 | + if( !$time && !$flags && !$bypass ) { |
| 2827 | + return FileCache::singleton()->findFile( $title ); |
| 2828 | + } else { |
| 2829 | + return RepoGroup::singleton()->findFile( $title, $time, $flags ); |
| 2830 | + } |
| 2831 | +} |
| 2832 | + |
| 2833 | +/** |
| 2834 | + * Get an object referring to a locally registered file. |
| 2835 | + * Returns a valid placeholder object if the file does not exist. |
| 2836 | + */ |
| 2837 | +function wfLocalFile( $title ) { |
| 2838 | + return RepoGroup::singleton()->getLocalRepo()->newFile( $title ); |
| 2839 | +} |
| 2840 | + |
| 2841 | +/** |
| 2842 | + * Should low-performance queries be disabled? |
| 2843 | + * |
| 2844 | + * @return bool |
| 2845 | + */ |
| 2846 | +function wfQueriesMustScale() { |
| 2847 | + global $wgMiserMode; |
| 2848 | + return $wgMiserMode |
| 2849 | + || ( SiteStats::pages() > 100000 |
| 2850 | + && SiteStats::edits() > 1000000 |
| 2851 | + && SiteStats::users() > 10000 ); |
| 2852 | +} |
| 2853 | + |
| 2854 | +/** |
| 2855 | + * Get the path to a specified script file, respecting file |
| 2856 | + * extensions; this is a wrapper around $wgScriptExtension etc. |
| 2857 | + * |
| 2858 | + * @param string $script Script filename, sans extension |
| 2859 | + * @return string |
| 2860 | + */ |
| 2861 | +function wfScript( $script = 'index' ) { |
| 2862 | + global $wgScriptPath, $wgScriptExtension; |
| 2863 | + return "{$wgScriptPath}/{$script}{$wgScriptExtension}"; |
| 2864 | +} |
| 2865 | + |
| 2866 | +/** |
| 2867 | + * Convenience function converts boolean values into "true" |
| 2868 | + * or "false" (string) values |
| 2869 | + * |
| 2870 | + * @param bool $value |
| 2871 | + * @return string |
| 2872 | + */ |
| 2873 | +function wfBoolToStr( $value ) { |
| 2874 | + return $value ? 'true' : 'false'; |
| 2875 | +} |
| 2876 | + |
| 2877 | +/** |
| 2878 | + * Load an extension messages file |
| 2879 | + * |
| 2880 | + * @param string $extensionName Name of extension to load messages from\for. |
| 2881 | + * @param string $langcode Language to load messages for, or false for default |
| 2882 | + * behvaiour (en, content language and user language). |
| 2883 | + * @since r24808 (v1.11) Using this method of loading extension messages will not work |
| 2884 | + * on MediaWiki prior to that |
| 2885 | + */ |
| 2886 | +function wfLoadExtensionMessages( $extensionName, $langcode = false ) { |
| 2887 | + global $wgExtensionMessagesFiles, $wgMessageCache, $wgLang, $wgContLang; |
| 2888 | + |
| 2889 | + #For recording whether extension message files have been loaded in a given language. |
| 2890 | + static $loaded = array(); |
| 2891 | + |
| 2892 | + if( !array_key_exists( $extensionName, $loaded ) ) { |
| 2893 | + $loaded[$extensionName] = array(); |
| 2894 | + } |
| 2895 | + |
| 2896 | + if ( !isset($wgExtensionMessagesFiles[$extensionName]) ) { |
| 2897 | + throw new MWException( "Messages file for extensions $extensionName is not defined" ); |
| 2898 | + } |
| 2899 | + |
| 2900 | + if( !$langcode && !array_key_exists( '*', $loaded[$extensionName] ) ) { |
| 2901 | + # Just do en, content language and user language. |
| 2902 | + $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName], false ); |
| 2903 | + # Mark that they have been loaded. |
| 2904 | + $loaded[$extensionName]['en'] = true; |
| 2905 | + $loaded[$extensionName][$wgLang->getCode()] = true; |
| 2906 | + $loaded[$extensionName][$wgContLang->getCode()] = true; |
| 2907 | + # Mark that this part has been done to avoid weird if statements. |
| 2908 | + $loaded[$extensionName]['*'] = true; |
| 2909 | + } elseif( is_string( $langcode ) && !array_key_exists( $langcode, $loaded[$extensionName] ) ) { |
| 2910 | + # Load messages for specified language. |
| 2911 | + $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName], $langcode ); |
| 2912 | + # Mark that they have been loaded. |
| 2913 | + $loaded[$extensionName][$langcode] = true; |
| 2914 | + } |
| 2915 | +} |
| 2916 | + |
| 2917 | +/** |
| 2918 | + * Get a platform-independent path to the null file, e.g. |
| 2919 | + * /dev/null |
| 2920 | + * |
| 2921 | + * @return string |
| 2922 | + */ |
| 2923 | +function wfGetNull() { |
| 2924 | + return wfIsWindows() |
| 2925 | + ? 'NUL' |
| 2926 | + : '/dev/null'; |
| 2927 | +} |
| 2928 | + |
| 2929 | +/** |
| 2930 | + * Displays a maxlag error |
| 2931 | + * |
| 2932 | + * @param string $host Server that lags the most |
| 2933 | + * @param int $lag Maxlag (actual) |
| 2934 | + * @param int $maxLag Maxlag (requested) |
| 2935 | + */ |
| 2936 | +function wfMaxlagError( $host, $lag, $maxLag ) { |
| 2937 | + global $wgShowHostnames; |
| 2938 | + header( 'HTTP/1.1 503 Service Unavailable' ); |
| 2939 | + header( 'Retry-After: ' . max( intval( $maxLag ), 5 ) ); |
| 2940 | + header( 'X-Database-Lag: ' . intval( $lag ) ); |
| 2941 | + header( 'Content-Type: text/plain' ); |
| 2942 | + if( $wgShowHostnames ) { |
| 2943 | + echo "Waiting for $host: $lag seconds lagged\n"; |
| 2944 | + } else { |
| 2945 | + echo "Waiting for a database server: $lag seconds lagged\n"; |
| 2946 | + } |
| 2947 | +} |
| 2948 | + |
| 2949 | +/** |
| 2950 | + * Throws an E_USER_NOTICE saying that $function is deprecated |
| 2951 | + * @param string $function |
| 2952 | + * @return null |
| 2953 | + */ |
| 2954 | +function wfDeprecated( $function ) { |
| 2955 | + global $wgDebugLogFile; |
| 2956 | + if ( !$wgDebugLogFile ) { |
| 2957 | + return; |
| 2958 | + } |
| 2959 | + $callers = wfDebugBacktrace(); |
| 2960 | + if( isset( $callers[2] ) ){ |
| 2961 | + $callerfunc = $callers[2]; |
| 2962 | + $callerfile = $callers[1]; |
| 2963 | + if( isset( $callerfile['file'] ) && isset( $callerfile['line'] ) ){ |
| 2964 | + $file = $callerfile['file'] . ' at line ' . $callerfile['line']; |
| 2965 | + } else { |
| 2966 | + $file = '(internal function)'; |
| 2967 | + } |
| 2968 | + $func = ''; |
| 2969 | + if( isset( $callerfunc['class'] ) ) |
| 2970 | + $func .= $callerfunc['class'] . '::'; |
| 2971 | + $func .= @$callerfunc['function']; |
| 2972 | + $msg = "Use of $function is deprecated. Called from $func in $file"; |
| 2973 | + } else { |
| 2974 | + $msg = "Use of $function is deprecated."; |
| 2975 | + } |
| 2976 | + wfDebug( "$msg\n" ); |
| 2977 | +} |
| 2978 | + |
| 2979 | +/** |
| 2980 | + * Sleep until the worst slave's replication lag is less than or equal to |
| 2981 | + * $maxLag, in seconds. Use this when updating very large numbers of rows, as |
| 2982 | + * in maintenance scripts, to avoid causing too much lag. Of course, this is |
| 2983 | + * a no-op if there are no slaves. |
| 2984 | + * |
| 2985 | + * Every time the function has to wait for a slave, it will print a message to |
| 2986 | + * that effect (and then sleep for a little while), so it's probably not best |
| 2987 | + * to use this outside maintenance scripts in its present form. |
| 2988 | + * |
| 2989 | + * @param int $maxLag |
| 2990 | + * @return null |
| 2991 | + */ |
| 2992 | +function wfWaitForSlaves( $maxLag ) { |
| 2993 | + if( $maxLag ) { |
| 2994 | + $lb = wfGetLB(); |
| 2995 | + list( $host, $lag ) = $lb->getMaxLag(); |
| 2996 | + while( $lag > $maxLag ) { |
| 2997 | + $name = @gethostbyaddr( $host ); |
| 2998 | + if( $name !== false ) { |
| 2999 | + $host = $name; |
| 3000 | + } |
| 3001 | + print "Waiting for $host (lagged $lag seconds)...\n"; |
| 3002 | + sleep($maxLag); |
| 3003 | + list( $host, $lag ) = $lb->getMaxLag(); |
| 3004 | + } |
| 3005 | + } |
| 3006 | +} |
| 3007 | + |
| 3008 | +/** |
| 3009 | + * Output some plain text in command-line mode or in the installer (updaters.inc). |
| 3010 | + * Do not use it in any other context, its behaviour is subject to change. |
| 3011 | + */ |
| 3012 | +function wfOut( $s ) { |
| 3013 | + static $lineStarted = false; |
| 3014 | + global $wgCommandLineMode; |
| 3015 | + if ( $wgCommandLineMode && !defined( 'MEDIAWIKI_INSTALL' ) ) { |
| 3016 | + echo $s; |
| 3017 | + } else { |
| 3018 | + echo htmlspecialchars( $s ); |
| 3019 | + } |
| 3020 | + flush(); |
| 3021 | +} |
| 3022 | + |
| 3023 | +/** Generate a random 32-character hexadecimal token. |
| 3024 | + * @param mixed $salt Some sort of salt, if necessary, to add to random characters before hashing. |
| 3025 | + */ |
| 3026 | +function wfGenerateToken( $salt = '' ) { |
| 3027 | + $salt = serialize($salt); |
| 3028 | + |
| 3029 | + return md5( mt_rand( 0, 0x7fffffff ) . $salt ); |
| 3030 | +} |
| 3031 | + |
| 3032 | +/** |
| 3033 | + * Replace all invalid characters with - |
| 3034 | + * @param mixed $title Filename to process |
| 3035 | + */ |
| 3036 | +function wfStripIllegalFilenameChars( $name ) { |
| 3037 | + $name = wfBaseName( $name ); |
| 3038 | + $name = preg_replace ( "/[^".Title::legalChars()."]/", '-', $name ); |
| 3039 | + return $name; |
| 3040 | +} |
Property changes on: trunk/extensions/NSFileRepo/REL1_15_0/phase3/includes/GlobalFunctions.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 3041 | + native |
Index: trunk/extensions/NSFileRepo/REL1_15_0/phase3/includes/GlobalFunctions.patch |
— | — | @@ -0,0 +1,12 @@ |
| 2 | +Index: GlobalFunctions.php
|
| 3 | +===================================================================
|
| 4 | +--- GlobalFunctions.php (revision 53111)
|
| 5 | +@@ -3034,6 +3034,6 @@
|
| 6 | + */ |
| 7 | + function wfStripIllegalFilenameChars( $name ) { |
| 8 | + $name = wfBaseName( $name ); |
| 9 | +- $name = preg_replace ( "/[^".Title::legalChars()."]|:/", '-', $name ); |
| 10 | ++ $name = preg_replace ( "/[^".Title::legalChars()."]/", '-', $name ); |
| 11 | + return $name; |
| 12 | + } |
Index: trunk/extensions/NSFileRepo/NSFileRepo.i18n.php |
— | — | @@ -0,0 +1,24 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Internationalisation file for DiscussionThreading extension. |
| 5 | + * |
| 6 | + * @addtogroup Extensions |
| 7 | +*/ |
| 8 | + |
| 9 | +$messages = array(); |
| 10 | + |
| 11 | +/** English |
| 12 | + * @author Jack D. Pond |
| 13 | + */ |
| 14 | +$messages['en'] = array( |
| 15 | + 'NSFileRepo-desc' => 'Add threading to talk pages' |
| 16 | +); |
| 17 | + |
| 18 | +/** Message documentation (Message documentation) |
| 19 | + * @author Fryed-peach |
| 20 | + * @author Purodha |
| 21 | + */ |
| 22 | +$messages['qqq'] = array( |
| 23 | + 'NSFileRepo-desc' => 'Provide NameSpace-based features to uploaded files and images.' |
| 24 | +); |
| 25 | + |
Property changes on: trunk/extensions/NSFileRepo/NSFileRepo.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 26 | + native |
Index: trunk/extensions/NSFileRepo/NSFileRepo.php |
— | — | @@ -0,0 +1,221 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * The purpose of this extension is to provide NameSpace-based features to uploaded files in the local file repositories (FileRepo) |
| 5 | + |
| 6 | + * The optimal solution would be a clean extension that is easily maintainable as the trunk of MW moves foward. |
| 7 | + * |
| 8 | + * @author Jack D. Pond <jack.pond@psitex.com> |
| 9 | + * @addtogroup Extensions |
| 10 | + * @copyright 2009 Jack D. pond |
| 11 | + * @url http://www.mediawiki.org/wiki/Manual:Extension:NSFileRepo |
| 12 | + * @licence GNU General Public Licence 2.0 or later |
| 13 | + * |
| 14 | + * This extension extends and is dependent on extension Lockdown - see http://www.mediawiki.org/wiki/Extension:Lockdown |
| 15 | + * It must be included(required) after Lockdown! |
| 16 | + */ |
| 17 | + |
| 18 | +if (!defined('MEDIAWIKI')) die('Not an entry point.'); |
| 19 | + |
| 20 | +# Internationalisation file |
| 21 | +$wgExtensionMessagesFiles['NSFileRepo'] = dirname(__FILE__) . '/NSFileRepo.i18n.php'; |
| 22 | + |
| 23 | + |
| 24 | +$wgExtensionFunctions[] = 'NSFileRepoSetup'; |
| 25 | +$wgExtensionCredits['media'][] = array( |
| 26 | + 'path' => __FILE__, |
| 27 | + 'name' => 'NSFileRepo', |
| 28 | + 'author' => 'Jack D. Pond', |
| 29 | + 'version' => '0.0', |
| 30 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:NSFileRepo', |
| 31 | + 'description' => 'Provide NameSpace-based features to uploaded files', |
| 32 | + 'descriptionmsg' => 'NSFileRepo-desc' |
| 33 | +); |
| 34 | + |
| 35 | + |
| 36 | +/** |
| 37 | + * Set up hooks for NSFileRepo |
| 38 | + * |
| 39 | + */ |
| 40 | + |
| 41 | +$wgHooks['UploadForm:BeforeProcessing'][] = 'NSFileRepoNSCheck'; |
| 42 | +/** |
| 43 | +Note, this must be AFTER lockdown has been included - thus assuming that the user has access to files in general + files at this particular namespace. |
| 44 | +*/ |
| 45 | +$wgHooks['userCan'][] = 'NSFileRepolockdownUserCan'; |
| 46 | +$wgHooks['ImgAuthBeforeStream'][] = 'NSFileRepoImgAuthCheck'; |
| 47 | + |
| 48 | + |
| 49 | +class NSLocalRepo extends LocalRepo { |
| 50 | + var $fileFactory = array( 'NSLocalFile', 'newFromTitle' ); |
| 51 | + var $oldFileFactory = array( 'NSOldLocalFile', 'newFromTitle' ); |
| 52 | + var $fileFromRowFactory = array( 'NSLocalFile', 'newFromRow' ); |
| 53 | + var $oldFileFromRowFactory = array( 'NSOldLocalFile', 'newFromRow' ); |
| 54 | + |
| 55 | + static function getHashPathForLevel( $name, $levels ) { |
| 56 | + global $wgContLang; |
| 57 | + $bits=explode(':',$name); |
| 58 | + $filename = $bits[count($bits)-1]; |
| 59 | + $path = parent::getHashPathForLevel( $filename, $levels ); |
| 60 | + return ((count($bits) > 1) ? $wgContLang->getNsIndex($bits[0]).'/'.$path : $path); |
| 61 | + } |
| 62 | + /** |
| 63 | + * Get a relative path including trailing slash, e.g. f/fa/ |
| 64 | + * If the repo is not hashed, returns an empty string |
| 65 | + * This is needed because self:: will call parent if not included - exact same as in FSRepo |
| 66 | + */ |
| 67 | + function getHashPath( $name ) { |
| 68 | + return self::getHashPathForLevel( $name, $this->hashLevels ); |
| 69 | + } |
| 70 | + /** |
| 71 | + * Pick a random name in the temp zone and store a file to it. |
| 72 | + * @param string $originalName The base name of the file as specified |
| 73 | + * by the user. The file extension will be maintained. |
| 74 | + * @param string $srcPath The current location of the file. |
| 75 | + * @return FileRepoStatus object with the URL in the value. |
| 76 | + */ |
| 77 | + function storeTemp( $originalName, $srcPath ) { |
| 78 | + $date = gmdate( "YmdHis" ); |
| 79 | + $hashPath = $this->getHashPath( $originalName ); |
| 80 | + $bits=explode(':',$originalName); |
| 81 | + $filename = $bits[count($bits)-1]; |
| 82 | + $dstRel = "$hashPath$date!$filename"; |
| 83 | + $dstUrlRel = $hashPath . $date . '!' . rawurlencode( $filename ); |
| 84 | + $result = $this->store( $srcPath, 'temp', $dstRel ); |
| 85 | + $result->value = $this->getVirtualUrl( 'temp' ) . '/' . $dstUrlRel; |
| 86 | + return $result; |
| 87 | + } |
| 88 | +} |
| 89 | + |
| 90 | +class NSLocalFile extends LocalFile |
| 91 | +{ |
| 92 | + /** |
| 93 | + * Get the path of the file relative to the public zone root |
| 94 | + */ |
| 95 | + function getRel() { |
| 96 | + $bits=explode(':',$this->getName()); |
| 97 | + $filename = $bits[count($bits)-1]; |
| 98 | + return $this->getHashPath() . $filename; |
| 99 | + } |
| 100 | + |
| 101 | + /** |
| 102 | + * Get urlencoded relative path of the file |
| 103 | + */ |
| 104 | + function getUrlRel() { |
| 105 | + $bits=explode(':',$this->getName()); |
| 106 | + $filename = $bits[count($bits)-1]; |
| 107 | + return $this->getHashPath() . rawurlencode( $filename ); |
| 108 | + } |
| 109 | + /** Instantiating this class using "self" |
| 110 | + * If you're reading this, you're problably wondering why on earth are the following static functions, which are copied |
| 111 | + * verbatim from the original extended class "LocalFIle" included here? |
| 112 | + * The answer is that "self", will instantiate the class the code is physically in, not the class extended from it. |
| 113 | + * Without the inclusion of these methods in "NSLocalFile, "self" would instantiate a "LocalFile" class, not the |
| 114 | + * "NSLocalFile" class we want it to. Since there are only two methods within the "LocalFile" class that use "self", |
| 115 | + * I just copied that code into the new "NSLocalFile" extended class, and the copied code will instantiate the "NSLocalFIle" |
| 116 | + * class instead of the "LocalFile" class (at least in PHP 5.2.4) |
| 117 | + */ |
| 118 | + |
| 119 | + /** |
| 120 | + * Create a NSLocalFile from a title |
| 121 | + * Do not call this except from inside a repo class. |
| 122 | + * |
| 123 | + * Note: $unused param is only here to avoid an E_STRICT |
| 124 | + */ |
| 125 | + static function newFromTitle( $title, $repo, $unused = null ) { |
| 126 | + return new self( $title, $repo ); |
| 127 | + } |
| 128 | + /** |
| 129 | + * Create a NSLocalFile from a title |
| 130 | + * Do not call this except from inside a repo class. |
| 131 | + */ |
| 132 | + |
| 133 | + static function newFromRow( $row, $repo ) { |
| 134 | + $title = Title::makeTitle( NS_FILE, $row->img_name ); |
| 135 | + $file = new self( $title, $repo ); |
| 136 | + $file->loadFromRow( $row ); |
| 137 | + return $file; |
| 138 | + } |
| 139 | +} |
| 140 | +class NSOldLocalFile extends OldLocalFile |
| 141 | +{ |
| 142 | + function getRel( $name, $levels ) { |
| 143 | + return(NSLocalFile::getRel()); |
| 144 | + } |
| 145 | + function getUrlRel( $name, $levels ) { |
| 146 | + return(NSLocalFile::getUrlRel()); |
| 147 | + } |
| 148 | + |
| 149 | + /** See comment about Instantiating this class using "self", above */ |
| 150 | + |
| 151 | + static function newFromTitle( $title, $repo, $time = null ) { |
| 152 | + # The null default value is only here to avoid an E_STRICT |
| 153 | + if( $time === null ) |
| 154 | + throw new MWException( __METHOD__.' got null for $time parameter' ); |
| 155 | + return new self( $title, $repo, $time, null ); |
| 156 | + } |
| 157 | + |
| 158 | + static function newFromArchiveName( $title, $repo, $archiveName ) { |
| 159 | + return new self( $title, $repo, null, $archiveName ); |
| 160 | + } |
| 161 | + |
| 162 | + static function newFromRow( $row, $repo ) { |
| 163 | + $title = Title::makeTitle( NS_FILE, $row->oi_name ); |
| 164 | + $file = new self( $title, $repo, null, $row->oi_archive_name ); |
| 165 | + $file->loadFromRow( $row, 'oi_' ); |
| 166 | + return $file; |
| 167 | + } |
| 168 | +} |
| 169 | + |
| 170 | + |
| 171 | +/** |
| 172 | + * Initial setup, add .i18n. messages from $IP/extensions/DiscussionThreading/DiscussionThreading.i18n.php |
| 173 | +*/ |
| 174 | +function NSFileRepoSetup() { |
| 175 | + global $wgLocalFileRepo; |
| 176 | + wfLoadExtensionMessages( 'NSFileRepo' ); |
| 177 | + $wgLocalFileRepo['class'] = "NSLocalRepo"; |
| 178 | + RepoGroup::destroySingleton(); |
| 179 | +} |
| 180 | +/* |
| 181 | + * Check for Namespace in Title Line |
| 182 | +*/ |
| 183 | +function NSFileRepoNSCheck($UploadForm) { |
| 184 | + $title = Title::newFromText($UploadForm->mDesiredDestName); |
| 185 | + if ($title->mNamespace < 100) { |
| 186 | + $UploadForm->mDesiredDestName = preg_replace ( "/:/", '-', $UploadForm->mDesiredDestName); |
| 187 | + } else { |
| 188 | + $bits=explode(':',$UploadForm->mDesiredDestName); |
| 189 | + $ns = array_shift($bits); |
| 190 | + $UploadForm->mDesiredDestName = $ns.":".implode("-",$bits); |
| 191 | + } |
| 192 | + return (true); |
| 193 | +} |
| 194 | + |
| 195 | + |
| 196 | +// If Extension:Lockdown has been activated (recommend), check individual namespace protection |
| 197 | + |
| 198 | +function NSFileRepolockdownUserCan($title, $user, $action, &$result) { |
| 199 | + if (function_exists('lockdownUserCan')){ |
| 200 | + if($title->getNamespace() == NS_FILE) { |
| 201 | + $ntitle = Title::newFromText($title->mDbkeyform); |
| 202 | + return ($ntitle->mNamespace < 100) ? true : lockdownUserCan($ntitle, $user, $action, $result); |
| 203 | + } |
| 204 | + } |
| 205 | + return true; |
| 206 | +} |
| 207 | + |
| 208 | +function NSFileRepoImgAuthCheck($title, $path, $name, $result) { |
| 209 | + global $wgContLang; |
| 210 | + |
| 211 | +# See if stored in a NS path |
| 212 | + |
| 213 | + $subdirs = explode('/',$_SERVER['PATH_INFO']); |
| 214 | + if (strlen($subdirs[1]) == 3 && is_numeric($subdirs[1]) && $subdirs[1] >= 100) { |
| 215 | + $title = Title::makeTitleSafe( NS_FILE, $wgContLang->getNsText($subdirs[1]).":".$name ); |
| 216 | + if( !$title instanceof Title ) { |
| 217 | + $result = array(wfMsgHTML('image_auth-accessdenied'),wfMsgHTML('image_auth-badtitle',$name)); |
| 218 | + return false; |
| 219 | + } |
| 220 | + } |
| 221 | + return true; |
| 222 | +} |
\ No newline at end of file |
Property changes on: trunk/extensions/NSFileRepo/NSFileRepo.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 223 | + native |
Index: trunk/extensions/NSFileRepo/README |
— | — | @@ -0,0 +1,144 @@ |
| 2 | +{{Page security extension disclaimer}}
|
| 3 | +{{Extension by patch warning|version=}}
|
| 4 | +{{Extension|templatemode=
|
| 5 | +|name = NSFileRepo
|
| 6 | +|status = beta
|
| 7 | +|type1 = user rights
|
| 8 | +|type2 =
|
| 9 | +|hook1 = userCan
|
| 10 | +|hook2 = ImgAuthBeforeStream
|
| 11 | +|username = [[User:Jpond|Jack D. Pond]]
|
| 12 | +|author = <!-- add only if different from user name -->
|
| 13 | +|description = implements per-namespace group permissions for image and file rights protection
|
| 14 | +|image =
|
| 15 | +|version = 0.0
|
| 16 | +|update = 2009-07-11
|
| 17 | +|mediawiki = 1.13, 1.14, 1.15
|
| 18 | +|license = GNU General Public Licence 2.0
|
| 19 | +|download = {{WikimediaDownload|NSFileRepo}}
|
| 20 | +|readme = [http://svn.wikimedia.org/svnroot/mediawiki/trunk/extensions/NSFileRepo/README README]
|
| 21 | +|changelog = [http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/NSFileRepo/?view=log log]
|
| 22 | +|parameters = <!-- configuration parameters, for use in LocalSettings.php -->
|
| 23 | +|rights = <!-- user rights, for use with $wgGroupPermissions -->
|
| 24 | +|example =
|
| 25 | +}}
|
| 26 | +
|
| 27 | +==What can this extension do?==
|
| 28 | +
|
| 29 | +The '''NSFileRepo''' extension implements a way to restrict access to specific files and images to a given set of user groups. This provides a more fine grained security model and allows access restriction to users in specified groups.
|
| 30 | +
|
| 31 | +__TOC__
|
| 32 | +==Usage==
|
| 33 | +
|
| 34 | +Generically, you use the same syntax as a normal file/image reference link, adding the namespace between the file specifier ("File","Image", or "Media"), and the file name:
|
| 35 | +
|
| 36 | +<pre>[[{FILE_NS}:{Namespace}:{Filename}]]</pre>
|
| 37 | +
|
| 38 | +Example(Where "Project" is the protected namespace and "ProjectFile.pdf" is the file to which you wish to limit access):
|
| 39 | +
|
| 40 | +<pre>[[File:Project:ProjectFile.pdf]]</pre>
|
| 41 | +<br>
|
| 42 | +The standard for accessing files/images is generally:
|
| 43 | +<pre>
|
| 44 | +[[File:Filename.txt]]
|
| 45 | +[[Image:Filename.jpg]]
|
| 46 | +[[Media:Filename.pdf]]
|
| 47 | +</pre>
|
| 48 | +
|
| 49 | +This extension allows you to protect access to files/images, by adding the namespace text identifier after the file namespace identifier, for example(Where "Project" is the protected namespace and "ProjectFile.xxx" is the file to which you wish to limit access):
|
| 50 | +<pre>
|
| 51 | +[[File:Project:Filename.txt]]
|
| 52 | +[[Image:Project:Filename.jpg]]
|
| 53 | +[[Media:Project:Filename.pdf]]
|
| 54 | +</pre>
|
| 55 | +
|
| 56 | +It may be helpful to understand the default security model used by MediaWiki using the instructions below:
|
| 57 | +
|
| 58 | +* [[Manual:User rights]]
|
| 59 | +* [[Manual:$wgGroupPermissions]]
|
| 60 | +
|
| 61 | +Limitations of security are the same as for Extension Lockdown. To review these limitations, [[Extension:Lockdown#Additional_measures | please reference here]].
|
| 62 | +
|
| 63 | +To use the full capabilities of this extension (e.g., specific namespace protections), you will need to install and use the namespace protections provided through [[Extension:Lockdown]].
|
| 64 | +
|
| 65 | +This extension was made possible by the introduction of Repository Classes by Tim Starling - an elegant and brilliant implementation. It uses a new Local Repository class mechanism. Technical details on how this extension works can be found [[Extension:NSFileRepo/DOC | here]].
|
| 66 | +
|
| 67 | +== Announcements ==
|
| 68 | +
|
| 69 | +* The first version of this (Rel 0.0) was released 2009-07-11. The following activities are underway to make this extension easier to install and use, including:
|
| 70 | +** Modifying and updating the standard version of img_auth.php to include localization and a hook necessary for this extension. Will hopefully be approved for version 1.16
|
| 71 | +** Discussing ways to allow modification of wfStripIllegalFilenameChars so that future patching will not be needed.
|
| 72 | +
|
| 73 | +==Download instructions==
|
| 74 | +
|
| 75 | +This Extension and the necessary patch/files may be downloaded from one of the following (SVN preferred)
|
| 76 | +
|
| 77 | +* Revision 0.0 (beta)
|
| 78 | +** [http://wiki.montcopa.org/PublicDownloads/NSFileRepo.tar tar] (May require eol conversion)
|
| 79 | +** [http://wiki.montcopa.org/PublicDownloads/NSFileRepo.tar zip]
|
| 80 | +** [http://svn.wikimedia.org/svnroot/mediawiki/trunk/extensions/NSFileRepo SVN]
|
| 81 | +
|
| 82 | +Copy all files and directories into directory:
|
| 83 | +
|
| 84 | +<pre>
|
| 85 | +$IP/extensions/NSFileRepo
|
| 86 | +</pre>
|
| 87 | +
|
| 88 | +==Installation==
|
| 89 | +
|
| 90 | +You will need to read and understand two other required enhancements to MediaWiki:
|
| 91 | +
|
| 92 | +* [[Manual:Image Authorization | Image Authorization]].
|
| 93 | +* [[Extension:Lockdown | Extension Lockdown]]
|
| 94 | +
|
| 95 | +Please read and understand the above before executing the following instructions
|
| 96 | +
|
| 97 | +# Download and install [[Extension:Lockdown | Extension Lockdown]]
|
| 98 | +# Download and copy the NSFileRepo extension into directory <nowiki>$IP/extensions/NSFileRepo</nowiki>
|
| 99 | +# Activate the Image Authorization according to instructions found in [[Manual:Image Authorization | Image Authorization]]
|
| 100 | +# Copy the img_auth.php and img_auth.i18.php from the distribution in directory <nowiki>{release}/phase3/</nowiki> to your wiki code base directory ($IP). This will overwrite the existing img_auth.php file. Alternately you could copy img_auth.php to another name in the same directory, then use that file name instead of img_auth.php (but still must be in the $IP directory and the localization file must still be img_auth.i18.php).
|
| 101 | +# <nowiki>$IP/include/GlobalFunctions.php</nowiki> Must be patched. This is a very minor patch to remove the disabling of colons (':'). You can do this one of three ways (whichever you're most comfortable with):
|
| 102 | +## Edit the file according to instructions [[#Patch_GlobalFunctions.php | below]]
|
| 103 | +## If you have not otherwise patched the file, you may want to copy it from the distribution, which will be in a directory corresponding to the release you are using under <nowiki>{release}/phase3/includes/GlobalFunctions.php</nowiki>
|
| 104 | +## Apply the patch which will be in a directory corresponding to the release you are using under <nowiki>{release}/phase3/includes/GlobalFunctions.patch</nowiki>
|
| 105 | +# To activate this extension, add the following to [[Manual:LocalSettings.php|LocalSettings.php]]:
|
| 106 | +<source lang="php">
|
| 107 | +require_once("$IP/extensions/NSFileRepo/NSFileRepo.php");
|
| 108 | +</source>
|
| 109 | +
|
| 110 | +===Configuration parameters===
|
| 111 | +
|
| 112 | +The user rights and configuration requiremements are are the same as described in [[Extension:Lockdown#Configuration | Extension Lockdown]].
|
| 113 | +
|
| 114 | +==Patch GlobalFunctions.php==
|
| 115 | +
|
| 116 | +In version 1_13_0, a new function wfStripIllegalFilenameChars was added to <nowiki>includes/GlobalFunctions.php</nowiki>. This prevents the protection namespace from being detectd.
|
| 117 | +
|
| 118 | +<pre>
|
| 119 | +Index: GlobalFunctions.php
|
| 120 | +===================================================================
|
| 121 | +--- GlobalFunctions.php (revision 52849)
|
| 122 | +@@ -3034,6 +3034,6 @@
|
| 123 | + */
|
| 124 | + function wfStripIllegalFilenameChars( $name ) {
|
| 125 | + $name = wfBaseName( $name );
|
| 126 | +- $name = preg_replace ( "/[^".Title::legalChars()."]|:/", '-', $name );
|
| 127 | ++ $name = preg_replace ( "/[^".Title::legalChars()."]/", '-', $name );
|
| 128 | + return $name;
|
| 129 | + }
|
| 130 | +</pre>
|
| 131 | +<br>
|
| 132 | +<br>
|
| 133 | +You need to remove the "or :" clause from the REGEX expression of wfStripIllegalFilenameChars by deleting the characters <nowiki>"|:"</nowiki>
|
| 134 | +<br>
|
| 135 | +
|
| 136 | +==See also==
|
| 137 | +
|
| 138 | +* [[Manual:Image Authorization | Image Authorization]].
|
| 139 | +* [[Extension:Lockdown | Extension Lockdown]]
|
| 140 | +
|
| 141 | +[[Category:View page extensions]]
|
| 142 | +[[Category:Edit extensions]]
|
| 143 | +
|
| 144 | +{{languages}}
|