/** * Constructor. * * @param Dropbox_AppInfo $appInfo * See {@link getAppInfo()} * @param string $clientIdentifier * See {@link getClientIdentifier()} * @param null|string $userLocale * See {@link getUserLocale()} */ public function __construct($appInfo, $clientIdentifier, $userLocale = null) { Dropbox_AppInfo::checkArg("appInfo", $appInfo); Dropbox_Client::checkClientIdentifierArg("clientIdentifier", $clientIdentifier); Dropbox_Checker::argStringNonEmptyOrNull("userLocale", $userLocale); $this->appInfo = $appInfo; $this->clientIdentifier = $clientIdentifier; $this->userLocale = $userLocale; }
/** * Returns cryptographically strong secure random bytes (as a PHP string). * * @param int $numBytes * The number of bytes of random data to return. * * @return string */ public static function getRandomBytes($numBytes) { Dropbox_Checker::argIntPositive("numBytes", $numBytes); // openssl_random_pseudo_bytes had some issues prior to PHP 5.3.4 if (function_exists('openssl_random_pseudo_bytes') && version_compare(PHP_VERSION, '5.3.4') >= 0) { $s = openssl_random_pseudo_bytes($numBytes, $isCryptoStrong); if ($isCryptoStrong) { return $s; } } if (function_exists('mcrypt_create_iv')) { return mcrypt_create_iv($numBytes); } // Hopefully the above two options cover all our users. But if not, there are // other platform-specific options we could add. throw new Exception("no suitable random number source available"); }
/** * Use this to check that a function argument is of type <code>AppInfo</code> * * @internal */ public static function checkArg($argName, $argValue) { if (!$argValue instanceof self) { Dropbox_Checker::throwError($argName, $argValue, __CLASS__); } }
/** * Call this after the user has visited the authorize URL returned by {@link start()}, * approved your app, was presented with an authorization code by Dropbox, and has copy/paste'd * that authorization code into your app. * * See <a href="https://www.dropbox.com/developers/core/docs#oa2-token">/oauth2/token</a>. * * @param string $code * The authorization code provided to the user by Dropbox. * * @return array * A <code>list(string $accessToken, string $userId)</code>, where * <code>$accessToken</code> can be used to construct a {@link Client} and * <code>$userId</code> is the user ID of the user's Dropbox account. * * @throws Dropbox_Exception * Thrown if there's an error getting the access token from Dropbox. */ public function finish($code) { Dropbox_Checker::argStringNonEmpty("code", $code); return $this->_finish($code, null); }
/** * Check that a function argument is either <code>null</code> or of type * <code>WriteMode</code>. * * @internal */ public static function checkArgOrNull($argName, $argValue) { if ($argValue === null) { return; } if (!$argValue instanceof self) { Dropbox_Checker::throwError($argName, $argValue, __CLASS__); } }
/** * Return the last component of a path (the file or folder name). * * <code> * Path::getName("/Misc/Notes.txt") // "Notes.txt" * Path::getName("/Misc") // "Misc" * Path::getName("/") // null * </code> * * @param string $path * The full path you want to get the last component of. * * @return null|string * The last component of <code>$path</code> or <code>null</code> if the given * <code>$path</code> was <code>"/"<code>. */ public static function getName($path) { Dropbox_Checker::argString("path", $path); if (substr_compare($path, "/", 0, 1) !== 0) { throw new InvalidArgumentException("'path' must start with \"/\""); } $l = strlen($path); if ($l === 1) { return null; } if ($path[$l - 1] === "/") { throw new InvalidArgumentException("'path' must not end with \"/\""); } $lastSlash = strrpos($path, "/"); return substr($path, $lastSlash + 1); }
/** * Perform an OAuth-2-authorized POST request to the Dropbox API. Will automatically * fill in "User-Agent" and "locale" as well. * * @param string $host * Either the "API" or "API content" hostname from {@link getHost()}. * @param string $path * The "path" part of the URL. For example, "/commit_chunked_upload". * @param array|null $params * POST parameters. * * @return Dropbox_HttpResponse * * @throws Dropbox_Exception */ public function doPost($host, $path, $params = null) { Dropbox_Checker::argString("host", $host); Dropbox_Checker::argString("path", $path); return Dropbox_RequestUtil::doPost($this->clientIdentifier, $this->accessToken, $this->userLocale, $host, $path, $params); }
/** * Call this after the user has visited the authorize URL ({@link start()}), approved your app, * and was redirected to your redirect URI. * * See <a href="https://www.dropbox.com/developers/core/docs#oa2-token">/oauth2/token</a>. * * @param array $queryParams * The query parameters on the GET request to your redirect URI. * * @return array * A <code>list(string $accessToken, string $userId, string $urlState)</code>, where * <code>$accessToken</code> can be used to construct a {@link Client}, <code>$userId</code> * is the user ID of the user's Dropbox account, and <code>$urlState</code> is the * value you originally passed in to {@link start()}. * * @throws Dropbox_Exception * Thrown if there's an error getting the access token from Dropbox. * @throws Dropbox_WebAuthException_BadRequest * @throws Dropbox_WebAuthException_BadState * @throws Dropbox_WebAuthException_Csrf * @throws Dropbox_WebAuthException_NotApproved * @throws Dropbox_WebAuthException_Provider * * */ public function finish($queryParams) { Dropbox_Checker::argArray("queryParams", $queryParams); $csrfTokenFromSession = $this->csrfTokenStore->get(); Dropbox_Checker::argStringOrNull("this->csrfTokenStore->get()", $csrfTokenFromSession); // Check well-formedness of request. if (!isset($queryParams['state'])) { throw new Dropbox_WebAuthException_BadRequest("Missing query parameter 'state'."); } $state = $queryParams['state']; Dropbox_Checker::argString("queryParams['state']", $state); $error = null; $errorDescription = null; if (isset($queryParams['error'])) { $error = $queryParams['error']; Dropbox_Checker::argString("queryParams['error']", $error); if (isset($queryParams['error_description'])) { $errorDescription = $queryParams['error_description']; Dropbox_Checker::argString("queryParams['error_description']", $errorDescription); } } $code = null; if (isset($queryParams['code'])) { $code = $queryParams['code']; Dropbox_Checker::argString("queryParams['code']", $code); } if ($code !== null && $error !== null) { throw new Dropbox_WebAuthException_BadRequest("Query parameters 'code' and 'error' are both set;" . " only one must be set."); } if ($code === null && $error === null) { throw new Dropbox_WebAuthException_BadRequest("Neither query parameter 'code' or 'error' is set."); } // Check CSRF token if ($csrfTokenFromSession === null) { throw new Dropbox_WebAuthException_BadState(); } $splitPos = strpos($state, "|"); if ($splitPos === false) { $givenCsrfToken = $state; $urlState = null; } else { $givenCsrfToken = substr($state, 0, $splitPos); $urlState = substr($state, $splitPos + 1); } if (!Dropbox_Security::stringEquals($csrfTokenFromSession, $givenCsrfToken)) { throw new Dropbox_WebAuthException_Csrf("Expected " . Dropbox_Client::q($csrfTokenFromSession) . ", got " . Dropbox_Client::q($givenCsrfToken) . "."); } $this->csrfTokenStore->clear(); // Check for error identifier if ($error !== null) { if ($error === 'access_denied') { // When the user clicks "Deny". if ($errorDescription === null) { throw new Dropbox_WebAuthException_NotApproved("No additional description from Dropbox."); } else { throw new Dropbox_WebAuthException_NotApproved("Additional description from Dropbox: {$errorDescription}"); } } else { // All other errors. $fullMessage = $error; if ($errorDescription !== null) { $fullMessage .= ": "; $fullMessage .= $errorDescription; } throw new Dropbox_WebAuthException_Provider($fullMessage); } } // If everything went ok, make the network call to get an access token. list($accessToken, $userId) = $this->_finish($code, $this->redirectUri); return array($accessToken, $userId, $urlState); }
/** * @param int $maxRetries * The number of times to retry it the action if it fails with one of the transient * API errors. A value of 1 means we'll try the action once and if it fails, we * will retry once. * * @param callable $action * The the action you want to retry. * * @return mixed * Whatever is returned by the $action callable. */ public static function runWithRetry($maxRetries, Dropbox_Closure_ReRunnableActionInterface $action) { Dropbox_Checker::argNat("maxRetries", $maxRetries); $retryDelay = 1; $numRetries = 0; while (true) { try { return $action->run(); } catch (Dropbox_Exception_NetworkIO $ex) { $savedEx = $ex; } catch (Dropbox_Exception_ServerError $ex) { $savedEx = $ex; } catch (Dropbox_Exception_RetryLater $ex) { $savedEx = $ex; } // We maxed out our retries. Propagate the last exception we got. if ($numRetries >= $maxRetries) { throw $savedEx; } $numRetries++; sleep($retryDelay); $retryDelay *= 2; // Exponential back-off. } throw new RuntimeException("unreachable"); }