diff options
Diffstat (limited to 'bin/reevotech/vendor/addwiki/mediawiki-api-base/src')
14 files changed, 1520 insertions, 0 deletions
diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/ApiRequester.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/ApiRequester.php new file mode 100644 index 00000000..cc3c8bcf --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/ApiRequester.php @@ -0,0 +1,31 @@ +<?php + +namespace Mediawiki\Api; + +/** + * @since 2.2 + * @licence GNU GPL v2+ + * @author Addshore + * @author Jeroen De Dauw < jeroendedauw@gmail.com > + */ +interface ApiRequester { + + /** + * @since 2.2 + * + * @param Request $request The GET request to send. + * + * @return mixed Normally an array + */ + public function getRequest( Request $request ); + + /** + * @since 2.2 + * + * @param Request $request The POST request to send. + * + * @return mixed Normally an array + */ + public function postRequest( Request $request ); + +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/ApiUser.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/ApiUser.php new file mode 100644 index 00000000..b2f59153 --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/ApiUser.php @@ -0,0 +1,90 @@ +<?php + +namespace Mediawiki\Api; + +use InvalidArgumentException; + +/** + * @since 0.1 + * + * @author Addshore + * @author RobinR1 + * @author Bene + * + * Represents a user that can log in to the api + */ +class ApiUser { + + /** + * @var string + */ + private $password; + + /** + * @var string + */ + private $username; + + /** + * @var string + */ + private $domain; + + /** + * @param string $username The username. + * @param string $password The user's password. + * @param string|null $domain The domain (for authentication systems that support domains). + * + * @throws \InvalidArgumentException + */ + public function __construct( $username, $password, $domain = null ) { + $domainIsStringOrNull = ( is_string( $domain ) || is_null( $domain ) ); + if ( !is_string( $username ) || !is_string( $password ) || !$domainIsStringOrNull ) { + throw new InvalidArgumentException( 'Username, Password and Domain must all be strings' ); + } + if ( empty( $username ) || empty( $password ) ) { + throw new InvalidArgumentException( 'Username and Password are not allowed to be empty' ); + } + $this->username = $username; + $this->password = $password; + $this->domain = $domain; + } + + /** + * @since 0.1 + * @return string + */ + public function getUsername() { + return $this->username; + } + + /** + * @since 0.1 + * @return string + */ + public function getPassword() { + return $this->password; + } + + /** + * @since 0.1 + * @return string + */ + public function getDomain() { + return $this->domain; + } + + /** + * @since 0.1 + * @param mixed $other Another ApiUser object to compare with. + * + * @return bool + */ + public function equals( $other ) { + return $other instanceof self + && $this->username == $other->getUsername() + && $this->password == $other->getPassword() + && $this->domain == $other->getDomain(); + } + +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/AsyncApiRequester.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/AsyncApiRequester.php new file mode 100644 index 00000000..6190ac7e --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/AsyncApiRequester.php @@ -0,0 +1,37 @@ +<?php + +namespace Mediawiki\Api; + +use GuzzleHttp\Promise\PromiseInterface; + +/** + * @since 2.2 + * @licence GNU GPL v2+ + * @author Addshore + * @author Jeroen De Dauw < jeroendedauw@gmail.com > + */ +interface AsyncApiRequester { + + /** + * @since 2.2 + * + * @param Request $request The GET request to send. + * + * @return PromiseInterface + * Normally promising an array, though can be mixed (json_decode result) + * Can throw UsageExceptions or RejectionExceptions + */ + public function getRequestAsync( Request $request ); + + /** + * @since 2.2 + * + * @param Request $request The POST request to send. + * + * @return PromiseInterface + * Normally promising an array, though can be mixed (json_decode result) + * Can throw UsageExceptions or RejectionExceptions + */ + public function postRequestAsync( Request $request ); + +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/FluentRequest.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/FluentRequest.php new file mode 100644 index 00000000..0d10553b --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/FluentRequest.php @@ -0,0 +1,118 @@ +<?php + +namespace Mediawiki\Api; + +/** + * @since 1.0 + * + * @author Addshore + */ +class FluentRequest implements Request { + + /** + * @var array + */ + private $params = []; + + /** + * @var array + */ + private $headers = []; + + /** + * @since 1.0 + * + * @return array + */ + public function getParams() { + return $this->params; + } + + /** + * @since 1.0 + * + * @return array + */ + public function getHeaders() { + return $this->headers; + } + + /** + * @since 1.0 + * + * @return static + */ + public static function factory() { + return new static(); + } + + /** + * @since 1.0 + * + * @param string $action The action name. + * + * @return $this + */ + public function setAction( $action ) { + $this->setParam( 'action', $action ); + return $this; + } + + /** + * Totally overwrite any previously set params + * + * @since 1.0 + * + * @param array $params New parameters. + * + * @return $this + */ + public function setParams( array $params ) { + $this->params = $params; + return $this; + } + + /** + * Totally overwrite any previously set params + * + * @since 1.0 + * + * @param array $params Additional parameters. + * + * @return $this + */ + public function addParams( array $params ) { + $this->params = array_merge( $this->params, $params ); + return $this; + } + + /** + * Set a single parameter. + * + * @since 1.0 + * + * @param string $param The parameter name. + * @param string $value The parameter value. + * + * @return $this + */ + public function setParam( $param, $value ) { + $this->params[$param] = $value; + return $this; + } + + /** + * Totally overwrite any previously set HTTP headers. + * + * @since 1.0 + * + * @param array $headers New headers. + * + * @return $this + */ + public function setHeaders( $headers ) { + $this->headers = $headers; + return $this; + } + +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/Guzzle/ClientFactory.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/Guzzle/ClientFactory.php new file mode 100644 index 00000000..704a1660 --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/Guzzle/ClientFactory.php @@ -0,0 +1,96 @@ +<?php + +namespace Mediawiki\Api\Guzzle; + +use GuzzleHttp\Client; +use GuzzleHttp\Handler\CurlHandler; +use GuzzleHttp\HandlerStack; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; + +/** + * @since 2.1 + * + * @author Addshore + */ +class ClientFactory implements LoggerAwareInterface { + + private $client; + private $logger; + private $config; + + /** + * @since 2.1 + * + * @param array $config All configuration settings supported by Guzzle, and these: + * middleware => array of extra middleware to pass to guzzle + * user-agent => string default user agent to use for requests + */ + public function __construct( array $config = [] ) { + $this->logger = new NullLogger(); + $this->config = $config; + } + + /** + * @since 2.1 + * + * @return Client + */ + public function getClient() { + if ( $this->client === null ) { + $this->client = $this->newClient(); + } + return $this->client; + } + + /** + * @return Client + */ + private function newClient() { + $this->config += [ + 'cookies' => true, + 'headers' => [], + 'middleware' => [], + ]; + + if ( !array_key_exists( 'User-Agent', $this->config['headers'] ) ) { + if ( array_key_exists( 'user-agent', $this->config ) ) { + $this->config['headers']['User-Agent'] = $this->config['user-agent']; + } else { + $this->config['headers']['User-Agent'] = 'Addwiki - mediawiki-api-base'; + } + } + unset( $this->config['user-agent'] ); + + if ( !array_key_exists( 'handler', $this->config ) ) { + $this->config['handler'] = HandlerStack::create( new CurlHandler() ); + } + + $middlewareFactory = new MiddlewareFactory(); + $middlewareFactory->setLogger( $this->logger ); + + $this->config['middleware'][] = $middlewareFactory->retry(); + + foreach ( $this->config['middleware'] as $name => $middleware ) { + $this->config['handler']->push( $middleware ); + } + unset( $this->config['middleware'] ); + + return new Client( $this->config ); + } + + /** + * Sets a logger instance on the object + * + * @since 2.1 + * + * @param LoggerInterface $logger The new Logger object. + * + * @return null + */ + public function setLogger( LoggerInterface $logger ) { + $this->logger = $logger; + } + +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/Guzzle/MiddlewareFactory.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/Guzzle/MiddlewareFactory.php new file mode 100644 index 00000000..9e03c02f --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/Guzzle/MiddlewareFactory.php @@ -0,0 +1,158 @@ +<?php + +namespace Mediawiki\Api\Guzzle; + +use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Middleware; +use GuzzleHttp\Psr7\Request; +use GuzzleHttp\Psr7\Response; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; + +/** + * @access private + * + * @author Addshore + */ +class MiddlewareFactory implements LoggerAwareInterface { + + /** + * @var LoggerInterface + */ + private $logger; + + public function __construct() { + $this->logger = new NullLogger(); + } + + /** + * @param LoggerInterface $logger The new Logger object. + */ + public function setLogger( LoggerInterface $logger ) { + $this->logger = $logger; + } + + /** + * @access private + * + * @param bool $delay default to true, can be false to speed up tests + * + * @return callable + */ + public function retry( $delay = true ) { + if ( $delay ) { + return Middleware::retry( $this->newRetryDecider(), $this->getRetryDelay() ); + } else { + return Middleware::retry( $this->newRetryDecider() ); + } + } + + /** + * Returns a method that takes the number of retries and returns the number of miliseconds + * to wait + * + * @return callable + */ + private function getRetryDelay() { + return function ( $numberOfRetries, Response $response = null ) { + // The $response argument is only passed as of Guzzle 6.2.2. + if ( $response !== null ) { + // Retry-After may be a number of seconds or an absolute date (RFC 7231, + // section 7.1.3). + $retryAfter = $response->getHeaderLine( 'Retry-After' ); + + if ( is_numeric( $retryAfter ) ) { + return 1000 * $retryAfter; + } + + if ( $retryAfter ) { + $seconds = strtotime( $retryAfter ) - time(); + return 1000 * max( 1, $seconds ); + } + } + + return 1000 * $numberOfRetries; + }; + } + + /** + * @return callable + */ + private function newRetryDecider() { + return function ( + $retries, + Request $request, + Response $response = null, + RequestException $exception = null + ) { + // Don't retry if we have run out of retries + if ( $retries >= 5 ) { + return false; + } + + $shouldRetry = false; + + // Retry connection exceptions + if ( $exception instanceof ConnectException ) { + $shouldRetry = true; + } + + if ( $response ) { + $data = json_decode( $response->getBody(), true ); + + // Retry on server errors + if ( $response->getStatusCode() >= 500 ) { + $shouldRetry = true; + } + + foreach ( $response->getHeader( 'Mediawiki-Api-Error' ) as $mediawikiApiErrorHeader ) { + if ( + // Retry if the API explicitly tells us to: + // https://www.mediawiki.org/wiki/Manual:Maxlag_parameter + $response->getHeaderLine( 'Retry-After' ) + || + // Retry if we have a response with an API error worth retrying + in_array( + $mediawikiApiErrorHeader, + [ + 'ratelimited', + 'maxlag', + 'readonly', + 'internal_api_error_DBQueryError', + ] + ) + || + // Or if we have been stopped from saving as an 'anti-abuse measure' + // Note: this tries to match "actionthrottledtext" i18n messagae for mediawiki + ( + $mediawikiApiErrorHeader == 'failed-save' && + strstr( $data['error']['info'], 'anti-abuse measure' ) + ) + ) { + $shouldRetry = true; + } + + } + } + + // Log if we are retrying + if ( $shouldRetry ) { + $this->logger->warning( + sprintf( + 'Retrying %s %s %s/5, %s', + $request->getMethod(), + $request->getUri(), + $retries + 1, + $response ? 'status code: ' . $response->getStatusCode() : + $exception->getMessage() + ) + ); + } + + return $shouldRetry; + }; + } + +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/MediawikiApi.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/MediawikiApi.php new file mode 100644 index 00000000..0c6c4fe3 --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/MediawikiApi.php @@ -0,0 +1,507 @@ +<?php + +namespace Mediawiki\Api; + +use DOMDocument; +use DOMXPath; +use GuzzleHttp\Client; +use GuzzleHttp\ClientInterface; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Promise\PromiseInterface; +use InvalidArgumentException; +use Mediawiki\Api\Guzzle\ClientFactory; +use Psr\Http\Message\ResponseInterface; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use Psr\Log\NullLogger; +use SimpleXMLElement; + +/** + * Main class for this library + * + * @since 0.1 + * + * @author Addshore + */ +class MediawikiApi implements MediawikiApiInterface, LoggerAwareInterface { + + /** + * @var ClientInterface|null Should be accessed through getClient + */ + private $client = null; + + /** + * @var bool|string + */ + private $isLoggedIn; + + /** + * @var MediawikiSession + */ + private $session; + + /** + * @var string + */ + private $version; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var string + */ + private $apiUrl; + + /** + * @since 2.0 + * + * @param string $apiEndpoint e.g. https://en.wikipedia.org/w/api.php + * + * @return self returns a MediawikiApi instance using $apiEndpoint + */ + public static function newFromApiEndpoint( $apiEndpoint ) { + return new self( $apiEndpoint ); + } + + /** + * Create a new MediawikiApi object from a URL to any page in a MediaWiki website. + * + * @since 2.0 + * @see https://en.wikipedia.org/wiki/Really_Simple_Discovery + * + * @param string $url e.g. https://en.wikipedia.org OR https://de.wikipedia.org/wiki/Berlin + * @return self returns a MediawikiApi instance using the apiEndpoint provided by the RSD + * file accessible on all Mediawiki pages + * @throws RsdException If the RSD URL could not be found in the page's HTML. + */ + public static function newFromPage( $url ) { + // Set up HTTP client and HTML document. + $tempClient = new Client( [ 'headers' => [ 'User-Agent' => 'addwiki-mediawiki-client' ] ] ); + $pageHtml = $tempClient->get( $url )->getBody(); + $pageDoc = new DOMDocument(); + + // Try to load the HTML (turn off errors temporarily; most don't matter, and if they do get + // in the way of finding the API URL, will be reported in the RsdException below). + $internalErrors = libxml_use_internal_errors( true ); + $pageDoc->loadHTML( $pageHtml ); + $libXmlErrors = libxml_get_errors(); + libxml_use_internal_errors( $internalErrors ); + + // Extract the RSD link. + $xpath = 'head/link[@type="application/rsd+xml"][@href]'; + $link = ( new DOMXpath( $pageDoc ) )->query( $xpath ); + if ( $link->length === 0 ) { + // Format libxml errors for display. + $libXmlErrorStr = array_reduce( $libXmlErrors, function ( $prevErr, $err ) { + return $prevErr . ', ' . $err->message . ' (line '.$err->line . ')'; + } ); + if ( $libXmlErrorStr ) { + $libXmlErrorStr = "In addition, libxml had the following errors: $libXmlErrorStr"; + } + throw new RsdException( "Unable to find RSD URL in page: $url $libXmlErrorStr" ); + } + $rsdUrl = $link->item( 0 )->attributes->getnamedItem( 'href' )->nodeValue; + + // Then get the RSD XML, and return the API link. + $rsdXml = new SimpleXMLElement( $tempClient->get( $rsdUrl )->getBody() ); + return self::newFromApiEndpoint( (string)$rsdXml->service->apis->api->attributes()->apiLink ); + } + + /** + * @param string $apiUrl The API Url + * @param ClientInterface|null $client Guzzle Client + * @param MediawikiSession|null $session Inject a custom session here + */ + public function __construct( $apiUrl, ClientInterface $client = null, + MediawikiSession $session = null ) { + if ( !is_string( $apiUrl ) ) { + throw new InvalidArgumentException( '$apiUrl must be a string' ); + } + if ( $session === null ) { + $session = new MediawikiSession( $this ); + } + + $this->apiUrl = $apiUrl; + $this->client = $client; + $this->session = $session; + + $this->logger = new NullLogger(); + } + + /** + * Get the API URL (the URL to which API requests are sent, usually ending in api.php). + * This is useful if you've created this object via MediawikiApi::newFromPage(). + * + * @since 2.3 + * + * @return string The API URL. + */ + public function getApiUrl() { + return $this->apiUrl; + } + + /** + * @return ClientInterface + */ + private function getClient() { + if ( $this->client === null ) { + $clientFactory = new ClientFactory(); + $clientFactory->setLogger( $this->logger ); + $this->client = $clientFactory->getClient(); + } + return $this->client; + } + + /** + * Sets a logger instance on the object + * + * @since 1.1 + * + * @param LoggerInterface $logger The new Logger object. + * + * @return null + */ + public function setLogger( LoggerInterface $logger ) { + $this->logger = $logger; + $this->session->setLogger( $logger ); + } + + /** + * @since 2.0 + * + * @param Request $request The GET request to send. + * + * @return PromiseInterface + * Normally promising an array, though can be mixed (json_decode result) + * Can throw UsageExceptions or RejectionExceptions + */ + public function getRequestAsync( Request $request ) { + $promise = $this->getClient()->requestAsync( + 'GET', + $this->apiUrl, + $this->getClientRequestOptions( $request, 'query' ) + ); + + return $promise->then( function ( ResponseInterface $response ) { + return call_user_func( [ $this, 'decodeResponse' ], $response ); + } ); + } + + /** + * @since 2.0 + * + * @param Request $request The POST request to send. + * + * @return PromiseInterface + * Normally promising an array, though can be mixed (json_decode result) + * Can throw UsageExceptions or RejectionExceptions + */ + public function postRequestAsync( Request $request ) { + $promise = $this->getClient()->requestAsync( + 'POST', + $this->apiUrl, + $this->getClientRequestOptions( $request, $this->getPostRequestEncoding( $request ) ) + ); + + return $promise->then( function ( ResponseInterface $response ) { + return call_user_func( [ $this, 'decodeResponse' ], $response ); + } ); + } + + /** + * @since 0.2 + * + * @param Request $request The GET request to send. + * + * @return mixed Normally an array + */ + public function getRequest( Request $request ) { + $response = $this->getClient()->request( + 'GET', + $this->apiUrl, + $this->getClientRequestOptions( $request, 'query' ) + ); + + return $this->decodeResponse( $response ); + } + + /** + * @since 0.2 + * + * @param Request $request The POST request to send. + * + * @return mixed Normally an array + */ + public function postRequest( Request $request ) { + $response = $this->getClient()->request( + 'POST', + $this->apiUrl, + $this->getClientRequestOptions( $request, $this->getPostRequestEncoding( $request ) ) + ); + + return $this->decodeResponse( $response ); + } + + /** + * @param ResponseInterface $response + * + * @return mixed + * @throws UsageException + */ + private function decodeResponse( ResponseInterface $response ) { + $resultArray = json_decode( $response->getBody(), true ); + + $this->logWarnings( $resultArray ); + $this->throwUsageExceptions( $resultArray ); + + return $resultArray; + } + + /** + * @param Request $request + * + * @return string + */ + private function getPostRequestEncoding( Request $request ) { + if ( $request instanceof MultipartRequest ) { + return 'multipart'; + } + foreach ( $request->getParams() as $value ) { + if ( is_resource( $value ) ) { + return 'multipart'; + } + } + return 'form_params'; + } + + /** + * @param Request $request + * @param string $paramsKey either 'query' or 'multipart' + * + * @throws RequestException + * + * @return array as needed by ClientInterface::get and ClientInterface::post + */ + private function getClientRequestOptions( Request $request, $paramsKey ) { + $params = array_merge( $request->getParams(), [ 'format' => 'json' ] ); + if ( $paramsKey === 'multipart' ) { + $params = $this->encodeMultipartParams( $request, $params ); + } + + return [ + $paramsKey => $params, + 'headers' => array_merge( $this->getDefaultHeaders(), $request->getHeaders() ), + ]; + } + + /** + * Turn the normal key-value array of request parameters into a multipart array where each + * parameter is a new array with a 'name' and 'contents' elements (and optionally more, if the + * request is a MultipartRequest). + * + * @param Request $request The request to which the parameters belong. + * @param string[] $params The existing parameters. Not the same as $request->getParams(). + * + * @return array + */ + private function encodeMultipartParams( Request $request, $params ) { + // See if there are any multipart parameters in this request. + $multipartParams = ( $request instanceof MultipartRequest ) + ? $request->getMultipartParams() + : []; + return array_map( + function ( $name, $value ) use ( $multipartParams ) { + $partParams = [ + 'name' => $name, + 'contents' => $value, + ]; + if ( isset( $multipartParams[ $name ] ) ) { + // If extra parameters have been set for this part, use them. + $partParams = array_merge( $multipartParams[ $name ], $partParams ); + } + return $partParams; + }, + array_keys( $params ), + $params + ); + } + + /** + * @return array + */ + private function getDefaultHeaders() { + return [ + 'User-Agent' => $this->getUserAgent(), + ]; + } + + private function getUserAgent() { + $loggedIn = $this->isLoggedin(); + if ( $loggedIn ) { + return 'addwiki-mediawiki-client/' . $loggedIn; + } + return 'addwiki-mediawiki-client'; + } + + /** + * @param $result + */ + private function logWarnings( $result ) { + if ( is_array( $result ) && array_key_exists( 'warnings', $result ) ) { + foreach ( $result['warnings'] as $module => $warningData ) { + // Accomodate both formatversion=2 and old-style API results + $logPrefix = $module . ': '; + if ( isset( $warningData['*'] ) ) { + $this->logger->warning( $logPrefix . $warningData['*'], [ 'data' => $warningData ] ); + } else { + $this->logger->warning( $logPrefix . $warningData['warnings'], [ 'data' => $warningData ] ); + } + } + } + } + + /** + * @param array $result + * + * @throws UsageException + */ + private function throwUsageExceptions( $result ) { + if ( is_array( $result ) && array_key_exists( 'error', $result ) ) { + throw new UsageException( + $result['error']['code'], + $result['error']['info'], + $result + ); + } + } + + /** + * @since 0.1 + * + * @return bool|string false or the name of the current user + */ + public function isLoggedin() { + return $this->isLoggedIn; + } + + /** + * @since 0.1 + * + * @param ApiUser $apiUser The ApiUser to log in as. + * + * @throws UsageException + * @return bool success + */ + public function login( ApiUser $apiUser ) { + $this->logger->log( LogLevel::DEBUG, 'Logging in' ); + $credentials = $this->getLoginParams( $apiUser ); + $result = $this->postRequest( new SimpleRequest( 'login', $credentials ) ); + if ( $result['login']['result'] == "NeedToken" ) { + $params = array_merge( [ 'lgtoken' => $result['login']['token'] ], $credentials ); + $result = $this->postRequest( new SimpleRequest( 'login', $params ) ); + } + if ( $result['login']['result'] == "Success" ) { + $this->isLoggedIn = $apiUser->getUsername(); + return true; + } + + $this->isLoggedIn = false; + $this->logger->log( LogLevel::DEBUG, 'Login failed.', $result ); + $this->throwLoginUsageException( $result ); + return false; + } + + /** + * @param ApiUser $apiUser + * + * @return string[] + */ + private function getLoginParams( ApiUser $apiUser ) { + $params = [ + 'lgname' => $apiUser->getUsername(), + 'lgpassword' => $apiUser->getPassword(), + ]; + + if ( !is_null( $apiUser->getDomain() ) ) { + $params['lgdomain'] = $apiUser->getDomain(); + } + return $params; + } + + /** + * @param array $result + * + * @throws UsageException + */ + private function throwLoginUsageException( $result ) { + $loginResult = $result['login']['result']; + + throw new UsageException( + 'login-' . $loginResult, + array_key_exists( 'reason', $result['login'] ) + ? $result['login']['reason'] + : 'No Reason given', + $result + ); + } + + /** + * @since 0.1 + * + * @return bool success + */ + public function logout() { + $this->logger->log( LogLevel::DEBUG, 'Logging out' ); + $result = $this->postRequest( new SimpleRequest( 'logout' ) ); + if ( $result === [] ) { + $this->isLoggedIn = false; + $this->clearTokens(); + return true; + } + return false; + } + + /** + * @since 0.1 + * + * @param string $type The token type to get. + * + * @return string + */ + public function getToken( $type = 'csrf' ) { + return $this->session->getToken( $type ); + } + + /** + * Clear all tokens stored by the API. + * + * @since 0.1 + */ + public function clearTokens() { + $this->session->clearTokens(); + } + + /** + * @return string + */ + public function getVersion() { + if ( !isset( $this->version ) ) { + $result = $this->getRequest( new SimpleRequest( 'query', [ + 'meta' => 'siteinfo', + 'continue' => '', + ] ) ); + preg_match( + '/\d+(?:\.\d+)+/', + $result['query']['general']['generator'], + $versionParts + ); + $this->version = $versionParts[0]; + } + return $this->version; + } + +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/MediawikiApiInterface.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/MediawikiApiInterface.php new file mode 100644 index 00000000..83580676 --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/MediawikiApiInterface.php @@ -0,0 +1,60 @@ +<?php + +namespace Mediawiki\Api; + +/** + * @since 2.2 + * @licence GNU GPL v2+ + * @author Addshore + * @author Jeroen De Dauw < jeroendedauw@gmail.com > + */ +interface MediawikiApiInterface extends ApiRequester, AsyncApiRequester { + + /** + * @since 2.2 + * + * @return bool|string false or the name of the current user + */ + public function isLoggedin(); + + /** + * @since 2.2 + * + * @param ApiUser $apiUser The ApiUser to log in as. + * + * @throws UsageException + * @return bool success + */ + public function login( ApiUser $apiUser ); + + /** + * @since 2.2 + * + * @return bool success + */ + public function logout(); + + /** + * @since 2.2 + * + * @param string $type The type of token to get. + * + * @return string + */ + public function getToken( $type = 'csrf' ); + + /** + * @since 2.2 + * + * Clears all tokens stored by the api + */ + public function clearTokens(); + + /** + * @since 2.2 + * + * @return string + */ + public function getVersion(); + +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/MediawikiSession.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/MediawikiSession.php new file mode 100644 index 00000000..c430695c --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/MediawikiSession.php @@ -0,0 +1,168 @@ +<?php + +namespace Mediawiki\Api; + +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use Psr\Log\NullLogger; + +/** + * @since 0.1 + * + * @author Addshore + */ +class MediawikiSession implements LoggerAwareInterface { + + /** + * @var array + */ + private $tokens = []; + + /** + * @var MediawikiApi + */ + private $api; + + /** + * @var bool if this session is running against mediawiki version pre 1.25 + */ + private $usePre125TokensModule = false; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param MediawikiApi $api The API object to use for this session. + */ + public function __construct( MediawikiApi $api ) { + $this->api = $api; + $this->logger = new NullLogger(); + } + + /** + * Sets a logger instance on the object + * + * @since 1.1 + * + * @param LoggerInterface $logger The new Logger object. + * + * @return null + */ + public function setLogger( LoggerInterface $logger ) { + $this->logger = $logger; + } + + /** + * Tries to get the specified token from the API + * + * @since 0.1 + * + * @param string $type The type of token to get. + * + * @return string + */ + public function getToken( $type = 'csrf' ) { + // If we don't already have the token that we want + if ( !array_key_exists( $type, $this->tokens ) ) { + $this->logger->log( LogLevel::DEBUG, 'Getting fresh token', [ 'type' => $type ] ); + + // If we know that we don't have the new module mw<1.25 + if ( $this->usePre125TokensModule ) { + return $this->reallyGetPre125Token( $type ); + } else { + return $this->reallyGetToken( $type ); + } + + } + + return $this->tokens[$type]; + } + + private function reallyGetPre125Token( $type ) { + // Suppress deprecation warning + $result = @$this->api->postRequest( // @codingStandardsIgnoreLine + new SimpleRequest( 'tokens', [ 'type' => $this->getOldTokenType( $type ) ] ) + ); + $this->tokens[$type] = array_pop( $result['tokens'] ); + + return $this->tokens[$type]; + } + + private function reallyGetToken( $type ) { + // We suppress errors on this call so the user doesn't get get a warning that isn't their fault. + $result = @$this->api->postRequest( // @codingStandardsIgnoreLine + new SimpleRequest( 'query', [ + 'meta' => 'tokens', + 'type' => $this->getNewTokenType( $type ), + 'continue' => '', + ] ) + ); + // If mw<1.25 (no new module) + $metaWarning = "Unrecognized value for parameter 'meta': tokens"; + if ( isset( $result['warnings']['query']['*'] ) + && false !== strpos( $result['warnings']['query']['*'], $metaWarning ) ) { + $this->usePre125TokensModule = true; + $this->logger->log( LogLevel::DEBUG, 'Falling back to pre 1.25 token system' ); + $this->tokens[$type] = $this->reallyGetPre125Token( $type ); + } else { + $this->tokens[$type] = array_pop( $result['query']['tokens'] ); + } + + return $this->tokens[$type]; + } + + /** + * Tries to guess a new token type from an old token type + * + * @param string $type + * + * @return string + */ + private function getNewTokenType( $type ) { + switch ( $type ) { + case 'edit': + case 'delete': + case 'protect': + case 'move': + case 'block': + case 'unblock': + case 'email': + case 'import': + case 'options': + return 'csrf'; + } + // Return the same type, don't know what to do with this.. + return $type; + } + + /** + * Tries to guess an old token type from a new token type + * + * @param $type + * + * @return string + */ + private function getOldTokenType( $type ) { + switch ( $type ) { + // Guess that we want an edit token, this may not always work as we might be trying to + // use it for something else... + case 'csrf': + return 'edit'; + } + return $type; + } + + /** + * Clears all tokens stored by the api + * + * @since 0.2 + */ + public function clearTokens() { + $this->logger->log( LogLevel::DEBUG, 'Clearing session tokens', [ 'tokens' => $this->tokens ] ); + $this->tokens = []; + } + +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/MultipartRequest.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/MultipartRequest.php new file mode 100644 index 00000000..8d3fbdf4 --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/MultipartRequest.php @@ -0,0 +1,77 @@ +<?php + +namespace Mediawiki\Api; + +use Exception; + +/** + * A MultipartRequest is the same as a FluentRequest with additional support for setting request + * parameters (both normal parameters and headers) on multipart requests. + * + * @link http://docs.guzzlephp.org/en/stable/request-options.html#multipart + * + * @since 2.4.0 + */ +class MultipartRequest extends FluentRequest { + + /** @var mixed[] */ + protected $multipartParams = []; + + /** + * Check the structure of a multipart parameter array. + * + * @param mixed[] $params The multipart parameters to check. + * + * @throws Exception + */ + protected function checkMultipartParams( $params ) { + foreach ( $params as $key => $val ) { + if ( !is_array( $val ) ) { + throw new Exception( "Parameter '$key' must be an array." ); + } + if ( !in_array( $key, array_keys( $this->getParams() ) ) ) { + throw new Exception( "Parameter '$key' is not already set on this request." ); + } + } + } + + /** + * Set all multipart parameters, replacing all existing ones. + * + * Each key of the array passed in here must be the name of a parameter already set on this + * request object. + * + * @param mixed[] $params The multipart parameters to use. + * @return $this + */ + public function setMultipartParams( $params ) { + $this->checkMultipartParams( $params ); + $this->multipartParams = $params; + return $this; + } + + /** + * Add extra multipart parameters. + * + * Each key of the array passed in here must be the name of a parameter already set on this + * request object. + * + * @param mixed[] $params The multipart parameters to add to any already present. + * + * @return $this + */ + public function addMultipartParams( $params ) { + $this->checkMultipartParams( $params ); + $this->multipartParams = array_merge( $this->multipartParams, $params ); + return $this; + } + + /** + * Get all multipart request parameters. + * + * @return mixed[] + */ + public function getMultipartParams() { + return $this->multipartParams; + } +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/Request.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/Request.php new file mode 100644 index 00000000..0daace2a --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/Request.php @@ -0,0 +1,30 @@ +<?php + +namespace Mediawiki\Api; + +/** + * @since 0.2 + * + * @author Addshore + */ +interface Request { + + /** + * @since 0.2 + * + * @return array + */ + public function getParams(); + + /** + * Associative array of headers to add to the request. + * Each key is the name of a header, and each value is a string or array of strings representing + * the header field values. + * + * @since 0.3 + * + * @return array + */ + public function getHeaders(); + +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/RsdException.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/RsdException.php new file mode 100644 index 00000000..b304f574 --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/RsdException.php @@ -0,0 +1,12 @@ +<?php + +namespace Mediawiki\Api; + +use Exception; + +/** + * An exception raised when an issue is encountered with Really Simple Discovery. + * @see https://en.wikipedia.org/wiki/Really_Simple_Discovery + */ +class RsdException extends Exception { +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/SimpleRequest.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/SimpleRequest.php new file mode 100644 index 00000000..1e33348d --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/SimpleRequest.php @@ -0,0 +1,61 @@ +<?php + +namespace Mediawiki\Api; + +use InvalidArgumentException; + +/** + * Please consider using a FluentRequest object + * + * @since 0.2 + * + * @author Addshore + */ +class SimpleRequest implements Request { + + /** + * @var string + */ + private $action; + + /** + * @var array + */ + private $params; + + /** + * @var array + */ + private $headers; + + /** + * @param string $action The API action. + * @param array $params The parameters for the action. + * @param array $headers Any extra HTTP headers to send. + * + * @throws InvalidArgumentException + */ + public function __construct( $action, array $params = [], array $headers = [] ) { + if ( !is_string( $action ) ) { + throw new InvalidArgumentException( '$action must be string' ); + } + $this->action = $action; + $this->params = $params; + $this->headers = $headers; + } + + /** + * @return string[] + */ + public function getParams() { + return array_merge( [ 'action' => $this->action ], $this->params ); + } + + /** + * @return string[] + */ + public function getHeaders() { + return $this->headers; + } + +} diff --git a/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/UsageException.php b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/UsageException.php new file mode 100644 index 00000000..77148f1b --- /dev/null +++ b/bin/reevotech/vendor/addwiki/mediawiki-api-base/src/UsageException.php @@ -0,0 +1,75 @@ +<?php + +namespace Mediawiki\Api; + +use Exception; + +/** + * Class representing a Mediawiki Api UsageException + * + * @since 0.1 + * + * @author Addshore + */ +class UsageException extends Exception { + + /** + * @var string + */ + private $apiCode; + + /** + * @var array + */ + private $result; + + /** + * @var string + */ + private $rawMessage; + + /** + * @since 0.1 + * + * @param string $apiCode The API error code. + * @param string $message The API error message. + * @param array $result the result the exception was generated from + */ + public function __construct( $apiCode = '', $message = '', $result = [] ) { + $this->apiCode = $apiCode; + $this->result = $result; + $this->rawMessage = $message; + $message = 'Code: ' . $apiCode . PHP_EOL . + 'Message: ' . $message . PHP_EOL . + 'Result: ' . json_encode( $result ); + parent::__construct( $message, 0, null ); + } + + /** + * @since 0.1 + * + * @return string + */ + public function getApiCode() { + return $this->apiCode; + } + + /** + * @since 0.3 + * + * @return array + */ + public function getApiResult() { + return $this->result; + } + + /** + * @since 2.3.0 + * + * @return string + */ + public function getRawMessage() { + return $this->rawMessage; + } + +} |