<?php

/**
 * Sign requests before performing the request.
 * 
 * @version $Id: OAuthRequestSigner.php 58 2009-02-23 01:47:23Z marcw@pobox.com $
 * @author Marc Worrell <marcw@pobox.com>
 * @date  Nov 16, 2007 4:02:49 PM
 * 
 * 
 * The MIT License
 * 
 * Copyright (c) 2007-2008 Mediamatic Lab
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */


require_once dirname(__FILE__) . '/OAuthStore.php';
require_once dirname(__FILE__) . '/OAuthRequest.php';


class OAuthRequestSigner extends OAuthRequest
{
	protected $request;
	protected $store;
	protected $usr_id = 0;
	private   $signed = false;

	
	/**
	 * Construct the request to be signed.  Parses or appends the parameters in the params url.
	 * When you supply an params array, then the params should not be urlencoded.
	 * When you supply a string, then it is assumed it is of the type application/x-www-form-urlencoded
	 * 
	 * @param string request	url
	 * @param string method		PUT, GET, POST etc.
	 * @param mixed params 		string (for urlencoded data, or array with name/value pairs)
	 * @param string body		optional body for PUT and/or POST requests
	 */
	function __construct ( $request, $method = 'GET', $params = null, $body = null )
	{
		$this->store = elggconnect_get_oauth_store();//OAuthStore::instance();
		
		if (is_string($params))
		{
			parent::__construct($request, $method, $params);
		}
		else
		{
			parent::__construct($request, $method);
			if (is_array($params))
			{
				foreach ($params as $name => $value)
				{
					$this->setParam($name, $value);
				}
			}
		}
		
		// With put/ post we might have a body (not for application/x-www-form-urlencoded requests)
		if ($method == 'PUT' || $method == 'POST')
		{
			$this->setBody($body);
		}
	}


	/**
	 * Reset the 'signed' flag, so that any changes in the parameters force a recalculation
	 * of the signature.
	 */
	function setUnsigned ()
	{
		$this->signed = false;
	}


	/**
	 * Sign our message in the way the server understands.
	 * Set the needed oauth_xxxx parameters.
	 * 
	 * @param int usr_id		(optional) user that wants to sign this request
	 * @param array secrets		secrets used for signing, when empty then secrets will be fetched from the token registry
	 * @param string name		name of the token to be used for signing
	 * @exception OAuthException when there is no oauth relation with the server
	 * @exception OAuthException when we don't support the signing methods of the server
	 */	
	function sign ( $usr_id = 0, $secrets = null, $name = '' )
	{
		$url = $this->getRequestUrl();
		if (empty($secrets))
		{
			// get the access tokens for the site (on an user by user basis)
			$secrets = $this->store->getSecretsForSignature($url, $usr_id, $name);
		}
		if (empty($secrets))
		{
			throw new OAuthException('No OAuth relation with the server for at "'.$url.'"');
		}

		$signature_method = $this->selectSignatureMethod($secrets['signature_methods']);

		$token		  = isset($secrets['token'])        ? $secrets['token']        : '';
		$token_secret = isset($secrets['token_secret']) ? $secrets['token_secret'] : '';

		$this->setParam('oauth_signature_method',$signature_method);
		$this->setParam('oauth_signature',		 '');
		$this->setParam('oauth_nonce', 			 !empty($secrets['nonce'])     ? $secrets['nonce']     : uniqid(''));
		$this->setParam('oauth_timestamp', 		 !empty($secrets['timestamp']) ? $secrets['timestamp'] : time());
		$this->setParam('oauth_token', 		 	 $token);
		$this->setParam('oauth_consumer_key',	 $secrets['consumer_key']);
		$this->setParam('oauth_version',		 '1.0');
		
		$body = $this->getBody();
		if (!is_null($body))
		{
			// We also need to sign the body, use the default signature method
			$body_signature = $this->calculateDataSignature($body, $secrets['consumer_secret'], $token_secret, $signature_method);
			$this->setParam('xoauth_body_signature', $body_signature, true);
		}
		
		$signature = $this->calculateSignature($secrets['consumer_secret'], $token_secret);
		$this->setParam('oauth_signature',		 $signature, true);
		
		$this->signed = true;
		$this->usr_id = $usr_id;
	}


	/**
	 * Builds the Authorization header for the request.
	 * Adds all oauth_ and xoauth_ parameters to the Authorization header.
	 * 
	 * @return string
	 */
	function getAuthorizationHeader ()
	{
		if (!$this->signed)
		{
			$this->sign($this->usr_id);
		}
		$h   = array();
		$h[] = 'Authorization: OAuth realm=""';
		foreach ($this->param as $name => $value)
		{
			if (strncmp($name, 'oauth_', 6) == 0 || strncmp($name, 'xoauth_', 7) == 0)
          	{
				$h[] = $name.'="'.$value.'"';
			}
		}
		$hs = implode(', ', $h);
		return $hs;
	}


	/**
	 * Builds the application/x-www-form-urlencoded parameter string.  Can be appended as
	 * the query part to a GET or inside the request body for a POST.
	 * 
	 * @param boolean oauth_as_header		(optional) set to false to include oauth parameters
	 * @return string
	 */	
	function getQueryString ( $oauth_as_header = true )
	{
		$parms = array();
		foreach ($this->param as $name => $value)
		{
			if (	!$oauth_as_header 
				||	(strncmp($name, 'oauth_', 6) != 0 && strncmp($name, 'xoauth_', 7) != 0))
			{
				if (is_array($value))
				{
					foreach ($value as $v)
					{
						$parms[] = $name.'='.$v;
					}
				}
				else
				{
					$parms[] = $name.'='.$value;
				}
			}
		}
		return implode('&', $parms);
	}

}


/* vi:set ts=4 sts=4 sw=4 binary noeol: */

?>