]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blobdiff - includes/api/ApiMain.php
MediaWiki 1.15.5
[autoinstallsdev/mediawiki.git] / includes / api / ApiMain.php
index 60d932be1ff6a32506d2f1ee7e37b851279e090f..063e357496f66d8ac488a7fe1d83875d693a2075 100644 (file)
@@ -65,10 +65,9 @@ class ApiMain extends ApiBase {
                'feedwatchlist' => 'ApiFeedWatchlist',
                'help' => 'ApiHelp',
                'paraminfo' => 'ApiParamInfo',
-               'purge' => 'ApiPurge',
-       );
 
-       private static $WriteModules = array (
+               // Write modules
+               'purge' => 'ApiPurge',
                'rollback' => 'ApiRollback',
                'delete' => 'ApiDelete',
                'undelete' => 'ApiUndelete',
@@ -80,6 +79,7 @@ class ApiMain extends ApiBase {
                'emailuser' => 'ApiEmailUser',
                'watch' => 'ApiWatch',
                'patrol' => 'ApiPatrol',
+               'import' => 'ApiImport',
        );
 
        /**
@@ -121,7 +121,9 @@ class ApiMain extends ApiBase {
 
 
        private $mPrinter, $mModules, $mModuleNames, $mFormats, $mFormatNames;
-       private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode, $mSquidMaxage;
+       private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode;
+       private $mCacheMode = 'private';
+       private $mCacheControl = array();
 
        /**
        * Constructs an instance of ApiMain that utilizes the module and format specified by $request.
@@ -149,20 +151,10 @@ class ApiMain extends ApiBase {
                                wfDebug( "API: stripping user credentials for JSON callback\n" );
                                $wgUser = new User();
                        }
-
-                       if (!$wgUser->isAllowed('read')) {
-                               self::$Modules = array(
-                                       'login'  => self::$Modules['login'],
-                                       'logout' => self::$Modules['logout'],
-                                       'help'   => self::$Modules['help'],
-                                       );
-                       }
                }
 
-               global $wgAPIModules, $wgEnableWriteAPI; // extension modules
+               global $wgAPIModules; // extension modules
                $this->mModules = $wgAPIModules + self :: $Modules;
-               if($wgEnableWriteAPI)
-                       $this->mModules += self::$WriteModules;
 
                $this->mModuleNames = array_keys($this->mModules);
                $this->mFormats = self :: $Formats;
@@ -174,7 +166,6 @@ class ApiMain extends ApiBase {
 
                $this->mRequest = & $request;
 
-               $this->mSquidMaxage = -1; // flag for executeActionWithErrorHandling()
                $this->mCommit = false;
        }
 
@@ -200,28 +191,103 @@ class ApiMain extends ApiBase {
        }
 
        /**
-        * This method will simply cause an error if the write mode was disabled
-        * or if the current user doesn't have the right to use it
+        * Only kept for backwards compatibility
+        * @deprecated Use isWriteMode() instead
         */
        public function requestWriteMode() {
-               global $wgUser;
                if (!$this->mEnableWrite)
-                       $this->dieUsage('Editing of this wiki through the API' .
-                       ' is disabled. Make sure the $wgEnableWriteAPI=true; ' .
-                       'statement is included in the wiki\'s ' .
-                       'LocalSettings.php file', 'noapiwrite');
-               if (!$wgUser->isAllowed('writeapi'))
-                       $this->dieUsage('You\'re not allowed to edit this ' .
-                       'wiki through the API', 'writeapidenied');
+                       $this->dieUsageMsg(array('writedisabled'));
                if (wfReadOnly())
-                       $this->dieUsageMsg(array('readonlytext'));
+                       $this->dieUsageMsg(array('readonlytext'));              
        }
 
        /**
         * Set how long the response should be cached.
         */
        public function setCacheMaxAge($maxage) {
-               $this->mSquidMaxage = $maxage;
+               $this->setCacheControl( array(
+                       'max-age' => $maxage,
+                       's-maxage' => $maxage
+               ) );
+       }
+
+       /**
+        * Set the type of caching headers which will be sent.
+        *
+        * @param $mode One of:
+        *    - 'public':     Cache this object in public caches, if the maxage or smaxage 
+        *         parameter is set, or if setCacheMaxAge() was called. If a maximum age is
+        *         not provided by any of these means, the object will be private.
+        *    - 'private':    Cache this object only in private client-side caches.
+        *    - 'anon-public-user-private': Make this object cacheable for logged-out
+        *         users, but private for logged-in users. IMPORTANT: If this is set, it must be 
+        *         set consistently for a given URL, it cannot be set differently depending on 
+        *         things like the contents of the database, or whether the user is logged in.
+        *
+        *  If the wiki does not allow anonymous users to read it, the mode set here
+        *  will be ignored, and private caching headers will always be sent. In other words, 
+        *  the "public" mode is equivalent to saying that the data sent is as public as a page
+        *  view.
+        *
+        *  For user-dependent data, the private mode should generally be used. The 
+        *  anon-public-user-private mode should only be used where there is a particularly 
+        *  good performance reason for caching the anonymous response, but where the
+        *  response to logged-in users may differ, or may contain private data. 
+        *
+        *  If this function is never called, then the default will be the private mode.
+        */
+       public function setCacheMode( $mode ) {
+               if ( !in_array( $mode, array( 'private', 'public', 'anon-public-user-private' ) ) ) {
+                       wfDebug( __METHOD__.": unrecognised cache mode \"$mode\"\n" );
+                       // Ignore for forwards-compatibility
+                       return;
+               }
+
+               if ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) ) {
+                       // Private wiki, only private headers
+                       if ( $mode !== 'private' ) {
+                               wfDebug( __METHOD__.": ignoring request for $mode cache mode, private wiki\n" );
+                               return;
+                       }
+               }
+
+               wfDebug( __METHOD__.": setting cache mode $mode\n" );
+               $this->mCacheMode = $mode;
+       }
+       
+       /**
+        * @deprecated Private caching is now the default, so there is usually no 
+        * need to call this function. If there is a need, you can use 
+        * $this->setCacheMode('private')
+        */
+       public function setCachePrivate() {
+               $this->setCacheMode( 'private' );
+       }
+
+       /**
+        * Set directives (key/value pairs) for the Cache-Control header.
+        * Boolean values will be formatted as such, by including or omitting
+        * without an equals sign.
+        *
+        * Cache control values set here will only be used if the cache mode is not 
+        * private, see setCacheMode().
+        */
+       public function setCacheControl( $directives ) {
+               $this->mCacheControl = $directives + $this->mCacheControl;
+       }
+       
+       /**
+        * Make sure Vary: Cookie and friends are set. Use this when the output of a request
+        * may be cached for anons but may not be cached for logged-in users.
+        *
+        * WARNING: This function must be called CONSISTENTLY for a given URL. This means that a
+        * given URL must either always or never call this function; if it sometimes does and
+        * sometimes doesn't, stuff will break.
+        *
+        * @deprecated Use setCacheMode( 'anon-public-user-private' )
+        */
+       public function setVaryCookie() {
+               $this->setCacheMode( 'anon-public-user-private' );
        }
 
        /**
@@ -273,7 +339,7 @@ class ApiMain extends ApiBase {
                        $errCode = $this->substituteResultWithError($e);
 
                        // Error results should not be cached
-                       $this->setCacheMaxAge(0);
+                       $this->setCacheMode( 'private' );
 
                        $headerStr = 'MediaWiki-API-Error: ' . $errCode;
                        if ($e->getCode() === 0)
@@ -289,21 +355,9 @@ class ApiMain extends ApiBase {
                        $this->printResult(true);
                }
 
-               if($this->mSquidMaxage == -1)
-               {
-                       # Nobody called setCacheMaxAge(), use the (s)maxage parameters
-                       $smaxage = $this->getParameter('smaxage');
-                       $maxage = $this->getParameter('maxage');
-               }
-               else
-                       $smaxage = $maxage = $this->mSquidMaxage;
-
-               // Set the cache expiration at the last moment, as any errors may change the expiration.
-               // if $this->mSquidMaxage == 0, the expiry time is set to the first second of unix epoch
-               $exp = min($smaxage, $maxage);
-               $expires = ($exp == 0 ? 1 : time() + $exp);
-               header('Expires: ' . wfTimestamp(TS_RFC2822, $expires));
-               header('Cache-Control: s-maxage=' . $smaxage . ', must-revalidate, max-age=' . $maxage);
+               // Send cache headers after any code which might generate an error, to 
+               // avoid sending public cache headers for errors.
+               $this->sendCacheHeaders();
 
                if($this->mPrinter->getIsHtml())
                        echo wfReportTime();
@@ -311,6 +365,71 @@ class ApiMain extends ApiBase {
                ob_end_flush();
        }
 
+       protected function sendCacheHeaders() {
+               if ( $this->mCacheMode == 'private' ) {
+                       header( 'Cache-Control: private' );
+                       return;
+               }
+
+               if ( $this->mCacheMode == 'anon-public-user-private' ) {
+                       global $wgOut;
+                       header( 'Vary: Accept-Encoding, Cookie' );
+                       header( $wgOut->getXVO() );
+                       if ( session_id() != '' || $wgOut->haveCacheVaryCookies() ) {
+                               // Logged in, mark this request private
+                               header( 'Cache-Control: private' );
+                               return;
+                       }
+                       // Logged out, send normal public headers below
+               } else /* if public */ {
+                       // Give a debugging message if the user object is unstubbed on a public request
+                       global $wgUser;
+                       if ( !( $wgUser instanceof StubUser ) ) {
+                               wfDebug( __METHOD__." \$wgUser is unstubbed on a public request!\n" );
+                       }
+               }
+
+               // If nobody called setCacheMaxAge(), use the (s)maxage parameters
+               if ( !isset( $this->mCacheControl['s-maxage'] ) ) {
+                       $this->mCacheControl['s-maxage'] = $this->getParameter( 'smaxage' );
+               }
+               if ( !isset( $this->mCacheControl['max-age'] ) ) {
+                       $this->mCacheControl['max-age'] = $this->getParameter( 'maxage' );
+               }
+               
+               if ( !$this->mCacheControl['s-maxage'] && !$this->mCacheControl['max-age'] ) {
+                       // Public cache not requested
+                       // Sending a Vary header in this case is harmless, and protects us
+                       // against conditional calls of setCacheMaxAge().
+                       header( 'Cache-Control: private' );
+                       return;
+               }
+
+               $this->mCacheControl['public'] = true;
+
+               // Send an Expires header
+               $maxAge = min( $this->mCacheControl['s-maxage'], $this->mCacheControl['max-age'] );
+               $expiryUnixTime = ( $maxAge == 0 ? 1 : time() + $maxAge );
+               header( 'Expires: ' . wfTimestamp( TS_RFC2822, $expiryUnixTime ) );
+
+               // Construct the Cache-Control header
+               $ccHeader = '';
+               $separator = '';
+               foreach ( $this->mCacheControl as $name => $value ) {
+                       if ( is_bool( $value ) ) {
+                               if ( $value ) {
+                                       $ccHeader .= $separator . $name;
+                                       $separator = ', ';
+                               }
+                       } else {
+                               $ccHeader .= $separator . "$name=$value";
+                               $separator = ', ';
+                       }
+               }
+
+               header( "Cache-Control: $ccHeader" );
+       }
+
        /**
         * Replace the result data with the information about an exception.
         * Returns the error code
@@ -360,9 +479,11 @@ class ApiMain extends ApiBase {
                        }
 
                        $this->getResult()->reset();
+                       $this->getResult()->disableSizeCheck();
                        // Re-add the id
-                       if($this->mRequest->getCheck('requestid'))
-                               $this->getResult()->addValue(null, 'requestid', $this->mRequest->getVal('requestid'));
+                       $requestid = $this->getParameter('requestid');
+                       if(!is_null($requestid))
+                               $this->getResult()->addValue(null, 'requestid', $requestid);
                        $this->getResult()->addValue(null, 'error', $errMessage);
 
                return $errMessage['code'];
@@ -373,8 +494,9 @@ class ApiMain extends ApiBase {
         */
        protected function executeAction() {
                // First add the id to the top element
-               if($this->mRequest->getCheck('requestid'))
-                       $this->getResult()->addValue(null, 'requestid', $this->mRequest->getVal('requestid'));
+               $requestid = $this->getParameter('requestid');
+               if(!is_null($requestid))
+                       $this->getResult()->addValue(null, 'requestid', $requestid);
 
                $params = $this->extractRequestParams();
 
@@ -398,14 +520,26 @@ class ApiMain extends ApiBase {
                                header( 'X-Database-Lag: ' . intval( $lag ) );
                                // XXX: should we return a 503 HTTP error code like wfMaxlagError() does?
                                if( $wgShowHostnames ) {
-                                       ApiBase :: dieUsage( "Waiting for $host: $lag seconds lagged", 'maxlag' );
+                                       $this->dieUsage( "Waiting for $host: $lag seconds lagged", 'maxlag' );
                                } else {
-                                       ApiBase :: dieUsage( "Waiting for a database server: $lag seconds lagged", 'maxlag' );
+                                       $this->dieUsage( "Waiting for a database server: $lag seconds lagged", 'maxlag' );
                                }
                                return;
                        }
                }
 
