I think the problem is only in the signature part of OAuth implementation. I use also OAuth with Twitter and here it works without a problem.
But as far as I can see Twitter doesn't use the whole signature sing as Delicious (Yahoo) does in step 6: http://delicious.com/help/oauthapi
Here is the code how I implemented it. The problem occurs in the third step (post) only if the variable $data['title'] has a space inside. Otherwise it works without a problem.
Unable to find source-code formatter for language: php. Available languages are: javascript, sql, xhtml, actionscript, none, html, xml, java
public function authorize(array $data) {
$config = array(
'siteUrl' => 'https: 'callbackUrl' => '/callback',
'consumerKey' => $key,
'consumerSecret' => $secret,
);
$consumer = new Zend_Oauth_Consumer($config);
$token = $consumer->getRequestToken();
$session = new Zend_Session_Namespace('delicious_oauth');
$session->token = $token->getToken();
$session->secret = $token->getTokenSecret();
$urlParams = $token->getResponse()->getBody();
$url = 'https:
}
public function callback() {
$config = array(
'siteUrl' => 'https: 'callbackUrl' => '/callback',
'consumerKey' => $key,
'consumerSecret' => $secret,
);
$session = new Zend_Session_Namespace('delicious_oauth');
$request = new Zend_Oauth_Token_Request();
$request->setToken($session->token)
->setTokenSecret($session->secret);
unset($session->token);
unset($session->secret);
$consumer = new Zend_Oauth_Consumer($config);
$token = $consumer->getAccessToken($_GET, $request);
$data = array('oauth_token' => $token->getToken(), 'oauth_token_secret' => $token->getTokenSecret());
}
public function post(array $data) {
$config = array(
'siteUrl' => 'https: 'callbackUrl' => '/callback',
'consumerKey' => $key,
'consumerSecret' => $secret,
);
$data = $this->getData();
$token2 = new Zend_Oauth_Token_Access();
$token2->setToken($data['oauth_token'])
->setTokenSecret($data['oauth_token_secret']);
$client = $token2->getHttpClient($config);
$client->resetParameters();
$parameters = array(
'url' => $data['url'],
'description' => $data['title'],
'tags' => $data['tags'],
'extended' => $data['note'],
);
$client->setUri('http: $client->setParameterGet($parameters);
$client->setMethod(Zend_Http_Client::GET);
$client->setAdapter(new useKit_Http_Client_Adapter_Socket());
$response = $client->request();
return true;
}
Here is also the code from my client. It only overloads the standard client and adds the str_replace function.
Unable to find source-code formatter for language: php. Available languages are: javascript, sql, xhtml, actionscript, none, html, xml, java
<?php
class useKit_Http_Client_Adapter_Socket extends Zend_Http_Client_Adapter_Socket
{
/**
* Send request to the remote server
*
* @param string $method
* @param Zend_Uri_Http $uri
* @param string $http_ver
* @param array $headers
* @param string $body
* @return string Request as string
*/
public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '')
{
if (! $this->socket) {
require_once 'Zend/Http/Client/Adapter/Exception.php';
throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are not connected');
}
$host = $uri->getHost();
$host = (strtolower($uri->getScheme()) == 'https' ? $this->config['ssltransport'] : 'tcp') . ': if ($this->connected_to[0] != $host || $this->connected_to[1] != $uri->getPort()) {
require_once 'Zend/Http/Client/Adapter/Exception.php';
throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are connected to the wrong host');
}
$this->method = $method;
$path = $uri->getPath();
$query = str_replace('+', '%20', $uri->getQuery());
if ($uri->getQuery()) $path .= '?' . $query;
$request = "{$method} {$path} HTTP/{$http_ver}\r\n";
foreach ($headers as $k => $v) {
if (is_string($k)) $v = ucfirst($k) . ": $v";
$request .= "$v\r\n";
}
if(is_resource($body)) {
$request .= "\r\n";
} else {
$request .= "\r\n" . $body;
}
if (! @fwrite($this->socket, $request)) {
require_once 'Zend/Http/Client/Adapter/Exception.php';
throw new Zend_Http_Client_Adapter_Exception('Error writing request to server');
}
if(is_resource($body)) {
if(stream_copy_to_stream($body, $this->socket) == 0) {
require_once 'Zend/Http/Client/Adapter/Exception.php';
throw new Zend_Http_Client_Adapter_Exception('Error writing request to server');
}
}
return $request;
}
}
On thing I discovered during searching for the bug is, that the Yahoo also implements one function a bit different (the PHP library can be found here: http://developer.yahoo.com/social/sdk/#php )
The url encoding is done as following
Unable to find source-code formatter for language: php. Available languages are: javascript, sql, xhtml, actionscript, none, html, xml, java
static function urlencode_rfc3986($input) {
...
str_replace('+', ' ',
str_replace('%7E', '~', rawurlencode($input)));
...
}
In the Zend_Oauth_Http_Utility it is:
Unable to find source-code formatter for language: php. Available languages are: javascript, sql, xhtml, actionscript, none, html, xml, java
public static function urlEncode($value)
{
$encoded = rawurlencode($value);
$encoded = str_replace('%7E', '~', $encoded);
return $encoded;
}
As defined in the OAuth Protocol, every URL should be encode with rfc3986. I'm not sure, if the second implementation is also RFC 3986 http://tools.ietf.org/html/rfc3986
But like I described before, in the end it wasn't a problem of all the oauth functions, which are creating the right signature. The problem is, that inside the signature the url is encode with rfc3986 which means, it replaces spaces with %20. But then Zend_Http_Client uses http://ch2.php.net/manual/de/function.http-build-query.php to create the queries, and replaces spaces with +. So the reponse server creates a different signature.
May need a more detailed description as to how the client is being used (i.e. request method, authorisation scheme (any option passed into Zend_Oauth). Note that Zend_Oauth has been used elsewhere with no reported problems of this specific nature. Changing the encoding may alter signatures for working services (can't be allowed to occur), so I need to see whether this is something in the way the component is used specifically for Delicious but not for other service APIs. If this is only impacting Delicious when similar use cases work elsewhere, then it will not be fixed except for specific use cases in something like Zend_Service_Delicious where it becomes necessary but limited in scope.
Additionally, Zend_Oauth extends Zend_Http_Client which is responsible for URI encoding. This would require a fix to Zend_Http_Client if an incorrect behaviour.