| 1 |
<?php |
| 2 |
|
| 3 |
/** |
| 4 |
* Abstract OAuth consumer |
| 5 |
* @author Ben Tadiar <ben@handcraftedbyben.co.uk> |
| 6 |
* @link https://github.com/benthedesigner/dropbox |
| 7 |
* @package Dropbox\OAuth |
| 8 |
* @subpackage Consumer |
| 9 |
*/ |
| 10 |
|
| 11 |
abstract class Dropbox_ConsumerAbstract |
| 12 |
{ |
| 13 |
// Dropbox web endpoint. v2 API has just dropped the 1/ suffix to the below. |
| 14 |
const WEB_URL = 'https://www.dropbox.com/'; |
| 15 |
|
| 16 |
// OAuth flow methods |
| 17 |
const AUTHORISE_METHOD = 'oauth2/authorize'; |
| 18 |
// Beware - the documentation in one place says oauth2/token/revoke, but that appears to be wrong |
| 19 |
const DEAUTHORISE_METHOD = '2/auth/token/revoke'; |
| 20 |
const ACCESS_TOKEN_METHOD = 'oauth2/token'; |
| 21 |
// The next endpoint only exists with APIv1 |
| 22 |
const OAUTH_UPGRADE = 'oauth2/token_from_oauth1'; |
| 23 |
|
| 24 |
private $scopes = array( |
| 25 |
'account_info.read', |
| 26 |
'files.content.write', |
| 27 |
'files.content.read', |
| 28 |
'files.metadata.read', |
| 29 |
); |
| 30 |
|
| 31 |
/** |
| 32 |
* Signature method, either PLAINTEXT or HMAC-SHA1 |
| 33 |
* @var string |
| 34 |
*/ |
| 35 |
private $sigMethod = 'PLAINTEXT'; |
| 36 |
|
| 37 |
/** |
| 38 |
* Output file handle |
| 39 |
* @var null|resource |
| 40 |
*/ |
| 41 |
protected $outFile = null; |
| 42 |
|
| 43 |
/** |
| 44 |
* Input file handle |
| 45 |
* @var null|resource |
| 46 |
*/ |
| 47 |
protected $inFile = null; |
| 48 |
|
| 49 |
/** |
| 50 |
* Authenticate using 3-legged OAuth flow, firstly |
| 51 |
* checking we don't already have tokens to use |
| 52 |
* @return void |
| 53 |
*/ |
| 54 |
protected function authenticate() |
| 55 |
{ |
| 56 |
global $iwp_backup_core; |
| 57 |
|
| 58 |
$access_token = $this->storage->get('access_token'); |
| 59 |
//Check if the new token type is set if not they need to be upgraded to OAuth2 |
| 60 |
if (!empty($access_token) && isset($access_token->oauth_token) && !isset($access_token->token_type)) { |
| 61 |
$iwp_backup_core->log('OAuth v1 token found: upgrading to v2'); |
| 62 |
$this->upgradeOAuth(); |
| 63 |
$iwp_backup_core->log('OAuth token upgrade successful'); |
| 64 |
} |
| 65 |
|
| 66 |
if (!empty($access_token) && isset($access_token->refresh_token) && isset($access_token->expires_in)) { |
| 67 |
if ($access_token->expires_in < time()) $this->refreshAccessToken(); |
| 68 |
} |
| 69 |
|
| 70 |
if (empty($access_token) || !isset($access_token->oauth_token)) { |
| 71 |
try { |
| 72 |
$this->getAccessToken(); |
| 73 |
} catch(Exception $e) { |
| 74 |
$excep_class = get_class($e); |
| 75 |
// 04-Sep-2015 - Dropbox started throwing a 400, which caused a Dropbox_BadRequestException which previously wasn't being caught |
| 76 |
if ('Dropbox_BadRequestException' == $excep_class || 'Dropbox_Exception' == $excep_class) { |
| 77 |
global $iwp_backup_core; |
| 78 |
$iwp_backup_core->log($e->getMessage().' - need to reauthenticate this site with Dropbox (if this fails, then you can also try wiping your settings from the Expert Settings section)'); |
| 79 |
//$this->getRequestToken(); |
| 80 |
$this->authorise(); |
| 81 |
} else { |
| 82 |
throw $e; |
| 83 |
} |
| 84 |
} |
| 85 |
} |
| 86 |
} |
| 87 |
|
| 88 |
/** |
| 89 |
* Upgrade the user's OAuth1 token to a OAuth2 token |
| 90 |
* @return void |
| 91 |
*/ |
| 92 |
private function upgradeOAuth() |
| 93 |
{ |
| 94 |
// N.B. This call only exists under API v1 - i.e. there is no APIv2 equivalent. Hence the APIv1 endpoint (API_URL) is used, and not the v2 (API_URL_V2) |
| 95 |
$url = 'https://api.dropbox.com/1/' . self::OAUTH_UPGRADE; |
| 96 |
$response = $this->fetch('POST', $url, ''); |
| 97 |
$token = new stdClass(); |
| 98 |
/* |
| 99 |
oauth token secret and oauth token were needed by oauth1 |
| 100 |
these are replaced in oauth2 with an access token |
| 101 |
currently they are still there just in case a method somewhere is expecting them to both be set |
| 102 |
as far as I can tell only the oauth token is used |
| 103 |
after more testing token secret can be removed. |
| 104 |
*/ |
| 105 |
|
| 106 |
$token->oauth_token_secret = $response['body']->access_token; |
| 107 |
$token->oauth_token = $response['body']->access_token; |
| 108 |
$token->token_type = $response['body']->token_type; |
| 109 |
$this->storage->set($token, 'access_token'); |
| 110 |
$this->storage->set('true','upgraded'); |
| 111 |
$this->storage->do_unset('request_token'); |
| 112 |
} |
| 113 |
|
| 114 |
/** |
| 115 |
* Obtain user authorisation |
| 116 |
* The user will be redirected to Dropbox' web endpoint |
| 117 |
* @link http://tools.ietf.org/html/rfc5849#section-2.2 |
| 118 |
* @return void |
| 119 |
*/ |
| 120 |
private function authorise() |
| 121 |
{ |
| 122 |
// Only redirect if not using CLI |
| 123 |
if (PHP_SAPI !== 'cli' && (!defined('DOING_CRON') || !DOING_CRON) && (!defined('DOING_AJAX') || !DOING_AJAX)) { |
| 124 |
$url = $this->getAuthoriseUrl(); |
| 125 |
if (!headers_sent()) { |
| 126 |
header('Location: ' . $url); |
| 127 |
exit; |
| 128 |
} else { |
| 129 |
throw new Dropbox_Exception(sprintf(__('The %s authentication could not go ahead, because something else on your site is breaking it. Try disabling your other plugins and switching to a default theme. (Specifically, you are looking for the component that sends output (most likely PHP warnings/errors) before the page begins. Turning off any debugging settings may also help).', 'InfiniteWP'), 'Dropbox')); |
| 130 |
} |
| 131 |
?><?php |
| 132 |
return false; |
| 133 |
} |
| 134 |
global $iwp_backup_core; |
| 135 |
$iwp_backup_core->log('Dropbox reauthorisation needed; but we are running from cron, AJAX or the CLI, so this is not possible'); |
| 136 |
$this->storage->do_unset('access_token'); |
| 137 |
throw new Dropbox_Exception(sprintf(__('You need to re-authenticate with %s, as your existing credentials are not working.', 'InfiniteWP'), 'Dropbox')); |
| 138 |
return false; |
| 139 |
} |
| 140 |
|
| 141 |
/** |
| 142 |
* Build the user authorisation URL |
| 143 |
* @return string |
| 144 |
*/ |
| 145 |
public function getAuthoriseUrl() |
| 146 |
{ |
| 147 |
/* |
| 148 |
Generate a random key to be passed to Dropbox and stored in session to be checked to prevent CSRF |
| 149 |
Uses OpenSSL or Mcrypt or defaults to pure PHP implementaion if neither are available. |
| 150 |
*/ |
| 151 |
|
| 152 |
global $iwp_backup_core; |
| 153 |
if (!function_exists('crypt_random_string')) $iwp_backup_core->ensure_phpseclib('Crypt_Random'); |
| 154 |
|
| 155 |
$CSRF = base64_encode(crypt_random_string(16)); |
| 156 |
$this->storage->set($CSRF,'CSRF'); |
| 157 |
// Prepare request parameters |
| 158 |
/* |
| 159 |
For OAuth v2 Dropbox needs to use a authorisation url that matches one that is set inside the |
| 160 |
Dropbox developer console. In order to check this it needs the client ID for the OAuth v2 app |
| 161 |
This will use the default one unless the user is using their own Dropbox App |
| 162 |
|
| 163 |
Check if the key has dropbox: if so then remove it to stop the request from being invalid |
| 164 |
*/ |
| 165 |
$appkey = $this->storage->get('appkey'); |
| 166 |
|
| 167 |
if (!empty($appkey) && 'dropbox:' == substr($appkey, 0, 8)) { |
| 168 |
$key = substr($appkey, 8); |
| 169 |
} else if (!empty($appkey)) { |
| 170 |
$key = $appkey; |
| 171 |
} |
| 172 |
|
| 173 |
if ('' != $this->instance_id) $this->instance_id = ':'.$this->instance_id; |
| 174 |
|
| 175 |
$params = array( |
| 176 |
'client_id' => empty($key) ? $this->oauth2_id : $key, |
| 177 |
'response_type' => 'code', |
| 178 |
'redirect_uri' => empty($key) ? $this->callback : $this->callbackhome, |
| 179 |
'state' => empty($key) ? "POST:".$CSRF.$this->instance_id.$this->callbackhome : $CSRF.$this->instance_id, |
| 180 |
'scope' => implode(' ', $this->scopes), |
| 181 |
'token_access_type' => 'offline' |
| 182 |
); |
| 183 |
|
| 184 |
// Build the URL and redirect the user |
| 185 |
$query = '?' . http_build_query($params, '', '&'); |
| 186 |
$url = self::WEB_URL . self::AUTHORISE_METHOD . $query; |
| 187 |
return $url; |
| 188 |
} |
| 189 |
|
| 190 |
protected function deauthenticate() |
| 191 |
{ |
| 192 |
$url = IWP_MMB_Dropbox_API::API_URL_V2 . self::DEAUTHORISE_METHOD; |
| 193 |
$response = $this->fetch('POST', $url, '', array('api_v2' => true)); |
| 194 |
$this->storage->delete(); |
| 195 |
} |
| 196 |
|
| 197 |
/** |
| 198 |
* Acquire an access token |
| 199 |
* Tokens acquired at this point should be stored to |
| 200 |
* prevent having to request new tokens for each API call |
| 201 |
* @link http://tools.ietf.org/html/rfc5849#section-2.3 |
| 202 |
*/ |
| 203 |
public function getAccessToken() |
| 204 |
{ |
| 205 |
|
| 206 |
// If this is non-empty, then we just received a code. It is stored in 'code' - our next job is to put it into the proper place. |
| 207 |
$code = $this->storage->get('code'); |
| 208 |
/* |
| 209 |
Checks to see if the user is using their own Dropbox App if so then they need to get |
| 210 |
a request token. If they are using our App then we just need to save these details |
| 211 |
*/ |
| 212 |
if (!empty($code)){ |
| 213 |
$appkey = $this->storage->get('appkey'); |
| 214 |
if (!empty($appkey)){ |
| 215 |
// Get the signed request URL |
| 216 |
$url = IWP_MMB_Dropbox_API::API_URL_V2 . self::ACCESS_TOKEN_METHOD; |
| 217 |
$params = array( |
| 218 |
'code' => $code, |
| 219 |
'grant_type' => 'authorization_code', |
| 220 |
'redirect_uri' => $this->callbackhome, |
| 221 |
'client_id' => $this->consumerKey, |
| 222 |
'client_secret' => $this->consumerSecret, |
| 223 |
); |
| 224 |
$response = $this->fetch('POST', $url, '' , $params); |
| 225 |
|
| 226 |
$code = json_decode(json_encode($response['body']),true); |
| 227 |
|
| 228 |
} else { |
| 229 |
$code = base64_decode($code); |
| 230 |
$code = json_decode($code, true); |
| 231 |
} |
| 232 |
|
| 233 |
/* |
| 234 |
Again oauth token secret and oauth token were needed by oauth1 |
| 235 |
these are replaced in oauth2 with an access token |
| 236 |
currently they are still there just in case a method somewhere is expecting them to both be set |
| 237 |
as far as I can tell only the oauth token is used |
| 238 |
after more testing token secret can be removed. |
| 239 |
*/ |
| 240 |
$token = new stdClass(); |
| 241 |
$token->oauth_token_secret = $code['access_token']; |
| 242 |
$token->oauth_token = $code['access_token']; |
| 243 |
$token->account_id = $code['account_id']; |
| 244 |
$token->token_type = $code['token_type']; |
| 245 |
$token->uid = $code['uid']; |
| 246 |
$token->refresh_token = $code['refresh_token']; |
| 247 |
$token->expires_in = time() + $code['expires_in'] - 30; |
| 248 |
$this->storage->set($token, 'access_token'); |
| 249 |
$this->storage->do_unset('upgraded'); |
| 250 |
|
| 251 |
//reset code |
| 252 |
$this->storage->do_unset('code'); |
| 253 |
} else { |
| 254 |
throw new Dropbox_BadRequestException("No Dropbox Code found, will try to get one now", 400); |
| 255 |
} |
| 256 |
} |
| 257 |
|
| 258 |
/** |
| 259 |
* This function will make a request to the auth server sending the users refresh token to get a new access token |
| 260 |
* |
| 261 |
* @return void |
| 262 |
*/ |
| 263 |
public function refreshAccessToken() { |
| 264 |
global $iwp_backup_core; |
| 265 |
|
| 266 |
$access_token = $this->storage->get('access_token'); |
| 267 |
|
| 268 |
$params = array( |
| 269 |
'grant_type' => 'refresh_token', |
| 270 |
'refresh_token' => $access_token->refresh_token, |
| 271 |
); |
| 272 |
|
| 273 |
$url = IWP_MMB_Dropbox_API::API_URL_V2 . self::ACCESS_TOKEN_METHOD; |
| 274 |
|
| 275 |
$response = $this->fetch('POST', $url, '' , $params); |
| 276 |
|
| 277 |
if ("200" != $response['code']) { |
| 278 |
$iwp_backup_core->log('Failed to refresh access token error code: '.$response['code']); |
| 279 |
return; |
| 280 |
} |
| 281 |
|
| 282 |
if (empty($response['body'])) { |
| 283 |
$iwp_backup_core->log('Failed to refresh access token empty response body'); |
| 284 |
return; |
| 285 |
} |
| 286 |
|
| 287 |
$body = $response['body']; |
| 288 |
|
| 289 |
if (isset($body->access_token) && isset($body->expires_in)) { |
| 290 |
$access_token->oauth_token_secret = $body->access_token; |
| 291 |
$access_token->oauth_token = $body->access_token; |
| 292 |
$access_token->expires_in = time() + $body->expires_in - 30; |
| 293 |
$this->storage->set($access_token, 'access_token'); |
| 294 |
$iwp_backup_core->log('Successfully updated and refreshed the access token'); |
| 295 |
} else { |
| 296 |
$iwp_backup_core->log('Failed to refresh access token missing token and expiry: '.json_encode($body)); |
| 297 |
return; |
| 298 |
} |
| 299 |
} |
| 300 |
|
| 301 |
/** |
| 302 |
* Get the request/access token |
| 303 |
* This will return the access/request token depending on |
| 304 |
* which stage we are at in the OAuth flow, or a dummy object |
| 305 |
* if we have not yet started the authentication process |
| 306 |
* @return object stdClass |
| 307 |
*/ |
| 308 |
private function getToken() |
| 309 |
{ |
| 310 |
if (!$token = $this->storage->get('access_token')) { |
| 311 |
if (!$token = $this->storage->get('request_token')) { |
| 312 |
$token = new stdClass(); |
| 313 |
$token->oauth_token = null; |
| 314 |
$token->oauth_token_secret = null; |
| 315 |
} |
| 316 |
} |
| 317 |
return $token; |
| 318 |
} |
| 319 |
|
| 320 |
/** |
| 321 |
* Generate signed request URL |
| 322 |
* See inline comments for description |
| 323 |
* @link http://tools.ietf.org/html/rfc5849#section-3.4 |
| 324 |
* @param string $method HTTP request method |
| 325 |
* @param string $url API endpoint to send the request to |
| 326 |
* @param string $call API call to send |
| 327 |
* @param array $additional Additional parameters as an associative array |
| 328 |
* @return array |
| 329 |
*/ |
| 330 |
protected function getSignedRequest($method, $url, $call, array $additional = array()) |
| 331 |
{ |
| 332 |
// Get the request/access token |
| 333 |
$token = $this->getToken(); |
| 334 |
|
| 335 |
// Prepare the standard request parameters differently for OAuth1 and OAuth2; we still need OAuth1 to make the request to the upgrade token endpoint |
| 336 |
if (isset($token)) { |
| 337 |
if (isset($token->oauth_token)) { |
| 338 |
$params = array( |
| 339 |
'access_token' => $token->oauth_token, |
| 340 |
); |
| 341 |
}else{ |
| 342 |
$params = array( |
| 343 |
'access_token' => $token, |
| 344 |
); |
| 345 |
} |
| 346 |
|
| 347 |
/* |
| 348 |
To keep this API backwards compatible with the API v1 endpoints all v2 endpoints will also send to this method a api_v2 parameter this will then return just the access token as the signed request is not needed for any calls. |
| 349 |
*/ |
| 350 |
|
| 351 |
if (isset($additional['api_v2']) && $additional['api_v2'] == true && !isset($additional['refresh_token'])) { |
| 352 |
unset($additional['api_v2']); |
| 353 |
if (isset($additional['timeout'])) unset($additional['timeout']); |
| 354 |
if (isset($additional['content_download']) && $additional['content_download'] == true) { |
| 355 |
unset($additional['content_download']); |
| 356 |
$extra_headers = array(); |
| 357 |
if (isset($additional['headers'])) { |
| 358 |
foreach ($additional['headers'] as $key => $header) { |
| 359 |
$extra_headers[] = $header; |
| 360 |
} |
| 361 |
unset($additional['headers']); |
| 362 |
} |
| 363 |
$headers = array( |
| 364 |
'Authorization: Bearer '.$params['access_token'], |
| 365 |
'Content-Type:', |
| 366 |
'Dropbox-API-Arg: '.json_encode($additional), |
| 367 |
); |
| 368 |
|
| 369 |
$headers = array_merge($headers, $extra_headers); |
| 370 |
$additional = ''; |
| 371 |
} else if (isset($additional['content_upload']) && $additional['content_upload'] == true) { |
| 372 |
unset($additional['content_upload']); |
| 373 |
$headers = array( |
| 374 |
'Authorization: Bearer '.$params['access_token'], |
| 375 |
'Content-Type: application/octet-stream', |
| 376 |
'Dropbox-API-Arg: '.json_encode($additional), |
| 377 |
); |
| 378 |
$additional = ''; |
| 379 |
} else { |
| 380 |
$headers = array( |
| 381 |
'Authorization: Bearer '.$params['access_token'], |
| 382 |
'Content-Type: application/json', |
| 383 |
); |
| 384 |
} |
| 385 |
return array( |
| 386 |
'url' => $url . $call, |
| 387 |
'postfields' => $additional, |
| 388 |
'headers' => $headers, |
| 389 |
); |
| 390 |
} elseif (isset($additional['refresh_token'])) { |
| 391 |
$extra_headers = array(); |
| 392 |
if (isset($additional['headers']) && !empty($additional['headers'])) { |
| 393 |
foreach ($additional['headers'] as $key => $header) { |
| 394 |
$extra_headers[] = $key.': '.$header; |
| 395 |
} |
| 396 |
unset($additional['headers']); |
| 397 |
} |
| 398 |
$headers = array(); |
| 399 |
$headers[] = 'Authorization: Basic ' . base64_encode($this->consumerKey . ':' . $this->consumerSecret); |
| 400 |
// $headers[] = 'Content-Type: application/x-www-form-urlencoded'; |
| 401 |
// $headers[] = 'Content-Type: application/json'; |
| 402 |
$headers = array_merge($headers, $extra_headers); |
| 403 |
|
| 404 |
return array( |
| 405 |
'url' => $url . $call, |
| 406 |
'postfields' => $additional, |
| 407 |
'headers' => $headers, |
| 408 |
); |
| 409 |
} |
| 410 |
} else { |
| 411 |
// Generate a random string for the request |
| 412 |
$nonce = md5(microtime(true) . uniqid('', true)); |
| 413 |
$params = array( |
| 414 |
'oauth_consumer_key' => $this->consumerKey, |
| 415 |
'oauth_token' => $token->oauth_token, |
| 416 |
'oauth_signature_method' => $this->sigMethod, |
| 417 |
'oauth_version' => '1.0', |
| 418 |
// Generate nonce and timestamp if signature method is HMAC-SHA1 |
| 419 |
'oauth_timestamp' => ($this->sigMethod == 'HMAC-SHA1') ? time() : null, |
| 420 |
'oauth_nonce' => ($this->sigMethod == 'HMAC-SHA1') ? $nonce : null, |
| 421 |
); |
| 422 |
} |
| 423 |
|
| 424 |
// Merge with the additional request parameters |
| 425 |
$params = array_merge($params, $additional); |
| 426 |
ksort($params); |
| 427 |
|
| 428 |
// URL encode each parameter to RFC3986 for use in the base string |
| 429 |
$encoded = array(); |
| 430 |
foreach($params as $param => $value) { |
| 431 |
if ($value !== null) { |
| 432 |
// If the value is a file upload (prefixed with @), replace it with |
| 433 |
// the destination filename, the file path will be sent in POSTFIELDS |
| 434 |
if (isset($value[0]) && $value[0] === '@') $value = $params['filename']; |
| 435 |
# Prevent spurious PHP warning by only doing non-arrays |
| 436 |
if (!is_array($value)) $encoded[] = $this->encode($param) . '=' . $this->encode($value); |
| 437 |
} else { |
| 438 |
unset($params[$param]); |
| 439 |
} |
| 440 |
} |
| 441 |
|
| 442 |
// Build the first part of the string |
| 443 |
$base = $method . '&' . $this->encode($url . $call) . '&'; |
| 444 |
|
| 445 |
// Re-encode the encoded parameter string and append to $base |
| 446 |
$base .= $this->encode(implode('&', $encoded)); |
| 447 |
|
| 448 |
// Concatenate the secrets with an ampersand |
| 449 |
$key = $this->consumerSecret . '&' . $token->oauth_token_secret; |
| 450 |
|
| 451 |
// Get the signature string based on signature method |
| 452 |
$signature = $this->getSignature($base, $key); |
| 453 |
$params['oauth_signature'] = $signature; |
| 454 |
|
| 455 |
// Build the signed request URL |
| 456 |
$query = '?' . http_build_query($params, '', '&'); |
| 457 |
|
| 458 |
return array( |
| 459 |
'url' => $url . $call . $query, |
| 460 |
'postfields' => $params, |
| 461 |
); |
| 462 |
} |
| 463 |
|
| 464 |
/** |
| 465 |
* Generate the oauth_signature for a request |
| 466 |
* @param string $base Signature base string, used by HMAC-SHA1 |
| 467 |
* @param string $key Concatenated consumer and token secrets |
| 468 |
*/ |
| 469 |
private function getSignature($base, $key) |
| 470 |
{ |
| 471 |
switch ($this->sigMethod) { |
| 472 |
case 'PLAINTEXT': |
| 473 |
$signature = $key; |
| 474 |
break; |
| 475 |
case 'HMAC-SHA1': |
| 476 |
$signature = base64_encode(hash_hmac('sha1', $base, $key, true)); |
| 477 |
break; |
| 478 |
} |
| 479 |
|
| 480 |
return $signature; |
| 481 |
} |
| 482 |
|
| 483 |
/** |
| 484 |
* Set the OAuth signature method |
| 485 |
* @param string $method Either PLAINTEXT or HMAC-SHA1 |
| 486 |
* @return void |
| 487 |
*/ |
| 488 |
public function setSignatureMethod($method) |
| 489 |
{ |
| 490 |
$method = strtoupper($method); |
| 491 |
|
| 492 |
switch ($method) { |
| 493 |
case 'PLAINTEXT': |
| 494 |
case 'HMAC-SHA1': |
| 495 |
$this->sigMethod = $method; |
| 496 |
break; |
| 497 |
default: |
| 498 |
throw new Dropbox_Exception('Unsupported signature method ' . $method); |
| 499 |
} |
| 500 |
} |
| 501 |
|
| 502 |
/** |
| 503 |
* Set the output file |
| 504 |
* @param resource Resource to stream response data to |
| 505 |
* @return void |
| 506 |
*/ |
| 507 |
public function setOutFile($handle) |
| 508 |
{ |
| 509 |
if (!is_resource($handle) || get_resource_type($handle) != 'stream') { |
| 510 |
throw new Dropbox_Exception('Outfile must be a stream resource'); |
| 511 |
} |
| 512 |
$this->outFile = $handle; |
| 513 |
} |
| 514 |
|
| 515 |
/** |
| 516 |
* Set the input file |
| 517 |
* @param resource Resource to read data from |
| 518 |
* @return void |
| 519 |
*/ |
| 520 |
public function setInFile($handle) { |
| 521 |
$this->inFile = $handle; |
| 522 |
} |
| 523 |
|
| 524 |
/** |
| 525 |
* Parse response parameters for a token into an object |
| 526 |
* Dropbox returns tokens in the response parameters, and |
| 527 |
* not a JSON encoded object as per other API requests |
| 528 |
* @link http://oauth.net/core/1.0/#response_parameters |
| 529 |
* @param string $response |
| 530 |
* @return object stdClass |
| 531 |
*/ |
| 532 |
private function parseTokenString($response) |
| 533 |
{ |
| 534 |
$parts = explode('&', $response); |
| 535 |
$token = new stdClass(); |
| 536 |
foreach ($parts as $part) { |
| 537 |
list($k, $v) = explode('=', $part, 2); |
| 538 |
$k = strtolower($k); |
| 539 |
$token->$k = $v; |
| 540 |
} |
| 541 |
return $token; |
| 542 |
} |
| 543 |
|
| 544 |
/** |
| 545 |
* Encode a value to RFC3986 |
| 546 |
* This is a convenience method to decode ~ symbols encoded |
| 547 |
* by rawurldecode. This will encode all characters except |
| 548 |
* the unreserved set, ALPHA, DIGIT, '-', '.', '_', '~' |
| 549 |
* @link http://tools.ietf.org/html/rfc5849#section-3.6 |
| 550 |
* @param mixed $value |
| 551 |
*/ |
| 552 |
private function encode($value) |
| 553 |
{ |
| 554 |
return str_replace('%7E', '~', rawurlencode($value)); |
| 555 |
} |
| 556 |
} |
| 557 |
|