+               global $wgUser;
+               if ($module->isReadMode() && !$wgUser->isAllowed('read'))
+                       $this->dieUsageMsg(array('readrequired'));
+               if ($module->isWriteMode()) {
+                       if (!$this->mEnableWrite)
+                               $this->dieUsageMsg(array('writedisabled'));
+                       if (!$wgUser->isAllowed('writeapi'))
+                               $this->dieUsageMsg(array('writerequired'));
+                       if (wfReadOnly())
+                               $this->dieUsageMsg(array('readonlytext'));
+               }
+
                if (!$this->mInternalMode) {
                        // Ignore mustBePosted() for internal calls
                        if($module->mustBePosted() && !$this->mRequest->wasPosted())
@@ -438,7 +572,7 @@ class ApiMain extends ApiBase {
         * Print results using the current printer
         */
        protected function printResult($isError) {
-               $this->getResult()->cleanupUTF8();
+               $this->getResult()->cleanUpUTF8();
                $printer = $this->mPrinter;
                $printer->profileIn();
 
@@ -454,6 +588,10 @@ class ApiMain extends ApiBase {
                $printer->closePrinter();
                $printer->profileOut();
        }
+       
+       public function isReadMode() {
+               return false;
+       }
 
        /**
         * See ApiBase for description.
@@ -657,7 +795,7 @@ class ApiMain extends ApiBase {
        public function getVersion() {
                $vers = array ();
                $vers[] = 'MediaWiki: ' . SpecialVersion::getVersion() . "\n    http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/";
-               $vers[] = __CLASS__ . ': $Id: ApiMain.php 45752 2009-01-14 21:36:57Z catrope $';
+               $vers[] = __CLASS__ . ': $Id: ApiMain.php 69990 2010-07-27 08:44:08Z tstarling $';
                $vers[] = ApiBase :: getBaseVersion();
                $vers[] = ApiFormatBase :: getBaseVersion();
                $vers[] = ApiQueryBase :: getBaseVersion();