|  |  |  | <?php
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * @link http://www.yiiframework.com/
 | 
					
						
							|  |  |  |  * @copyright Copyright (c) 2008 Yii Software LLC
 | 
					
						
							|  |  |  |  * @license http://www.yiiframework.com/license/
 | 
					
						
							|  |  |  |  */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace yii\authclient;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | use yii\base\Exception;
 | 
					
						
							|  |  |  | use Yii;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * OAuth1 serves as a client for the OAuth 1/1.0a flow.
 | 
					
						
							|  |  |  |  *
 | 
					
						
							|  |  |  |  * In oder to acquire access token perform following sequence:
 | 
					
						
							|  |  |  |  *
 | 
					
						
							|  |  |  |  * ~~~
 | 
					
						
							|  |  |  |  * use yii\authclient\OAuth1;
 | 
					
						
							|  |  |  |  *
 | 
					
						
							|  |  |  |  * $oauthClient = new OAuth1();
 | 
					
						
							|  |  |  |  * $requestToken = $oauthClient->fetchRequestToken(); // Get request token
 | 
					
						
							|  |  |  |  * $url = $oauthClient->buildAuthUrl($requestToken); // Get authorization URL
 | 
					
						
							|  |  |  |  * return Yii::$app->getResponse()->redirect($url); // Redirect to authorization URL
 | 
					
						
							|  |  |  |  * // After user returns at our site:
 | 
					
						
							|  |  |  |  * $accessToken = $oauthClient->fetchAccessToken($requestToken); // Upgrade to access token
 | 
					
						
							|  |  |  |  * ~~~
 | 
					
						
							|  |  |  |  *
 | 
					
						
							|  |  |  |  * @see http://oauth.net/
 | 
					
						
							|  |  |  |  *
 | 
					
						
							|  |  |  |  * @author Paul Klimov <klimov.paul@gmail.com>
 | 
					
						
							|  |  |  |  * @since 2.0
 | 
					
						
							|  |  |  |  */
 | 
					
						
							|  |  |  | class OAuth1 extends BaseOAuth
 | 
					
						
							|  |  |  | {
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * @var string protocol version.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public $version = '1.0';
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * @var string OAuth consumer key.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public $consumerKey;
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * @var string OAuth consumer secret.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public $consumerSecret;
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * @var string OAuth request token URL.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public $requestTokenUrl;
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * @var string request token HTTP method.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public $requestTokenMethod = 'GET';
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * @var string OAuth access token URL.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public $accessTokenUrl;
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * @var string access token HTTP method.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public $accessTokenMethod = 'GET';
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Fetches the OAuth request token.
 | 
					
						
							|  |  |  | 	 * @param array $params additional request params.
 | 
					
						
							|  |  |  | 	 * @return OAuthToken request token.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public function fetchRequestToken(array $params = [])
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$this->removeState('token');
 | 
					
						
							|  |  |  | 		$defaultParams = [
 | 
					
						
							|  |  |  | 			'oauth_consumer_key' => $this->consumerKey,
 | 
					
						
							|  |  |  | 			'oauth_callback' => $this->getReturnUrl(),
 | 
					
						
							|  |  |  | 			//'xoauth_displayname' => Yii::$app->name,
 | 
					
						
							|  |  |  | 		];
 | 
					
						
							|  |  |  | 		if (!empty($this->scope)) {
 | 
					
						
							|  |  |  | 			$defaultParams['scope'] = $this->scope;
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		$response = $this->sendSignedRequest($this->requestTokenMethod, $this->requestTokenUrl, array_merge($defaultParams, $params));
 | 
					
						
							|  |  |  | 		$token = $this->createToken([
 | 
					
						
							|  |  |  | 			'params' => $response
 | 
					
						
							|  |  |  | 		]);
 | 
					
						
							|  |  |  | 		$this->setState('requestToken', $token);
 | 
					
						
							|  |  |  | 		return $token;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Composes user authorization URL.
 | 
					
						
							|  |  |  | 	 * @param OAuthToken $requestToken OAuth request token.
 | 
					
						
							|  |  |  | 	 * @param array $params additional request params.
 | 
					
						
							|  |  |  | 	 * @return string authorize URL
 | 
					
						
							|  |  |  | 	 * @throws Exception on failure.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public function buildAuthUrl(OAuthToken $requestToken = null, array $params = [])
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		if (!is_object($requestToken)) {
 | 
					
						
							|  |  |  | 			$requestToken = $this->getState('requestToken');
 | 
					
						
							|  |  |  | 			if (!is_object($requestToken)) {
 | 
					
						
							|  |  |  | 				throw new Exception('Request token is required to build authorize URL!');
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		$params['oauth_token'] = $requestToken->getToken();
 | 
					
						
							|  |  |  | 		return $this->composeUrl($this->authUrl, $params);
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Fetches OAuth access token.
 | 
					
						
							|  |  |  | 	 * @param OAuthToken $requestToken OAuth request token.
 | 
					
						
							|  |  |  | 	 * @param string $oauthVerifier OAuth verifier.
 | 
					
						
							|  |  |  | 	 * @param array $params additional request params.
 | 
					
						
							|  |  |  | 	 * @return OAuthToken OAuth access token.
 | 
					
						
							|  |  |  | 	 * @throws Exception on failure.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public function fetchAccessToken(OAuthToken $requestToken = null, $oauthVerifier = null, array $params = [])
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		if (!is_object($requestToken)) {
 | 
					
						
							|  |  |  | 			$requestToken = $this->getState('requestToken');
 | 
					
						
							|  |  |  | 			if (!is_object($requestToken)) {
 | 
					
						
							|  |  |  | 				throw new Exception('Request token is required to fetch access token!');
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		$this->removeState('requestToken');
 | 
					
						
							|  |  |  | 		$defaultParams = [
 | 
					
						
							|  |  |  | 			'oauth_consumer_key' => $this->consumerKey,
 | 
					
						
							|  |  |  | 			'oauth_token' => $requestToken->getToken()
 | 
					
						
							|  |  |  | 		];
 | 
					
						
							|  |  |  | 		if ($oauthVerifier === null) {
 | 
					
						
							|  |  |  | 			if (isset($_REQUEST['oauth_verifier'])) {
 | 
					
						
							|  |  |  | 				$oauthVerifier = $_REQUEST['oauth_verifier'];
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if (!empty($oauthVerifier)) {
 | 
					
						
							|  |  |  | 			$defaultParams['oauth_verifier'] = $oauthVerifier;
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		$response = $this->sendSignedRequest($this->accessTokenMethod, $this->accessTokenUrl, array_merge($defaultParams, $params));
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		$token = $this->createToken([
 | 
					
						
							|  |  |  | 			'params' => $response
 | 
					
						
							|  |  |  | 		]);
 | 
					
						
							|  |  |  | 		$this->setAccessToken($token);
 | 
					
						
							|  |  |  | 		return $token;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Sends HTTP request, signed by {@link signatureMethod}.
 | 
					
						
							|  |  |  | 	 * @param string $method request type.
 | 
					
						
							|  |  |  | 	 * @param string $url request URL.
 | 
					
						
							|  |  |  | 	 * @param array $params request params.
 | 
					
						
							|  |  |  | 	 * @return array response.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	protected function sendSignedRequest($method, $url, array $params = [])
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$params = array_merge($params, $this->generateCommonRequestParams());
 | 
					
						
							|  |  |  | 		$params = $this->signRequest($method, $url, $params);
 | 
					
						
							|  |  |  | 		return $this->sendRequest($method, $url, $params);
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Composes HTTP request CUrl options, which will be merged with the default ones.
 | 
					
						
							|  |  |  | 	 * @param string $method request type.
 | 
					
						
							|  |  |  | 	 * @param string $url request URL.
 | 
					
						
							|  |  |  | 	 * @param array $params request params.
 | 
					
						
							|  |  |  | 	 * @return array CUrl options.
 | 
					
						
							|  |  |  | 	 * @throws Exception on failure.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	protected function composeRequestCurlOptions($method, $url, array $params)
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$curlOptions = [];
 | 
					
						
							|  |  |  | 		switch ($method) {
 | 
					
						
							|  |  |  | 			case 'GET': {
 | 
					
						
							|  |  |  | 				$curlOptions[CURLOPT_URL] = $this->composeUrl($url, $params);
 | 
					
						
							|  |  |  | 				break;
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			case 'POST': {
 | 
					
						
							|  |  |  | 				$curlOptions[CURLOPT_POST] = true;
 | 
					
						
							|  |  |  | 				if (!empty($params)){
 | 
					
						
							|  |  |  | 					$curlOptions[CURLOPT_POSTFIELDS] = $params;
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				$authorizationHeader = $this->composeAuthorizationHeader($params);
 | 
					
						
							|  |  |  | 				if (!empty($authorizationHeader)) {
 | 
					
						
							|  |  |  | 					$curlOptions[CURLOPT_HTTPHEADER] = ['Content-Type: application/atom+xml', $authorizationHeader];
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				break;
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			case 'HEAD':
 | 
					
						
							|  |  |  | 			case 'PUT':
 | 
					
						
							|  |  |  | 			case 'DELETE': {
 | 
					
						
							|  |  |  | 				$curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
 | 
					
						
							|  |  |  | 				if (!empty($params)) {
 | 
					
						
							|  |  |  | 					$curlOptions[CURLOPT_URL] = $this->composeUrl($url, $params);
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				break;
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			default: {
 | 
					
						
							|  |  |  | 				throw new Exception("Unknown request method '{$method}'.");
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		return $curlOptions;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Performs request to the OAuth API.
 | 
					
						
							|  |  |  | 	 * @param OAuthToken $accessToken actual access token.
 | 
					
						
							|  |  |  | 	 * @param string $url absolute API URL.
 | 
					
						
							|  |  |  | 	 * @param string $method request method.
 | 
					
						
							|  |  |  | 	 * @param array $params request parameters.
 | 
					
						
							|  |  |  | 	 * @return array API response.
 | 
					
						
							|  |  |  | 	 * @throws Exception on failure.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	protected function apiInternal($accessToken, $url, $method, array $params)
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$params['oauth_consumer_key'] = $this->consumerKey;
 | 
					
						
							|  |  |  | 		$params['oauth_token'] = $accessToken->getToken();
 | 
					
						
							|  |  |  | 		$response = $this->sendSignedRequest($method, $url, $params);
 | 
					
						
							|  |  |  | 		return $response;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Gets new auth token to replace expired one.
 | 
					
						
							|  |  |  | 	 * @param OAuthToken $token expired auth token.
 | 
					
						
							|  |  |  | 	 * @return OAuthToken new auth token.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public function refreshAccessToken(OAuthToken $token)
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		// @todo
 | 
					
						
							|  |  |  | 		return null;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Composes default {@link returnUrl} value.
 | 
					
						
							|  |  |  | 	 * @return string return URL.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	protected function defaultReturnUrl()
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$params = $_GET;
 | 
					
						
							|  |  |  | 		unset($params['oauth_token']);
 | 
					
						
							|  |  |  | 		return Yii::$app->getUrlManager()->createAbsoluteUrl(Yii::$app->controller->getRoute(), $params);
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Generates nonce value.
 | 
					
						
							|  |  |  | 	 * @return string nonce value.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	protected function generateNonce()
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		return md5(microtime() . mt_rand());
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Generates timestamp.
 | 
					
						
							|  |  |  | 	 * @return integer timestamp.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	protected function generateTimestamp()
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		return time();
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Generate common request params like version, timestamp etc.
 | 
					
						
							|  |  |  | 	 * @return array common request params.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	protected function generateCommonRequestParams()
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$params = [
 | 
					
						
							|  |  |  | 			'oauth_version' => $this->version,
 | 
					
						
							|  |  |  | 			'oauth_nonce' => $this->generateNonce(),
 | 
					
						
							|  |  |  | 			'oauth_timestamp' => $this->generateTimestamp(),
 | 
					
						
							|  |  |  | 		];
 | 
					
						
							|  |  |  | 		return $params;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Sign request with {@link signatureMethod}.
 | 
					
						
							|  |  |  | 	 * @param string $method request method.
 | 
					
						
							|  |  |  | 	 * @param string $url request URL.
 | 
					
						
							|  |  |  | 	 * @param array $params request params.
 | 
					
						
							|  |  |  | 	 * @return array signed request params.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	protected function signRequest($method, $url, array $params)
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$signatureMethod = $this->getSignatureMethod();
 | 
					
						
							|  |  |  | 		$params['oauth_signature_method'] = $signatureMethod->getName();
 | 
					
						
							|  |  |  | 		$signatureBaseString = $this->composeSignatureBaseString($method, $url, $params);
 | 
					
						
							|  |  |  | 		$signatureKey = $this->composeSignatureKey();
 | 
					
						
							|  |  |  | 		$params['oauth_signature'] = $signatureMethod->generateSignature($signatureBaseString, $signatureKey);
 | 
					
						
							|  |  |  | 		return $params;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Creates signature base string, which will be signed by {@link signatureMethod}.
 | 
					
						
							|  |  |  | 	 * @param string $method request method.
 | 
					
						
							|  |  |  | 	 * @param string $url request URL.
 | 
					
						
							|  |  |  | 	 * @param array $params request params.
 | 
					
						
							|  |  |  | 	 * @return string base signature string.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	protected function composeSignatureBaseString($method, $url, array $params)
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		unset($params['oauth_signature']);
 | 
					
						
							|  |  |  | 		uksort($params, 'strcmp'); // Parameters are sorted by name, using lexicographical byte value ordering. Ref: Spec: 9.1.1
 | 
					
						
							|  |  |  | 		$parts = [
 | 
					
						
							|  |  |  | 			strtoupper($method),
 | 
					
						
							|  |  |  | 			$url,
 | 
					
						
							|  |  |  | 			http_build_query($params, '', '&', PHP_QUERY_RFC3986)
 | 
					
						
							|  |  |  | 		];
 | 
					
						
							|  |  |  | 		$parts = array_map('rawurlencode', $parts);
 | 
					
						
							|  |  |  | 		return implode('&', $parts);
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Composes request signature key.
 | 
					
						
							|  |  |  | 	 * @return string signature key.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	protected function composeSignatureKey()
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$signatureKeyParts = [
 | 
					
						
							|  |  |  | 			$this->consumerSecret
 | 
					
						
							|  |  |  | 		];
 | 
					
						
							|  |  |  | 		$accessToken = $this->getAccessToken();
 | 
					
						
							|  |  |  | 		if (is_object($accessToken)) {
 | 
					
						
							|  |  |  | 			$signatureKeyParts[] = $accessToken->getTokenSecret();
 | 
					
						
							|  |  |  | 		} else {
 | 
					
						
							|  |  |  | 			$signatureKeyParts[] = '';
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		$signatureKeyParts = array_map('rawurlencode', $signatureKeyParts);
 | 
					
						
							|  |  |  | 		return implode('&', $signatureKeyParts);
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Composes authorization header content.
 | 
					
						
							|  |  |  | 	 * @param array $params request params.
 | 
					
						
							|  |  |  | 	 * @param string $realm authorization realm.
 | 
					
						
							|  |  |  | 	 * @return string authorization header content.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	protected function composeAuthorizationHeader(array $params, $realm = '')
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$header = 'Authorization: OAuth';
 | 
					
						
							|  |  |  | 		$headerParams = [];
 | 
					
						
							|  |  |  | 		if (!empty($realm)) {
 | 
					
						
							|  |  |  | 			$headerParams[] = 'realm="' . rawurlencode($realm) . '"';
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		foreach ($params as $key => $value) {
 | 
					
						
							|  |  |  | 			if (substr($key, 0, 5) != 'oauth') {
 | 
					
						
							|  |  |  | 				continue;
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			$headerParams[] = rawurlencode($key) . '="' . rawurlencode($value) . '"';
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if (!empty($headerParams)) {
 | 
					
						
							|  |  |  | 			$header .= ' ' . implode(', ', $headerParams);
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		return $header;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 |