Index: includes/api/ApiForeignRequest.php =================================================================== --- includes/api/ApiForeignRequest.php (revision 0) +++ includes/api/ApiForeignRequest.php (revision 0) @@ -0,0 +1,231 @@ +mApiName = $apiName; + $this->checkConfig( $apiConfig ); + if ( $this->mIsSetup && isset( $apiRequest['action'] ) ) { + $this->mReturnType = self :: API_FOREIGN_RETURN_TYPE; // Can be set later + unset( $apiRequest['format'] ); + $this->mQuery = $apiRequest; + return true; + } + else { + wfDebug( __CLASS__ . "::" . __METHOD__ . ": Passed a bad config for API: " . $this->mApiName ); + return false; + } + } + + /** + * Instantiate a new request. + * + * @param $apiName String The configuration name of the foreign API + * @param $request FauxRequest A fake request built with FauxRequest + * + * @return mixed ApiForeignRequest object on success, false on failure + */ + public static function newRequest( $apiName = '', FauxRequest $request ) { + global $wgForeignApiRequestConf; + if ( !isset( $wgForeignApiRequestConf[$apiName] ) ) { + wfDebug("Passed a bogus foreign API name: $apiName\n"); + return false; + } + return new ApiForeignRequest( $apiName, $wgForeignApiRequestConf[$apiName], $request->getValues() ); + } + + /** + * Check the configuration of the foreign API to see if we can run with this + * + * @param $conf Array Configuration array for the foreign API. See $wgForeignApiRequestConf + * @return bool + */ + private function checkConfig( $conf = array() ) { + if ( $this->mIsSetup === true ) { + return true; + } + if ( isset( $conf['api_url'] ) ) { + $this->mUrlBase = $conf['api_url']; + if ( isset( $conf['auth_type'] ) ) { + $this->mAuth['type'] = $conf['auth_type']; + if ( $this->mAuth['type'] === self :: API_FOREIGN_AUTH_USER ) { + $this->mAuth['user'] = $conf['auth_user']; + $this->mAuth['pass'] = $conf['auth_pass']; + } + } + else { + $this->mAuth['type'] = self :: API_FOREIGN_AUTH_NONE; + } + if ( isset( $conf['cache_time'] ) ) $this->mCacheTime = $conf['cache_time']; + $this->mIsSetup = true; + return true; + } + else { + $this->mIsSetup = false; + return false; + } + } + + /** + * Set the return type. Takes any param found in ForeignApiRequest::$ReturnTypes + * @return bool + */ + public function setReturnType( $type = '' ) { + if ( isset( ForeignApiRequest :: $ReturnTypes[ $type ] ) ) { + $this->mReturnType = $type; + return true; + } + else { + $this->mReturnType = ApiForeignRequest :: API_FOREIGN_RETURN_TYPE; + return false; + } + + } + + /** + * Set the query to be POST instead of GET. + * @param bool + */ + public function isPost( $allow ) { + $this->mPost = (bool)$allow; + } + + /** + * Load a new query into an already-setup ApiForeignRequest object. Keeps + * from doing revalidation on the config. + * @param FauxRequest A FauxRequest object + * @param bool $isPost Do we post this query? + * @return bool + */ + public function loadNewQuery( FauxRequest $newQuery, $isPost = false ) { + $newQuery = $newQuery->getValues(); + if ( !$this->mIsSetup || !isset( $newQuery['action'] ) ) { + wfDebug( __CLASS__ . "::" . __METHOD__ . ": Cannot load new query for API: " . $this->mApiName ); + return false; + } + else { + $newQuery = $newQuery->getValues(); + unset( $newQuery['format'] ); + $this->mQuery = $newQuery; + return true; + } + } + + /** + * Do the request. We'll do all the permission checking here. Authentication + * should be done at this stage as well. + */ + public function doRequest() { + if ( !$this->mIsSetup ) { + return false; + } + $this->mFullUrl = $this->mUrlBase . '?format=' . self :: API_FOREIGN_REQUEST_TYPE. + '&' . wfArrayToCGI( $this->mQuery ); + $key = wfMemcKey( 'ApiForeignRequest', $this->mApiName, md5( $this->mFullUrl ) ); + if ( isset ( $this->mLocalQueryCache[$key] ) ) { + $this->mResults = $this->mLocalQueryCache[$key]; + return true; + } + global $wgMemc; + $cache = $wgMemc->get( $key ); + if ( $cache ) { // Found it in Memcached + if ( count( $this->mLocalQueryCache ) > 100 ) { + $this->mLocalQueryCache = array(); // Flush the local querycache if it gets too big + } + $this->mLocalQueryCache[$key] = $cache; + $this->mResults = $cache; + return true; + } + if ( !$this->authorize() ) { + return false; + } + + if ( $this->mPost ) { + $res = Http::post( $this->mFullUrl ); + } + else { + $res = Http::get( $this->mFullUrl ); + } + if ( !$res ) { + return false; // Had problems fetching the remote resource + } + + // Decode the JSON and store it to local class cache and Memcached, if enabled + $this->mLocalQueryCache[$key] = json_decode( $res, true ); + if ( $this->mCacheTime > 0 ) { + $wgMemc->set( $key, $this->mLocalQueryCache[$key], $this->mCacheTime ); + } + $this->mResults = $this->mLocalQueryCache[$key]; + } + + /** + * Check if this MediaWiki install is authorized to query + * the remote one. + */ + private function authorize() { + if ( !is_null( $this->mCanRequest ) ) { + return $this->mCanRequest; + } + switch ( $this->mAuth['type'] ) { + case ApiForeignRequest :: API_FOREIGN_AUTH_NONE: + $this->mCanRequest = true; + break; + default: + $this->mCanRequest = $this->attemptRemoteLogin(); + break; + } + return $this->mCanRequest; + } + + private function attemptRemoteLogin() { + $res = Http::post( $this->mUrlBase . '?action=login&lgname=' . $this->mAuth['user'] . '&lgpassword=' . + $this->mAuth['pass'] . '&format=' . ApiForeignRequest :: API_FOREIGN_REQUEST_TYPE ); + $res = json_decode( $res, true ); + if ( $res['login']['result'] === 'Success' ) { + return true; + } + else { + return false; + } + } + + /** + * Get the raw results. Not very useful for most things. + * @return mixed + */ + public function getRawResults() { + return $this->mResults ? $this->mResults : false; + } +} Property changes on: includes\api\ApiForeignRequest.php ___________________________________________________________________ Name: svn:eol-style + native Index: includes/AutoLoader.php =================================================================== --- includes/AutoLoader.php (revision 39783) +++ includes/AutoLoader.php (working copy) @@ -237,6 +237,7 @@ 'ApiEmailUser' => 'includes/api/ApiEmailUser.php', 'ApiExpandTemplates' => 'includes/api/ApiExpandTemplates.php', 'ApiFeedWatchlist' => 'includes/api/ApiFeedWatchlist.php', + 'ApiForeignRequest' => 'includes/api/ApiForeignRequest.php', 'ApiFormatBase' => 'includes/api/ApiFormatBase.php', 'ApiFormatDbg' => 'includes/api/ApiFormatDbg.php', 'ApiFormatFeedWrapper' => 'includes/api/ApiFormatBase.php', @@ -551,3 +552,4 @@ } } + Index: includes/DefaultSettings.php =================================================================== --- includes/DefaultSettings.php (revision 39786) +++ includes/DefaultSettings.php (working copy) @@ -3254,6 +3254,29 @@ $wgEnableWriteAPI = false; /** + * $wgForeignApiRequestConf - A configuration array of + * of remote APIs we can connect to. The array key is + * a unique name for the API, allows having multiple + * foreign API configs. + * 'auth_type' :: One of either ApiForeignRequest::API_FOREIGN_AUTH_NONE (no authorization required) + * or ApiForeignRequest::API_FOREIGN_AUTH_USER (username and password required) + * 'auth_user' :: Username to login with if remote logins are required (usually needed for writeapi) + * 'auth_pass' :: Password for said user + * 'cache_length' :: How long (in seconds) to cache remote API data + * 'api_url' :: The exact URL to the foreign api.php + * + * Example: + * $wgForeignApiRequestConf['enwiki'] = array ( + * 'auth_type' => ApiForeignRequest::API_FOREIGN_AUTH_USER, + * 'auth_user' => 'SomeUser', + * 'auth_pass' => 'SomePass', + * 'cache_length' => 3600, + * 'api_url' => 'http://en.wikipedia.org/w/api.php', + * ); + */ +$wgForeignApiRequestConf = array(); + +/** * API module extensions * Associative array mapping module name to class name. * Extension modules may override the core modules.