-
Notifications
You must be signed in to change notification settings - Fork 1
/
HTTP_Request_Oauth.php
321 lines (265 loc) · 12.6 KB
/
HTTP_Request_Oauth.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
<?php
require_once 'HTTP/Request.php';
function _hex2bin($hex) {
$bin = '';
for($i = 0; $i < strlen($hex); $i += 2)
$bin .= chr(hexdec($hex{$i+1}) + hexdec($hex{$i}) * 16);
return base64_encode($bin);
}
class HTTP_Request_OAuth
extends HTTP_Request
{
/**
* Oauth realm
* @var string
*/
var $_realm;
/**
* Oauth consumer key
* @var string
*/
var $_consumer_key;
/**
* OAuth consumer secret
* @var string
*/
var $_consumer_secret;
/**
* Oauth token
* @var string
*/
var $_token;
/**
* Oauth token secret
* @var string
*/
var $_token_secret;
/**
* Constructor
*
* Sets up the object
* @param string The url to fetch/access
* @param array Associative array of parameters which can have the following keys:
* <ul>
* <li>consumer_key - Oauth consumer key (string)</li>
* <li>consumer_secret - Oauth consumer secret (string)</li>
* <li>signature_method - TBD</li>
* <li>token - Oauth session token or frob (string)</li>
* <li>token_secret - Oauth session secret (string)</li>
* <li>realm - Oauth realm (string)</li>
* <li>method - Method to use, GET, POST etc (string)</li>
* <li>http - HTTP Version to use, 1.0 or 1.1 (string)</li>
* <li>user - Basic Auth username (string)</li>
* <li>pass - Basic Auth password (string)</li>
* <li>proxy_host - Proxy server host (string)</li>
* <li>proxy_port - Proxy server port (integer)</li>
* <li>proxy_user - Proxy auth username (string)</li>
* <li>proxy_pass - Proxy auth password (string)</li>
* <li>timeout - Connection timeout in seconds (float)</li>
* <li>allowRedirects - Whether to follow redirects or not (bool)</li>
* <li>maxRedirects - Max number of redirects to follow (integer)</li>
* <li>useBrackets - Whether to append [] to array variable names (bool)</li>
* <li>saveBody - Whether to save response body in response object property (bool)</li>
* <li>readTimeout - Timeout for reading / writing data over the socket (array (seconds, microseconds))</li>
* <li>socketOptions - Options to pass to Net_Socket object (array)</li>
* </ul>
* @access public
*/
function HTTP_Request_OAuth($url = '', $params = array())
{
$this->_realm = null;
$this->_token = null;
$this->_token_secret = null;
$this->_consumer_key = null;
$this->_consumer_secret = null;
$this->signature_method = $params['signature_method'];
HTTP_Request::HTTP_Request($url, $params);
$this->addHeader('User-Agent', 'PHP HTTP_Request_OAuth class');
}
/**
* Signs the request, then sends it
*
* @access public
* @param bool Whether to store response body in Response object property,
* set this to false if downloading a LARGE file and using a Listener
* @return mixed PEAR error on error, true otherwise
*/
function sendRequest($saveBody=true, $authHeader=false)
{
$this->sign($authHeader);
$r = HTTP_Request::sendRequest($saveBody);
return $r;
}
/**
* @return array Various oauth_* parameters, as defined in the spec.
* Includes: oauth_token, oauth_consumer_key,
* oauth_signature_method, oauth_signature,
* oauth_timestamp, oauth_nonce, oauth_version.
*/
function oauth_parameters()
{
// e.g.: https://photos.example.net/request_token?oauth_consumer_key=dpf43f3p2l4k3l03&oauth_signature_method=PLAINTEXT&oauth_signature=kd94hf93k423kf44%26&oauth_timestamp=1191242090&oauth_nonce=hsu94j3884jdopsl&oauth_version=1.0
return array('oauth_token' => $this->_token,
'oauth_consumer_key' => $this->_consumer_key,
'oauth_signature_method' => $this->signature_method,
'oauth_signature' => null,
'oauth_timestamp' => time(),
'oauth_nonce' => uniqid(''),
'oauth_version' => '1.0');
}
/**
* @param array $params Associative array of parameters to normalize for signing.
* Note that values are expected to be urlencoded!
*
* @return string CGI query parameter string for signing.
*/
function oauth_parametersToString($params)
{
$param_keys = array_keys($params);
$param_values = array_values($params);
// sort by name, then by value
array_multisort($param_keys, SORT_ASC, $param_values, SORT_ASC);
// pack parameters into a normalized string
$normalized_keyvalues = array();
for($i = 0; $i < count($param_keys); $i += 1) {
$key = $param_keys[$i];
$value = $param_values[$i];
// don't urlencode the values - they are probably already urlencoded
// ?? ^ WTF? "Probably"?
if($key != 'oauth_signature')
if($key != 'oauth_token' || $value) {
$value = rawurlencode(urldecode($value));
$normalized_keyvalues[] = urlencode($key).'='.$value;
}
}
return join('&', $normalized_keyvalues);
}
/**
* @return string Request URL as defined in the spec,
* i.e. everything up to the query string.
*/
function oauth_requestURL()
{
return $this->_url->protocol . '://'
. $this->_url->user . (!empty($this->_url->pass) ? ':' : '')
. $this->_url->pass . (!empty($this->_url->user) ? '@' : '')
. $this->_url->host . ($this->_url->port == $this->_url->getStandardPort($this->_url->protocol) ? '' : ':' . $this->_url->port)
. $this->_url->path;
}
function _sha1($s, $consumer_secret, $token_secret) {
$key = $consumer_secret . '&' . $token_secret;
$digest_b64 = base64_encode(hash_hmac("sha1", $s, $key, TRUE));
return $digest_b64;
}
/**
* @return string Binary md5 digest, as distinct from PHP's built-in hexdigest.
*/
function _md5($s, $consumer_secret, $token_secret)
{
$s = join('&', array_map('urlencode',
array($s, $consumer_secret, $token_secret)));
$md5 = md5($s);
$bin = '';
for($i = 0; $i < strlen($md5); $i += 2)
$bin .= chr(hexdec($md5{$i+1}) + hexdec($md5{$i}) * 16);
return base64_encode($bin);
}
function addParam($key, $val, $preencoded = false) {
if ($this->_method == 'GET') {
$this->addQueryString($key, $val, $preencoded);
} else {
$this->addPostData($key, $val, $preencoded);
}
}
/**
* Sign this here request.
*
* @param boolean $authHeader Where to put the signature:
* true means Authorization HTTP header,
* false means CGI query params.
*/
function sign($authHeader=false)
{
$oauth_parameters = $this->oauth_parameters();
// get any and all existing oauth_* params out of POST, GET
foreach($oauth_parameters as $key => $value) {
$this->_url->removeQueryString($key);
unset($this->_postData[$key]);
}
// get a callback reference to the proper function for adding new oauth_* params
// function should accept two arguments: key, value
$parameter_adder = in_array($this->_requestHeaders['content-type'], array('application/x-www-form-urlencoded', 'multipart/form-data'))
? array(&$this, 'addPostData')
: array(&$this->_url, 'addQueryString');
// for later normalizing
$parameters_to_normalize = array();
// add new oauth_* parameters if they're not supposed to be in a header
foreach($oauth_parameters as $key => $value)
if($key != 'oauth_signature')
if($key != 'oauth_token' || $value) {
if(!$authHeader)
call_user_func($parameter_adder, $key, $value);
// these will later need to be normalized, and are expected to be urlencoded
$parameters_to_normalize[$key] = urlencode($value);
}
// the master list of parameters to normalize, order matters
$parameters_to_normalize = array_merge($this->_url->querystring,
$this->_postData,
$parameters_to_normalize);
$normalized_params_string = $this->oauth_parametersToString($parameters_to_normalize);
$signature_parts = array($this->_method,
$this->oauth_requestURL(),
$normalized_params_string);
$signed_string = join('&', array_map('urlencode', $signature_parts));
$oauth_parameters['oauth_signature'] =
$this->signature_method == 'md5' ?
HTTP_Request_OAuth::_md5($signed_string,
$this->_consumer_secret,
$this->_token_secret) :
$this->signature_method == 'HMAC-SHA1' ?
HTTP_Request_OAuth::_sha1($signed_string,
$this->_consumer_secret,
$this->_token_secret) :
die('unknown signature method');
if($authHeader) {
// oauth_* params go into the Authorization request header
$authorization_header = "OAuth ";
$i = 0;
if ($this->_realm) {
$authorization_header .= "realm=\"{$this->_realm}\"";
$i++;
}
foreach($oauth_parameters as $key => $value) {
# BLAH just want to join a list with ", "
if($key != 'oauth_token' || $value) {
if ($i++ > 0)
$authorization_header .= ", ";
$value = urlencode($value);
$authorization_header .= "{$key}=\"{$value}\"";
}
}
$this->addHeader('Authorization', $authorization_header);
} else {
// oauth_* params go into the request body or URL, see above
call_user_func($parameter_adder, 'oauth_signature', $oauth_parameters['oauth_signature']);
}
// for testing, or whatever - wildly insecure:
/*
$this->addHeader('X-Oauth-Params', $normalized_params_string);
$this->addHeader('X-Oauth-String', $signed_string);
$this->addHeader('X-Oauth-URL', $this->_url->getURL());
*/
}
/**
* @return array Token and secret, for requests that return such values.
*/
function getResponseTokenSecret()
{
if(preg_match('/\boauth_token=(\S+)\b/Uis', $this->getResponseBody(), $m))
$token = $m[1];
if(preg_match('/\boauth_token_secret=(\S+)\b/Uis', $this->getResponseBody(), $m))
$secret = $m[1];
return array($token, $secret);
}
}
?>