diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0db5431 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +tests/reports +build +.DS_Store +*.log + +# IDE +.project +.settings +.buildpath +*.bak + +# Composer +vendor +composer.lock diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..ad030e5 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,41 @@ +PAYPAL, INC. + +SDK LICENSE + +NOTICE TO USER: PayPal, Inc. is providing the Software and Documentation for use under the terms of this Agreement. Any use, reproduction, modification or distribution of the Software or Documentation, or any derivatives or portions hereof, constitutes your acceptance of this Agreement. + +As used in this Agreement, "PayPal" means PayPal, Inc. "Software" means the software code accompanying this agreement. "Documentation" means the documents, specifications and all other items accompanying this Agreement other than the Software. + +1. LICENSE GRANT Subject to the terms of this Agreement, PayPal hereby grants you a non-exclusive, worldwide, royalty free license to use, reproduce, prepare derivative works from, publicly display, publicly perform, distribute and sublicense the Software for any purpose, provided the copyright notice below appears in a conspicuous location within the source code of the distributed Software and this license is distributed in the supporting documentation of the Software you distribute. Furthermore, you must comply with all third party licenses in order to use the third party software contained in the Software. + +Subject to the terms of this Agreement, PayPal hereby grants you a non-exclusive, worldwide, royalty free license to use, reproduce, publicly display, publicly perform, distribute and sublicense the Documentation for any purpose. You may not modify the Documentation. + +No title to the intellectual property in the Software or Documentation is transferred to you under the terms of this Agreement. You do not acquire any rights to the Software or the Documentation except as expressly set forth in this Agreement. + +If you choose to distribute the Software in a commercial product, you do so with the understanding that you agree to defend, indemnify and hold harmless PayPal and its suppliers against any losses, damages and costs arising from the claims, lawsuits or other legal actions arising out of such distribution. You may distribute the Software in object code form under your own license, provided that your license agreement: + +(a) complies with the terms and conditions of this license agreement; + +(b) effectively disclaims all warranties and conditions, express or implied, on behalf of PayPal; + +(c) effectively excludes all liability for damages on behalf of PayPal; + +(d) states that any provisions that differ from this Agreement are offered by you alone and not PayPal; and + +(e) states that the Software is available from you or PayPal and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. + +2. DISCLAIMER OF WARRANTY +PAYPAL LICENSES THE SOFTWARE AND DOCUMENTATION TO YOU ONLY ON AN "AS IS" BASIS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. PAYPAL MAKES NO WARRANTY THAT THE SOFTWARE OR DOCUMENTATION WILL BE ERROR-FREE. Each user of the Software or Documentation is solely responsible for determining the appropriateness of using and distributing the Software and Documentation and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs, or equipment, and unavailability or interruption of operations. Use of the Software and Documentation is made with the understanding that PayPal will not provide you with any technical or customer support or maintenance. Some states or jurisdictions do not allow the exclusion of implied warranties or limitations on how long an implied warranty may last, so the above limitations may not apply to you. To the extent permissible, any implied warranties are limited to ninety (90) days. + + +3. LIMITATION OF LIABILITY +PAYPAL AND ITS SUPPLIERS SHALL NOT BE LIABLE FOR LOSS OR DAMAGE ARISING OUT OF THIS AGREEMENT OR FROM THE USE OF THE SOFTWARE OR DOCUMENTATION. IN NO EVENT WILL PAYPAL OR ITS SUPPLIERS BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, INCIDENTAL, OR SPECIAL DAMAGES INCLUDING LOST PROFITS, LOST SAVINGS, COSTS, FEES, OR EXPENSES OF ANY KIND ARISING OUT OF ANY PROVISION OF THIS AGREEMENT OR THE USE OR THE INABILITY TO USE THE SOFTWARE OR DOCUMENTATION, HOWEVER CAUSED AND UNDER ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. PAYPAL'S AGGREGATE LIABILITY AND THAT OF ITS SUPPLIERS UNDER OR IN CONNECTION WITH THIS AGREEMENT SHALL BE LIMITED TO THE AMOUNT PAID BY YOU FOR THE SOFTWARE AND DOCUMENTATION. + +4. TRADEMARK USAGE +PayPal is a trademark PayPal, Inc. in the United States and other countries. Such trademarks may not be used to endorse or promote any product unless expressly permitted under separate agreement with PayPal. + +5. TERM +Your rights under this Agreement shall terminate if you fail to comply with any of the material terms or conditions of this Agreement and do not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all your rights under this Agreement terminate, you agree to cease use and distribution of the Software and Documentation as soon as reasonably practicable. + +6. GOVERNING LAW AND JURISDICTION. This Agreement is governed by the statutes and laws of the State of California, without regard to the conflicts of law principles thereof. If any part of this Agreement is found void and unenforceable, it will not affect the validity of the balance of the Agreement, which shall remain valid and enforceable according to its terms. Any dispute arising out of or related to this Agreement shall be brought in the courts of Santa Clara County, California, USA. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e8b83b9 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ + +PayPal Core SDK - V1.2.0 +======================== + +Prerequisites +------------- + + * PHP 5.2 and above + * curl extension with support for OpenSSL + * PHPUnit 3.5 for running test suite (Optional) + * Composer (Optional - for running test cases) diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..0496420 --- /dev/null +++ b/build.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..af70d78 --- /dev/null +++ b/composer.json @@ -0,0 +1,21 @@ +{ + "name": "paypal/sdk-core-php", + "description": "PayPal Core SDK for PHP", + "keywords": ["paypal", "php", "sdk"], + "type": "library", + "homepage": "https://github.com/paypal/sdk-core-php", + "license": "Apache2", + "authors": [ + { + "name": "PayPal", + "homepage": "https://github.com/paypal/sdk-core-php/contributors" + } + ], + "require": { + "php": ">=5.2.0", + "ext-curl": "*" + }, + "autoload": { + "classmap": ["lib"] + } +} \ No newline at end of file diff --git a/lib/PPAPIService.php b/lib/PPAPIService.php new file mode 100644 index 0000000..7ef80a2 --- /dev/null +++ b/lib/PPAPIService.php @@ -0,0 +1,82 @@ +serviceName = $serviceName; + $config = PPConfigManager::getInstance(); + if($port!= null) + { + $this->endpoint = $config->get('service.EndPoint.'.$port); + } + // for backward compatibilty (for those who are using old config files with 'service.EndPoint') + else + { + $this->endpoint = $config->get('service.EndPoint'); + } + + $this->logger = new PPLoggingManager(__CLASS__); + $this->handlers = $handlers; + $this->serviceBinding = $serviceBinding; + } + + public function setServiceName($serviceName) { + $this->serviceName = $serviceName; + } + + public function addHandler($handler) { + $this->handlers[] = $handler; + } + + public function makeRequest($apiMethod, $params, $apiUsername = null, $accessToken = null, $tokenSecret = null) { + + $config = PPConfigManager::getInstance(); + if(is_string($apiUsername) || is_null($apiUsername)) { + // $apiUsername is optional, if null the default account in config file is taken + $credMgr = PPCredentialManager::getInstance(); + $apiCredential = clone($credMgr->getCredentialObject($apiUsername )); + } else { + $apiCredential = $apiUsername; //TODO: Aargh + } + if(isset($accessToken) && isset($tokenSecret)) { + $apiCredential->setThirdPartyAuthorization( + new PPTokenAuthorization($accessToken, $tokenSecret)); + } + + if($this->serviceBinding == 'SOAP' ) { + $url = $this->endpoint; + } else { + $url = $this->endpoint . $this->serviceName . '/' . $apiMethod; + } + + $request = new PPRequest($params, $this->serviceBinding); + $request->setCredential($apiCredential); + $httpConfig = new PPHttpConfig($url, PPHttpConfig::HTTP_POST); + $this->runHandlers($httpConfig, $request); + + $formatter = FormatterFactory::factory($this->serviceBinding); + $payload = $formatter->toString($request); + $connection = PPConnectionManager::getInstance()->getConnection($httpConfig); + $this->logger->info("Request: $payload"); + $response = $connection->execute($payload); + $this->logger->info("Response: $response"); + + return array('request' => $payload, 'response' => $response); + } + + private function runHandlers($httpConfig, $request) { + $handler = new PPAuthenticationHandler(); + $handler->handle($httpConfig, $request); + foreach($this->handlers as $handlerClass) { + $handler = new $handlerClass(); + $handler->handle($httpConfig, $request); + } + } + +} diff --git a/lib/PPBaseService.php b/lib/PPBaseService.php new file mode 100644 index 0000000..e5c8d8f --- /dev/null +++ b/lib/PPBaseService.php @@ -0,0 +1,114 @@ +lastRequest; + } + public function setLastRequest($lastRqst) { + $this->lastRequest = $lastRqst; + } + public function getLastResponse() { + return $this->lastResponse; + } + public function setLastResponse($lastRspns) { + $this->lastResponse = $lastRspns; + } + + public function getAccessToken() { + return $this->accessToken; + } + /** + * @deprecated + * For using third party token permissions, + * create a ICredential object and pass it to the + * call() method instead. + * + *
+	 * $service = new *Service();
+	 * $cred = new PPSignatureCredential("username", "password", "signature");
+	 * $cred->setThirdPartyAuthorization(new PPTokenAuthorization("accessToken", "tokenSecret"));
+	 * $service->SomeOperation($reqObject, $cred); 
+	 *
+ */ + public function setAccessToken($accessToken) { + $this->accessToken = $accessToken; + } + public function getTokenSecret() { + return $this->tokenSecret; + } + /** + * @deprecated + * For using third party token permissions, + * create a ICredential object and pass it to the + * call() method instead. + * + *
+	 * $service = new *Service();
+	 * $cred = new PPSignatureCredential("username", "password", "signature");
+	 * $cred->setThirdPartyAuthorization(new PPTokenAuthorization("accessToken", "tokenSecret"));
+	 * $service->SomeOperation($reqObject, $cred); 
+	 *
+ */ + public function setTokenSecret($tokenSecret) { + $this->tokenSecret = $tokenSecret; + } + + public function __construct($serviceName, $serviceBinding, $handlers=array()) { + $this->serviceName = $serviceName; + $this->serviceBinding = $serviceBinding; + $this->handlers = $handlers; + } + + public function getServiceName() { + return $this->serviceName; + } + + /** + * + * @param string $method - API method to call + * @param object $requestObject Request object + * @param mixed $apiCredential - Optional API credential - can either be + * a username configured in sdk_config.ini or a ICredential object + * created dynamically + */ + public function call($port, $method, $requestObject, $apiCredential = null) { + $service = new PPAPIService($port, $this->serviceName, + $this->serviceBinding, $this->handlers); + $ret = $service->makeRequest($method, $requestObject, $apiCredential, + $this->accessToken, $this->tokenSecret); + $this->lastRequest = $ret['request']; + $this->lastResponse = $ret['response']; + return $this->lastResponse; + } +} \ No newline at end of file diff --git a/lib/PPConfigManager.php b/lib/PPConfigManager.php new file mode 100644 index 0000000..351c135 --- /dev/null +++ b/lib/PPConfigManager.php @@ -0,0 +1,94 @@ +load($configFile); + } + + // create singleton object for PPConfigManager + public static function getInstance() + { + if ( !isset(self::$instance) ) { + self::$instance = new PPConfigManager(); + } + return self::$instance; + } + + //used to load the file + private function load($fileName) { + + $this->config = @parse_ini_file($fileName); + if($this->config == NULL || count($this->config) == 0) { + throw new PPConfigurationException("Config file $fileName not found","303"); + } + } + + /** + * simple getter for configuration params + * If an exact match for key is not found, + * does a "contains" search on the key + */ + public function get($searchKey){ + + if(array_key_exists($searchKey, $this->config)) + { + return $this->config[$searchKey]; + } + else { + $arr = array(); + foreach ($this->config as $k => $v){ + if(strstr($k, $searchKey)){ + $arr[$k] = $v; + } + } + + return $arr; + } + + } + + /** + * Utility method for handling account configuration + * return config key corresponding to the API userId passed in + * + * If $userId is null, returns config keys corresponding to + * all configured accounts + */ + public function getIniPrefix($userId = null) { + + if($userId == null) { + $arr = array(); + foreach ($this->config as $key => $value) { + $pos = strpos($key, '.'); + if(strstr($key, "acct")){ + $arr[] = substr($key, 0, $pos); + } + } + return array_unique($arr); + } else { + $iniPrefix = array_search($userId, $this->config); + $pos = strpos($iniPrefix, '.'); + $acct = substr($iniPrefix, 0, $pos); + + return $acct; + } + } +} \ No newline at end of file diff --git a/lib/PPConnectionManager.php b/lib/PPConnectionManager.php new file mode 100644 index 0000000..8982699 --- /dev/null +++ b/lib/PPConnectionManager.php @@ -0,0 +1,40 @@ +get("http.ConnectionTimeOut")) ) { + $httpConfig->setHttpTimeout( $configMgr->get("http.ConnectionTimeOut") ); + } + if( $configMgr->get("http.Proxy") ) { + $httpConfig->setHttpProxy( $configMgr->get("http.Proxy") ); + } + if( $configMgr->get("http.Retry") ) { + $retry = $configMgr->get("http.Retry"); + $httpConfig->setHttpRetryCount($retry ) ; + } + + return new PPHttpConnection($httpConfig); + } + +} diff --git a/lib/PPCredentialManager.php b/lib/PPCredentialManager.php new file mode 100644 index 0000000..1a40882 --- /dev/null +++ b/lib/PPCredentialManager.php @@ -0,0 +1,124 @@ +initCredential(); + } catch (Exception $e) { + $this->credentialHashmap = array(); + throw $e; + } + } + + /* + * Create singleton instance for this class. + */ + public static function getInstance() + { + if (!isset(self::$instance)) { + self::$instance = new PPCredentialManager(); + } + return self::$instance; + } + + /* + * Load credentials for multiple accounts, with priority given to Signature credential. + */ + private function initCredential(){ + $configMgr = PPConfigManager::getInstance(); + $suffix = 1; + $prefix = "acct"; + + $credArr = $configMgr->get($prefix); + $arrayPartKeys = $configMgr->getIniPrefix(); + if(count($arrayPartKeys) == 0) + throw new PPMissingCredentialException("No valid API accounts have been configured"); + + $key = $prefix.$suffix; + while (in_array($key, $arrayPartKeys)){ + + if(isset($credArr[$key.".Signature"]) + && $credArr[$key.".Signature"] != null && $credArr[$key.".Signature"] != ""){ + + $userName = isset($credArr[$key.'.UserName']) ? $credArr[$key.'.UserName'] : ""; + $password = isset($credArr[$key.'.Password']) ? $credArr[$key.'.Password'] : ""; + $signature = isset($credArr[$key.'.Signature']) ? $credArr[$key.'.Signature'] : ""; + + $this->credentialHashmap[$userName] = new PPSignatureCredential($userName, $password, $signature); + if (isset($credArr[$key.'.AppId'])) { + $this->credentialHashmap[$userName]->setApplicationId($credArr[$key.'.AppId']); + } + + } elseif (isset($credArr[$key.".CertPath"]) + && $credArr[$key.".CertPath"] != null && $credArr[$key.".CertPath"] != ""){ + + $userName = isset($credArr[$key.'.UserName']) ? $credArr[$key.'.UserName'] : ""; + $password = isset($credArr[$key.'.Password']) ? $credArr[$key.'.Password'] : ""; + $certPassPhrase = isset($credArr[$key.'.CertKey']) ? $credArr[$key.'.CertKey'] : ""; + $certPath = isset($credArr[$key.'.CertPath']) ? $credArr[$key.'.CertPath'] : ""; + + $this->credentialHashmap[$userName] = new PPCertificateCredential($userName, $password, $certPath, $certPassPhrase); + if (isset($credArr[$key.'.AppId'])) { + $this->credentialHashmap[$userName]->setApplicationId($credArr[$key.'.AppId']); + } + } elseif (isset($credArr[$key.".ClientId"]) && isset($credArr[$key.".ClientId"]) ){ + $userName = $key; + $this->credentialHashmap[$userName] = array('clientId' => $credArr[$key.".ClientId"], + 'clientSecret' => $credArr[$key.".ClientSecret"]); + } + if($userName && isset($credArr[$key . ".Subject"]) && trim($credArr[$key . ".Subject"]) != "" ) { + $this->credentialHashmap[$userName]->setThirdPartyAuthorization( + new PPSubjectAuthorization($credArr[$key . ".Subject"])); + } + + if ($userName && $this->defaultAccountName == null) { + if(array_key_exists($key. '.UserName', $credArr)) { + $this->defaultAccountName = $credArr[$key . '.UserName']; + } else { + $this->defaultAccountName = $key; + } + } + $suffix++; + $key = $prefix.$suffix; + } + + } + + /* + * Obtain Credential Object based on UserId provided. + */ + public function getCredentialObject($userId = null){ + + if($userId == null) { + $credObj = $this->credentialHashmap[$this->defaultAccountName]; + } else if (array_key_exists($userId, $this->credentialHashmap)) { + $credObj = $this->credentialHashmap[$userId]; + } + + if (empty($credObj)) { + throw new PPInvalidCredentialException("Invalid userId $userId"); + } + return $credObj; + } + + + public function __clone() + { + trigger_error('Clone is not allowed.', E_USER_ERROR); + } + +} \ No newline at end of file diff --git a/lib/PPHttpConfig.php b/lib/PPHttpConfig.php new file mode 100644 index 0000000..12600e6 --- /dev/null +++ b/lib/PPHttpConfig.php @@ -0,0 +1,152 @@ + 3, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_RETURNTRANSFER => TRUE, + CURLOPT_TIMEOUT => 60, // maximum number of seconds to allow cURL functions to execute + CURLOPT_USERAGENT => 'PayPal-PHP-SDK', + CURLOPT_HTTPHEADER => array(), + CURLOPT_SSL_VERIFYHOST => 2, + CURLOPT_SSL_VERIFYPEER => 1 + ); + + const HEADER_SEPARATOR = ';'; + const HTTP_GET = 'GET'; + const HTTP_POST = 'POST'; + + private $headers = array(); + + private $curlOptions; + + private $url; + + private $method; + /*** + * Number of times to retry a failed HTTP call + */ + private $retryCount; + + /** + * + * @param string $url + * @param string $method HTTP method (GET, POST etc) defaults to POST + */ + public function __construct($url, $method=self::HTTP_POST) { + $this->url = $url; + $this->method = $method; + $this->curlOptions = self::$DEFAULT_CURL_OPTS; + } + + public function getUrl() { + return $this->url; + } + + public function getMethod() { + return $this->method; + } + + public function getHeaders() { + return $this->headers; + } + + public function getHeader($name) { + if(array_key_exists($name, $this->headers)) { + return $this->headers[$name]; + } + return NULL; + } + + public function setHeaders(array $headers) { + $this->headers = $headers; + } + + public function addHeader($name, $value, $overWrite=true) { + if(!array_key_exists($name, $this->headers) || $overWrite) { + $this->headers[$name] = $value; + } else { + $this->headers[$name] = $this->headers[$name] . HEADER_SEPARATOR . $value; + } + } + + public function removeHeader($name) { + unset($this->headers[$name]); + } + + + + public function getCurlOptions() { + return $this->curlOptions; + } + + public function addCurlOption($name, $value) { + $this->curlOptions[$name] = $value; + } + + public function setCurlOptions($options) { + $this->curlOptions = $options; + } + + + + /** + * Set ssl parameters for certificate based client authentication + * + * @param string $certPath - path to client certificate file (PEM formatted file) + */ + public function setSSLCert($certPath, $passPhrase=NULL) { + $this->curlOptions[CURLOPT_SSLCERT] = realpath($certPath); + if(isset($passPhrase) && trim($passPhrase) != "") { + $this->curlOptions[CURLOPT_SSLCERTPASSWD] = $passPhrase; + } + } + + /** + * Set connection timeout in seconds + * @param integer $timeout + */ + public function setHttpTimeout($timeout) { + $this->curlOptions[CURLOPT_CONNECTTIMEOUT] = $timeout; + } + + /** + * Set HTTP proxy information + * @param string $proxy + * @throws PPConfigurationException + */ + public function setHttpProxy($proxy) { + $urlParts = parse_url($proxy); + if($urlParts == false || !array_key_exists("host", $urlParts)) + throw new PPConfigurationException("Invalid proxy configuration ".$proxy); + + $this->curlOptions[CURLOPT_PROXY] = $urlParts["host"]; + if(isset($urlParts["port"])) + $this->curlOptions[CURLOPT_PROXY] .= ":" . $urlParts["port"]; + if(isset($urlParts["user"])) + $this->curlOptions[URLOPT_PROXYUSERPWD] = $urlParts["user"] . ":" . $urlParts["pass"]; + } + + /** + * @param integer $retry + */ + public function setHttpRetryCount($retryCount) { + $this->retryCount = $retryCount; + } + + public function getHttpRetryCount() { + return $this->retryCount; + } + + /** + * Sets the User-Agent string on the HTTP request + * @param string $userAgentString + */ + public function setUserAgent($userAgentString) { + $this->curlOptions[CURLOPT_USERAGENT] = $userAgentString; + } +} diff --git a/lib/PPHttpConnection.php b/lib/PPHttpConnection.php new file mode 100644 index 0000000..cce6342 --- /dev/null +++ b/lib/PPHttpConnection.php @@ -0,0 +1,108 @@ +httpConfig = $httpConfig; + $this->logger = new PPLoggingManager(__CLASS__); + } + + private function getHttpHeaders() { + + $ret = array(); + foreach($this->httpConfig->getHeaders() as $k=>$v) { + $ret[] = "$k: $v"; + } + return $ret; + } + + /** + * Executes an HTTP request + * + * @param string $data query string OR POST content as a string + * @throws PPConnectionException + */ + public function execute($data) { + $this->logger->fine("Connecting to " . $this->httpConfig->getUrl()); + $this->logger->fine("Payload " . $data); + + $ch = curl_init($this->httpConfig->getUrl()); + curl_setopt_array($ch, $this->httpConfig->getCurlOptions()); + curl_setopt($ch, CURLOPT_URL, $this->httpConfig->getUrl()); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, $this->getHttpHeaders()); + + switch($this->httpConfig->getMethod()) { + case 'POST': + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + break; + } + if($this->httpConfig->getMethod() != NULL) { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->httpConfig->getMethod()); + } + foreach($this->getHttpHeaders() as $header) { + //TODO: Strip out credentials and other secure info when logging. + $this->logger->info("Adding header $header"); + } + $result = curl_exec($ch); + if (curl_errno($ch) == 60) { + $this->logger->info("Invalid or no certificate authority found - Retrying using bundled CA certs file"); + curl_setopt($ch, CURLOPT_CAINFO, + dirname(__FILE__) . '/cacert.pem'); + $result = curl_exec($ch); + } + $httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $retries = 0; + if(in_array($httpStatus, self::$retryCodes) && $this->httpConfig->getHttpRetryCount() != null) { + $this->logger->info("Got $httpStatus response from server. Retrying"); + + do { + $result = curl_exec($ch); + $httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE); + } while (in_array($httpStatus, self::$retryCodes) && (++$retries < $this->httpConfig->getHttpRetryCount()) ); + } + if ( curl_errno($ch) ) { + $ex = new PPConnectionException($this->httpConfig->getUrl(), curl_error($ch), curl_errno($ch)); + curl_close($ch); + throw $ex; + } + + curl_close($ch); + + if(in_array($httpStatus, self::$retryCodes)) { + $ex = new PPConnectionException($this->httpConfig->getUrl() , + "Got Http response code $httpStatus when accessing {$this->httpConfig->getUrl()}. Retried $retries times."); + $ex->setData($result); + throw $ex; + } else if($httpStatus < 200 || $httpStatus >=300) { + $ex = new PPConnectionException($this->httpConfig->getUrl() , + "Got Http response code $httpStatus when accessing {$this->httpConfig->getUrl()}."); + $ex->setData($result); + throw $ex; + } + return $result; + } + +} diff --git a/lib/PPLoggingManager.php b/lib/PPLoggingManager.php new file mode 100644 index 0000000..c2d1429 --- /dev/null +++ b/lib/PPLoggingManager.php @@ -0,0 +1,74 @@ +loggerName = $loggerName; + $config = PPConfigManager::getInstance(); + $this->loggerFile = ($config->get('log.FileName')) ? $config->get('log.FileName') : ini_get('error_log'); + $loggingEnabled = $config->get('log.LogEnabled'); + $this->isLoggingEnabled = (isset($loggingEnabled)) ? $loggingEnabled : false; + $loggingLevel = strtoupper($config->get('log.LogLevel')); + $this->loggingLevel = (isset($loggingLevel) && defined("PPLoggingLevel::$loggingLevel")) ? constant("PPLoggingLevel::$loggingLevel") : PPLoggingManager::DEFAULT_LOGGING_LEVEL; + + } + + public function log($message, $level=PPLoggingLevel::INFO) { + if($this->isLoggingEnabled && ($level <= $this->loggingLevel)) { + error_log( $this->loggerName . ": $message\n", 3, $this->loggerFile); + } + } + + public function error($message) { + $this->log($message, PPLoggingLevel::ERROR); + } + + public function warning($message) { + $this->log($message, PPLoggingLevel::WARN); + } + + public function info($message) { + $this->log($message, PPLoggingLevel::INFO); + } + + public function fine($message) { + $this->log($message, PPLoggingLevel::FINE); + } + +} \ No newline at end of file diff --git a/lib/PPMessage.php b/lib/PPMessage.php new file mode 100644 index 0000000..1fe6057 --- /dev/null +++ b/lib/PPMessage.php @@ -0,0 +1,116 @@ + $defaultValue) { + + if (($propertyValue = $this->{$property}) === NULL || $propertyValue == NULL) { + continue; + } + + if (is_object($propertyValue)) { + $nvp[] = $propertyValue->toNVPString($prefix . $property . '.'); // prefix + + } elseif (is_array($defaultValue) || is_array($propertyValue)) { + foreach (array_values($propertyValue) as $i => $item) { + if (!is_object($item)){ + $nvp[] = $prefix . $property . "($i)" . '=' . urlencode($item); + }else{ + $nvp[] = $item->toNVPString($prefix . $property . "($i)."); + } + } + + } else { + // Handle classes with attributes + if($property == 'value' && ($anno = PPUtils::propertyAnnotations($this, $property)) != NULL && isset($anno['value']) ) { + $nvpKey = substr($prefix, 0, -1); // Remove the ending '.' + } else { + $nvpKey = $prefix . $property ; + } + $nvp[] = $nvpKey . '=' . urlencode($propertyValue); + } + } + + return implode('&', $nvp); + } + + + + /** + * @param array $map + * @param string $prefix + */ + public function init(array $map = array(), $prefix = '') + { + if (empty($map)) { + return; + } + + $map = PPUtils::lowerKeys($map); + + foreach (get_object_vars($this) as $property => $defaultValue) { + if (array_key_exists($propKey = strtolower($prefix . $property), $map) && + $this->isBuiltInType(($type = PPUtils::propertyType($this, $property)))){ + $type = PPUtils::propertyType($this, $property); + $this->{$property} = urldecode($map[$propKey]); + continue; // string + + } elseif (!$filtered = PPUtils::filterKeyPrefix($map, $propKey)) { + continue; // NULL + } + + if (!class_exists($type = PPUtils::propertyType($this, $property)) && !$this->isBuiltInType($type)) { + trigger_error("Class $type not found.", E_USER_NOTICE); + continue; // just ignore + } + + if (is_array($defaultValue) || PPUtils::isPropertyArray($this, $property)) { // array of objects + if($this->isBuiltInType($type)) { // Array of simple types + foreach($filtered as $key => $value) { + $this->{$property}[trim($key, "()")] = urldecode($value); + } + } else { // Array of complex objects + $delim = '.'; + for ($i = 0; $itemValues = PPUtils::filterKeyPrefix($filtered, "($i)") ;$i++) { + $this->{$property}[$i] = $item = new $type(); + $item->init(PPUtils::filterKeyPrefix($itemValues, ".")); + if(array_key_exists("", $itemValues)) { + $item->value = urldecode($itemValues[""]); + } + } + // Handle cases where we have a list of objects + // with just the value present and all attributes values are null + foreach($filtered as $key => $value) { + $idx = trim($key, "()"); + if(is_numeric($idx) && (is_null($this->{$property}) || !array_key_exists($idx, $this->{$property})) ) { + $this->{$property}[$idx] = new $type; + $this->{$property}[$idx]->value = urldecode($value); + } + } + } + } else { // one object + $this->{$property} = new $type(); + $this->{$property}->init(PPUtils::filterKeyPrefix($filtered, '.')); // unprefix + if(array_key_exists("", $filtered)) { + $this->{$property}->value = urldecode($filtered[""]); + } + } + } + } + + private function isBuiltInType($typeName) { + static $types = array('string', 'int', 'integer', 'bool', 'boolean', 'float', 'decimal', 'long', 'datetime', 'double'); + return in_array(strtolower($typeName), $types); + } +} diff --git a/lib/PPRequest.php b/lib/PPRequest.php new file mode 100644 index 0000000..d503793 --- /dev/null +++ b/lib/PPRequest.php @@ -0,0 +1,72 @@ +requestObject = $requestObject; + $this->bindingType = $bindingType; + } + + public function getRequestObject() { + return $this->requestObject; + } + + public function getBindingType() { + return $this->bindingType; + } + + public function getBindingInfo($name=NULL) { + if(isset($name)) { + return $this->bindingInfo[$name]; + } + return $this->bindingInfo; + } + + /** + * + * @param string $name + * @param mixed $value + */ + public function addBindingInfo($name, $value) { + $this->bindingInfo[$name] = $value; + } + + public function setCredential($credential) { + $this->credential = $credential; + } + + public function getCredential() { + return $this->credential; + } +} \ No newline at end of file diff --git a/lib/PPUtils.php b/lib/PPUtils.php new file mode 100644 index 0000000..c324baf --- /dev/null +++ b/lib/PPUtils.php @@ -0,0 +1,398 @@ + $v) { + preg_match($pattern, $k, $matches); + if (count($matches) > 0) { + return true; + } + } + return false; + } + + + + /** + * Get the local IP address. The client address is a required + * request parameter for some API calls + */ + public static function getLocalIPAddress() + { + if (array_key_exists("SERVER_ADDR", $_SERVER)) { + // SERVER_ADDR is available only if we are running the CGI SAPI + return $_SERVER['SERVER_ADDR']; + + } else { + if (function_exists("gethostname")) { + // gethostname is available only in PHP >= v5.3 + return gethostbyname(gethostname()); + + } else { + // fallback if nothing works + return "127.0.0.1"; + } + } + } + + public static function xmlToArray($xmlInput) + { + $xml = simplexml_load_string($xmlInput); + + $ns = $xml->getNamespaces(true); + $soap = $xml->children($ns['SOAP-ENV']); + $getChild = $soap->Body->children(); + $array = array(); + $ret = PPUtils::convertXmlObjToArr($getChild, $array); + return $ret; + } + + + + private static function convertXmlObjToArr($obj, &$arr) + { + $children = $obj->children(); + foreach ($children as $elementName => $node) { + $nextIdx = count($arr); + $arr[$nextIdx] = array(); + $arr[$nextIdx]['name'] = strtolower((string)$elementName); + $arr[$nextIdx]['attributes'] = array(); + $attributes = $node->attributes(); + foreach ($attributes as $attributeName => $attributeValue) { + $attribName = strtolower(trim((string)$attributeName)); + $attribVal = trim((string)$attributeValue); + $arr[$nextIdx]['attributes'][$attribName] = $attribVal; + } + $text = (string)$node; + $text = trim($text); + if (strlen($text) > 0) { + $arr[$nextIdx]['text'] = $text; + } + $arr[$nextIdx]['children'] = array(); + PPutils::convertXmlObjToArr($node, $arr[$nextIdx]['children']); + } + return $arr; + } + + + + /** + * Escapes invalid xml characters + * + * @param $textContent = xml data to be escaped + * @return string + */ + public static function escapeInvalidXmlCharsRegex($textContent) + { + return htmlspecialchars($textContent, (1 | 2), 'UTF-8', false); + } + + + + /** + * @param array $map + * @param string $keyPrefix + * @return array + */ + public static function filterKeyPrefix(array $map, $keyPrefix) + { + $filtered = array(); + foreach ($map as $key => $val) { + if (($pos = stripos($key, $keyPrefix)) !== 0) { + continue; + } + + $filtered[substr_replace($key, '', 0, strlen($keyPrefix))] = $val; + } + + return $filtered; + } + + + + /** + * @var array|ReflectionProperty[] + */ + private static $propertiesRefl = array(); + + /** + * @var array|string[] + */ + private static $propertiesType = array(); + + + + /** + * @param string $class + * @param string $propertyName + * @throws RuntimeException + * @return string + */ + public static function propertyAnnotations($class, $propertyName) + { + $class = is_object($class) ? get_class($class) : $class; + if (!class_exists('ReflectionProperty')) { + throw new RuntimeException("Property type of " . $class . "::{$propertyName} cannot be resolved"); + } + + if ($annotations =& self::$propertiesType[$class][$propertyName]) { + return $annotations; + } + + if (!($refl =& self::$propertiesRefl[$class][$propertyName])) { + $refl = new ReflectionProperty($class, $propertyName); + } + + // todo: smarter regexp + if (!preg_match_all('~\@([^\s@\(]+)[\t ]*(?:\(?([^\n@]+)\)?)?~i', $refl->getDocComment(), $annots, PREG_PATTERN_ORDER)) { + return NULL; + } + foreach ($annots[1] as $i => $annot) { + $annotations[strtolower($annot)] = empty($annots[2][$i]) ? TRUE : rtrim($annots[2][$i], " \t\n\r)"); + } + + return $annotations; + } + + /** + * @param string $class + * @param string $propertyName + * @return string + */ + public static function isAttributeProperty($class, $propertyName) { + if (($annotations = self::propertyAnnotations($class, $property))) { + return $annotations['attribute']; + } + return FALSE; + } + + /** + * @param string $class + * @param string $propertyName + * @return string + */ + public static function isPropertyArray($class, $propertyName) { + if (($annotations = self::propertyAnnotations($class, $propertyName))) { + if (isset($annotations['var']) && substr($annotations['var'], -2) === '[]') { + return TRUE; + + } elseif (isset($annotations['array'])) { + return TRUE; + } + } + + return FALSE; + } + + + + /** + * @param string $class + * @param string $propertyName + * @throws RuntimeException + * @return string + */ + public static function propertyType($class, $propertyName) + { + if (($annotations = self::propertyAnnotations($class, $propertyName)) && isset($annotations['var'])) { + if (substr($annotations['var'], -2) === '[]') { + return substr($annotations['var'], 0, -2); + } + + return $annotations['var']; + } + + return 'string'; + } + + + + /** + * @param object $object + * @return array + */ + public static function objectProperties($object) + { + $props = array(); + foreach (get_object_vars($object) as $property => $default) { + $annotations = self::propertyAnnotations($object, $property); + if (isset($annotations['name'])) { + $props[strtolower($annotations['name'])] = $property; + } + + $props[strtolower($property)] = $property; + } + + return $props; + } + + + + /** + * @param array $array + * @return array + */ + public static function lowerKeys(array $array) + { + $ret = array(); + foreach ($array as $key => $value) { + $ret[strtolower($key)] = $value; + } + + return $ret; + } + +} + + + +/** + * XMLToArray Generator Class + * + * @author : MA Razzaque Rupom , + * Moderator, phpResource (LINK1http://groups.yahoo.com/group/phpresource/LINK1) + * URL: LINK2http://www.rupom.infoLINK2 + * @version : 1.0 + * @date 06/05/2006 + * Purpose : Creating Hierarchical Array from XML Data + * Released : Under GPL + */ +class XmlToArray +{ + + var $xml = ''; + + + + /** + * Default Constructor + * + * @param $xml = xml data + * @return none + */ + function XmlToArray($xml) + { + $this->xml = $xml; + } + + + + /** + * _struct_to_array($values, &$i) + * + * This is adds the contents of the return xml into the array for easier processing. + * Recursive, Static + * + * @access private + * @param array $values this is the xml data in an array + * @param int $i this is the current location in the array + * @return Array + */ + function _struct_to_array($values, &$i) + { + $child = array(); + if (isset($values[$i]['value'])) { + array_push($child, $values[$i]['value']); + } + + while ($i++ < count($values)) { + switch ($values[$i]['type']) { + case 'cdata': + array_push($child, $values[$i]['value']); + break; + + case 'complete': + $name = $values[$i]['tag']; + if (!empty($name)) { + $child[$name] = ($values[$i]['value']) ? ($values[$i]['value']) : ''; + if (isset($values[$i]['attributes'])) { + $child[$name] = $values[$i]['attributes']; + } + } + break; + + case 'open': + $name = $values[$i]['tag']; + $size = isset($child[$name]) ? sizeof($child[$name]) : 0; + $child[$name][$size] = $this->_struct_to_array($values, $i); + break; + + case 'close': + return $child; + break; + } + } + return $child; + } + + + + /** + * createArray($data) + * + * This is adds the contents of the return xml into the array for easier processing. + * + * @access public + * @return Array + */ + function createArray() + { + $xml = $this->xml; + $values = array(); + $index = array(); + $array = array(); + $parser = xml_parser_create(); + xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); + xml_parse_into_struct($parser, $xml, $values, $index); + xml_parser_free($parser); + $i = 0; + $name = $values[$i]['tag']; + $array[$name] = isset($values[$i]['attributes']) ? $values[$i]['attributes'] : ''; + $array[$name] = $this->_struct_to_array($values, $i); + return $array; + } + +} diff --git a/lib/PPXmlMessage.php b/lib/PPXmlMessage.php new file mode 100644 index 0000000..029fae8 --- /dev/null +++ b/lib/PPXmlMessage.php @@ -0,0 +1,186 @@ +toXMLString(); + } + + + + /** + * @return string + */ + public function toXMLString() + { + if (count($properties = get_object_vars($this)) >= 2 && array_key_exists('value', $properties)) { + $attributes = array(); + foreach (array_keys($properties) as $property) { + if ($property === 'value') continue; + if (($annots = PPUtils::propertyAnnotations($this, $property)) && isset($annots['attribute'])) { + if (($propertyValue = $this->{$property}) === NULL || $propertyValue == NULL) { + $attributes[] = NULL; + continue; + } + + $attributes[] = $property . '="' . PPUtils::escapeInvalidXmlCharsRegex($propertyValue) . '"'; + } + } + + if (count($attributes)) { + return implode(' ', $attributes) . '>' . PPUtils::escapeInvalidXmlCharsRegex($this->value); + } + } + + $xml = array(); + foreach ($properties as $property => $defaultValue) { + if (($propertyValue = $this->{$property}) === NULL || $propertyValue == NULL) { + continue; + } + + if (is_array($defaultValue) || is_array($propertyValue)) { + foreach ($propertyValue as $item) { + if (!is_object($item)) { + $xml[] = $this->buildProperty($property, $item); + }else{ + $xml[] = $this->buildProperty($property, $item); + } + } + + } else { + $xml[] = $this->buildProperty($property, $propertyValue); + } + } + + return implode($xml); + } + + + + /** + * @param string $property + * @param PPXmlMessage|string $value + * @param string $namespace + * @return string + */ + private function buildProperty($property, $value, $namespace = 'ebl') + { + $annotations = PPUtils::propertyAnnotations($this, $property); + if (!empty($annotations['namespace'])) { + $namespace = $annotations['namespace']; + } + if (!empty($annotations['name'])) { + $property = $annotations['name']; + } + + $el = '<' . $namespace . ':' . $property; + if (!is_object($value)) { + $el .= '>' . PPUtils::escapeInvalidXmlCharsRegex($value); + + } else { + if (substr($value = $value->toXMLString(), 0, 1) === '<' || $value=='') { + $el .= '>' . $value; + + } else { + $el .= ' ' . $value; + } + } + + return $el . ''; + } + + + + /** + * @param array $map + * @param string $prefix + */ + public function init(array $map = array(), $prefix = '') + { + if (empty($map)) { + return; + } + + if (($first = reset($map)) && !is_array($first) && !is_numeric(key($map))) { + parent::init($map, $prefix); + return; + } + + $propertiesMap = PPUtils::objectProperties($this); + $arrayCtr = array(); + foreach ($map as $element) { + + if (empty($element) || empty($element['name'])) { + continue; + + } elseif (!array_key_exists($property = strtolower($element['name']), $propertiesMap)) { + if (!preg_match('~^(.+)[\[\(](\d+)[\]\)]$~', $property, $m)) { + continue; + } + + $element['name'] = $m[1]; + $element['num'] = $m[2]; + } + $element['name'] = $propertiesMap[strtolower($element['name'])]; + if(PPUtils::isPropertyArray($this, $element['name'])) { + $arrayCtr[$element['name']] = isset($arrayCtr[$element['name']]) ? ($arrayCtr[$element['name']]+1) : 0; + $element['num'] = $arrayCtr[$element['name']]; + } + if (!empty($element["attributes"]) && is_array($element["attributes"])) { + foreach ($element["attributes"] as $key => $val) { + $element["children"][] = array( + 'name' => $key, + 'text' => $val, + ); + } + + if (isset($element['text'])) { + $element["children"][] = array( + 'name' => 'value', + 'text' => $element['text'], + ); + } + + $this->fillRelation($element['name'], $element); + + } elseif (!empty($element['text'])) { + $this->{$element['name']} = $element['text']; + + } elseif (!empty($element["children"]) && is_array($element["children"])) { + $this->fillRelation($element['name'], $element); + } + } + } + + + + /** + * @param string $property + * @param array $element + */ + private function fillRelation($property, array $element) + { + if (!class_exists($type = PPUtils::propertyType($this, $property))) { + trigger_error("Class $type not found.", E_USER_NOTICE); + return; // just ignore + } + + if (isset($element['num'])) { // array of objects + $this->{$property}[$element['num']] = $item = new $type(); + $item->init($element['children']); + + } else { + $this->{$property} = new $type(); + $this->{$property}->init($element["children"]); + } + } + +} \ No newline at end of file diff --git a/lib/auth/AuthUtil.php b/lib/auth/AuthUtil.php new file mode 100644 index 0000000..ca9403b --- /dev/null +++ b/lib/auth/AuthUtil.php @@ -0,0 +1,54 @@ +consumer = new OAuthConsumer("key", "secret", NULL); + $this->request_token = new OAuthToken("requestkey", "requestsecret", 1); + $this->access_token = new OAuthToken("accesskey", "accesssecret", 1); + $this->nonce = "nonce"; + }/*}}}*/ + + function lookup_consumer($consumer_key) {/*{{{*/ + if ($consumer_key == $this->consumer->key) return $this->consumer; + return NULL; + }/*}}}*/ + + function lookup_token($consumer, $token_type, $token) {/*{{{*/ + $token_attrib = $token_type . "_token"; + if ($consumer->key == $this->consumer->key + && $token == $this->$token_attrib->key) { + return $this->$token_attrib; + } + return NULL; + }/*}}}*/ + + function lookup_nonce($consumer, $token, $nonce, $timestamp) {/*{{{*/ + if ($consumer->key == $this->consumer->key + && (($token && $token->key == $this->request_token->key) + || ($token && $token->key == $this->access_token->key)) + && $nonce == $this->nonce) { + return $this->nonce; + } + return NULL; + }/*}}}*/ + + function new_request_token($consumer, $callback = NULL) {/*{{{*/ + if ($consumer->key == $this->consumer->key) { + return $this->request_token; + } + return NULL; + }/*}}}*/ + + function new_access_token($token, $consumer, $verifier = NULL) {/*{{{*/ + if ($consumer->key == $this->consumer->key + && $token->key == $this->request_token->key) { + return $this->access_token; + } + return NULL; + }/*}}}*/ +}/*}}}*/ diff --git a/lib/auth/IPPCredential.php b/lib/auth/IPPCredential.php new file mode 100644 index 0000000..a78e7be --- /dev/null +++ b/lib/auth/IPPCredential.php @@ -0,0 +1,23 @@ +thirdPartyAuthorization = $thirdPartyAuthorization; + } + + public function getThirdPartyAuthorization() { + return $this->thirdPartyAuthorization; + } + + public abstract function validate(); +} \ No newline at end of file diff --git a/lib/auth/IPPThirdPartyAuthorization.php b/lib/auth/IPPThirdPartyAuthorization.php new file mode 100644 index 0000000..7b4075b --- /dev/null +++ b/lib/auth/IPPThirdPartyAuthorization.php @@ -0,0 +1,11 @@ +add_signature_method($hmac_method); + + $sig_method = $hmac_method; + $authConsumer = new OAuthConsumer($key, $secret, NULL); + $authToken = NULL; + $authToken = new OAuthToken($token, $tokenSecret); + + //$params is the query param array which is required only in the httpMethod is "GET" + $params = array(); + //TODO: set the Query parameters to $params if httpMethod is "GET" + + $acc_req = OAuthRequest::from_consumer_and_token($authConsumer, $authToken, $httpMethod, $endpoint, $params); + $acc_req->sign_request($sig_method,$authConsumer, $authToken); + return OAuthutil::parseQueryString($acc_req); + } + + public static function generateFullAuthString($key, $secret, $token, $tokenSecret, $httpMethod, $endpoint) { + $authSignature = new AuthSignature(); + $response = $authSignature->genSign($key, $secret, $token, $tokenSecret, $httpMethod, $endpoint); + return "token=" . $token . + ",signature=" . $response['oauth_signature'] . + ",timestamp=" . $response['oauth_timestamp']; + } + +} +//PayPal specific modification ends +/* Generic exception class + */ +class OAuthException extends Exception { + // pass +} + +class OAuthConsumer { + public $key; + public $secret; + + function __construct($key, $secret, $callback_url=NULL) { + $this->key = $key; + $this->secret = $secret; + $this->callback_url = $callback_url; + } + + function __toString() { + return "OAuthConsumer[key=$this->key,secret=$this->secret]"; + } +} + +class OAuthToken { + // access tokens and request tokens + public $key; + public $secret; + + /** + * key = the token + * secret = the token secret + */ + function __construct($key, $secret) { + $this->key = $key; + $this->secret = $secret; + } + + /** + * generates the basic string serialization of a token that a server + * would respond to request_token and access_token calls with + */ + function to_string() { + return "oauth_token=" . + OAuthUtil::urlencode_rfc3986($this->key) . + "&oauth_token_secret=" . + OAuthUtil::urlencode_rfc3986($this->secret); + } + + function __toString() { + return $this->to_string(); + } +} + +/** + * A class for implementing a Signature Method + * See section 9 ("Signing Requests") in the spec + */ +abstract class OAuthSignatureMethod { + /** + * Needs to return the name of the Signature Method (ie HMAC-SHA1) + * @return string + */ + abstract public function get_name(); + + /** + * Build up the signature + * NOTE: The output of this function MUST NOT be urlencoded. + * the encoding is handled in OAuthRequest when the final + * request is serialized + * @param OAuthRequest $request + * @param OAuthConsumer $consumer + * @param OAuthToken $token + * @return string + */ + abstract public function build_signature($request, $consumer, $token); + + /** + * Verifies that a given signature is correct + * @param OAuthRequest $request + * @param OAuthConsumer $consumer + * @param OAuthToken $token + * @param string $signature + * @return bool + */ + public function check_signature($request, $consumer, $token, $signature) { + $built = $this->build_signature($request, $consumer, $token); + return $built == $signature; + } +} + +/** + * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104] + * where the Signature Base String is the text and the key is the concatenated values (each first + * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&' + * character (ASCII code 38) even if empty. + * - Chapter 9.2 ("HMAC-SHA1") + */ +class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { + function get_name() { + return "HMAC-SHA1"; + } + + public function build_signature($request, $consumer, $token) { + $base_string = $request->get_signature_base_string(); + $base_string=preg_replace("/(%[A-Za-z0-9]{2})/e", "strtolower('\\0')", $base_string);//convert base string to lowercase + $request->base_string = $base_string; + + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); + $key = implode('&', $key_parts); + $key=preg_replace("/(%[A-Za-z0-9]{2})/e", "strtolower('\\0')", $key);//convert to lowercase + return base64_encode(hash_hmac('sha1', $base_string, $key, true)); + } +} + +/** + * The PLAINTEXT method does not provide any security protection and SHOULD only be used + * over a secure channel such as HTTPS. It does not use the Signature Base String. + * - Chapter 9.4 ("PLAINTEXT") + */ +class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod { + public function get_name() { + return "PLAINTEXT"; + } + + /** + * oauth_signature is set to the concatenated encoded values of the Consumer Secret and + * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is + * empty. The result MUST be encoded again. + * - Chapter 9.4.1 ("Generating Signatures") + * + * Please note that the second encoding MUST NOT happen in the SignatureMethod, as + * OAuthRequest handles this! + */ + public function build_signature($request, $consumer, $token) { + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); + $key = implode('&', $key_parts); + $request->base_string = $key; + + return $key; + } +} + +/** + * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in + * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for + * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a + * verified way to the Service Provider, in a manner which is beyond the scope of this + * specification. + * - Chapter 9.3 ("RSA-SHA1") + */ +abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod { + public function get_name() { + return "RSA-SHA1"; + } + + // Up to the SP to implement this lookup of keys. Possible ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // (2) fetch via http using a url provided by the requester + // (3) some sort of specific discovery code based on request + // + // Either way should return a string representation of the certificate + protected abstract function fetch_public_cert(&$request); + + // Up to the SP to implement this lookup of keys. Possible ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // + // Either way should return a string representation of the certificate + protected abstract function fetch_private_cert(&$request); + + public function build_signature($request, $consumer, $token) { + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + // Fetch the private key cert based on the request + $cert = $this->fetch_private_cert($request); + + // Pull the private key ID from the certificate + $privatekeyid = openssl_get_privatekey($cert); + + // Sign using the key + $ok = openssl_sign($base_string, $signature, $privatekeyid); + + // Release the key resource + openssl_free_key($privatekeyid); + + return base64_encode($signature); + } + + public function check_signature($request, $consumer, $token, $signature) { + $decoded_sig = base64_decode($signature); + + $base_string = $request->get_signature_base_string(); + + // Fetch the public key cert based on the request + $cert = $this->fetch_public_cert($request); + + // Pull the public key ID from the certificate + $publickeyid = openssl_get_publickey($cert); + + // Check the computed signature against the one passed in the query + $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); + + // Release the key resource + openssl_free_key($publickeyid); + + return $ok == 1; + } +} + +class OAuthRequest { + public $parameters; + protected $http_method; + protected $http_url; + // for debug purposes + public $base_string; + public static $version = '1.0'; + public static $POST_INPUT = 'php://input'; + + function __construct($http_method, $http_url, $parameters=NULL) { + $parameters = ($parameters) ? $parameters : array(); + $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters); + $this->parameters = $parameters; + $this->http_method = $http_method; + $this->http_url = $http_url; + } + + + /** + * attempt to build up a request from what was passed to the server + */ + public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) { + $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") + ? 'http' + : 'https'; + $http_url = ($http_url) ? $http_url : $scheme . + '://' . $_SERVER['HTTP_HOST'] . + ':' . + $_SERVER['SERVER_PORT'] . + $_SERVER['REQUEST_URI']; + $http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD']; + + // We weren't handed any parameters, so let's find the ones relevant to + // this request. + // If you run XML-RPC or similar you should use this to provide your own + // parsed parameter-list + if (!$parameters) { + // Find request headers + $request_headers = OAuthUtil::get_headers(); + + // Parse the query-string to find GET parameters + $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']); + + // It's a POST request of the proper content-type, so parse POST + // parameters and add those overriding any duplicates from GET + if ($http_method == "POST" + && isset($request_headers['Content-Type']) + && strstr($request_headers['Content-Type'], + 'application/x-www-form-urlencoded') + ) { + $post_data = OAuthUtil::parse_parameters( + file_get_contents(self::$POST_INPUT) + ); + $parameters = array_merge($parameters, $post_data); + } + + // We have a Authorization-header with OAuth data. Parse the header + // and add those overriding any duplicates from GET or POST + if (isset($request_headers['Authorization']) && substr($request_headers['Authorization'], 0, 6) == 'OAuth ') { + $header_parameters = OAuthUtil::split_header( + $request_headers['Authorization'] + ); + $parameters = array_merge($parameters, $header_parameters); + } + + } + + return new OAuthRequest($http_method, $http_url, $parameters); + } + + /** + * pretty much a helper function to set up the request + */ + public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) { + $parameters = ($parameters) ? $parameters : array(); + $defaults = array("oauth_version" => OAuthRequest::$version, + // "oauth_nonce" => OAuthRequest::generate_nonce(), + "oauth_timestamp" => OAuthRequest::generate_timestamp(), + + "oauth_consumer_key" => $consumer->key); + if ($token) + $defaults['oauth_token'] = $token->key; + + $parameters = array_merge($defaults, $parameters); + ksort($parameters); + return new OAuthRequest($http_method, $http_url, $parameters); + } + + public function set_parameter($name, $value, $allow_duplicates = true) { + if ($allow_duplicates && isset($this->parameters[$name])) { + // We have already added parameter(s) with this name, so add to the list + if (is_scalar($this->parameters[$name])) { + // This is the first duplicate, so transform scalar (string) + // into an array so we can add the duplicates + $this->parameters[$name] = array($this->parameters[$name]); + } + + $this->parameters[$name][] = $value; + } else { + $this->parameters[$name] = $value; + } + } + + public function get_parameter($name) { + return isset($this->parameters[$name]) ? $this->parameters[$name] : null; + } + + public function get_parameters() { + return $this->parameters; + } + + public function unset_parameter($name) { + unset($this->parameters[$name]); + } + + /** + * The request parameters, sorted and concatenated into a normalized string. + * @return string + */ + public function get_signable_parameters() { + // Grab all parameters + $params = $this->parameters; + ksort($params); + // Remove oauth_signature if present + // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") + if (isset($params['oauth_signature'])) { + unset($params['oauth_signature']); + } + foreach($params as $key => $value) + { + $res[]=$key."=".$value; + } + + return implode('&', $res); + //return OAuthUtil::build_http_query($params); + } + + /** + * Returns the base string of this request + * + * The base string defined as the method, the url + * and the parameters (normalized), each urlencoded + * and the concated with &. + */ + public function get_signature_base_string() { + $parts = array( + $this->get_normalized_http_method(), + $this->get_normalized_http_url(), + $this->get_signable_parameters() + ); + + $parts = OAuthUtil::urlencode_rfc3986($parts); + + return implode('&', $parts); + } + + /** + * just uppercases the http method + */ + public function get_normalized_http_method() { + return strtoupper($this->http_method); + } + + /** + * parses the url and rebuilds it to be + * scheme://host/path + */ + public function get_normalized_http_url() { + $parts = parse_url($this->http_url); + + $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http'; + $port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80'); + $host = (isset($parts['host'])) ? $parts['host'] : ''; + $path = (isset($parts['path'])) ? $parts['path'] : ''; + + if (($scheme == 'https' && $port != '443') + || ($scheme == 'http' && $port != '80')) { + $host = "$host:$port"; + } + return "$scheme://$host$path"; + } + + /** + * builds a url usable for a GET request + */ + public function to_url() { + $post_data = $this->to_postdata(); + $out = $this->get_normalized_http_url(); + if ($post_data) { + $out .= '?'.$post_data; + } + return $out; + } + + /** + * builds the data one would send in a POST request + */ + public function to_postdata() { + return OAuthUtil::build_http_query($this->parameters); + } + + /** + * builds the Authorization: header + */ + public function to_header($realm=null) { + $first = true; + if($realm) { + $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"'; + $first = false; + } else + $out = 'Authorization: OAuth'; + + $total = array(); + foreach ($this->parameters as $k => $v) { + if (substr($k, 0, 5) != "oauth") continue; + if (is_array($v)) { + throw new OAuthException('Arrays not supported in headers'); + } + $out .= ($first) ? ' ' : ','; + $out .= OAuthUtil::urlencode_rfc3986($k) . + '="' . + OAuthUtil::urlencode_rfc3986($v) . + '"'; + $first = false; + } + return $out; + } + + public function __toString() { + return $this->to_url(); + } + + + public function sign_request($signature_method,$consumer, $token) { + + $empty=false; + $msg=array(); + if( $token->key==null){ + $msg[] = 'Token key'; + } + if($token->secret==null){ + $msg[] = 'Token secret'; + } + if($consumer->key == null){ + + $msg[] = 'Consumer key'; + } + if($consumer->secret == null){ + + $msg[] = 'Consumer secret'; + } + if($this->http_url == null){ + + $msg[] ='Endpoint'; + } + if($this->http_method == null){ + + $msg[] ='HTTP method'; + } + if(count($msg)) + { + throw new OAuthException('Enter valid '. implode(',',$msg)); + } + $this->set_parameter( + "oauth_signature_method", + $signature_method->get_name(), + false ); + + $signature = $this->build_signature($signature_method, $consumer, $token); + $this->set_parameter("oauth_signature", $signature, false); + + } + + public function build_signature($signature_method, $consumer, $token) { + $signature = $signature_method->build_signature($this, $consumer, $token); + return $signature; + } + + /** + * util function: current timestamp + */ + private static function generate_timestamp() { + return time(); + } + + /** + * util function: current nonce + */ + private static function generate_nonce() { + $mt = microtime(); + $rand = mt_rand(); + + return md5($mt . $rand); // md5s look nicer than numbers + } +} + +class OAuthServer { + protected $timestamp_threshold = 300; // in seconds, five minutes + protected $version = '1.0'; // hi blaine + protected $signature_methods = array(); + + protected $data_store; + + function __construct($data_store) { + $this->data_store = $data_store; + } + + public function add_signature_method($signature_method) { + $this->signature_methods[$signature_method->get_name()] = + $signature_method; + } + + // high level functions + + /** + * process a request_token request + * returns the request token on success + */ + public function fetch_request_token(&$request) { + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // no token required for the initial token request + $token = NULL; + + $this->check_signature($request, $consumer, $token); + + // Rev A change + $callback = $request->get_parameter('oauth_callback'); + $new_token = $this->data_store->new_request_token($consumer, $callback); + + return $new_token; + } + + /** + * process an access_token request + * returns the access token on success + */ + public function fetch_access_token(&$request) { + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // requires authorized request token + $token = $this->get_token($request, $consumer, "request"); + + $this->check_signature($request, $consumer, $token); + + // Rev A change + $verifier = $request->get_parameter('oauth_verifier'); + $new_token = $this->data_store->new_access_token($token, $consumer, $verifier); + + return $new_token; + } + + /** + * verify an api call, checks all the parameters + */ + public function verify_request(&$request) { + $this->get_version($request); + $consumer = $this->get_consumer($request); + $token = $this->get_token($request, $consumer, "access"); + $this->check_signature($request, $consumer, $token); + return array($consumer, $token); + } + + // Internals from here + /** + * version 1 + */ + private function get_version(&$request) { + $version = $request->get_parameter("oauth_version"); + if (!$version) { + // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present. + // Chapter 7.0 ("Accessing Protected Ressources") + $version = '1.0'; + } + if ($version !== $this->version) { + throw new OAuthException("OAuth version '$version' not supported"); + } + return $version; + } + + /** + * figure out the signature with some defaults + */ + private function get_signature_method($request) { + $signature_method = $request instanceof OAuthRequest + ? $request->get_parameter("oauth_signature_method") + : NULL; + + if (!$signature_method) { + // According to chapter 7 ("Accessing Protected Ressources") the signature-method + // parameter is required, and we can't just fallback to PLAINTEXT + throw new OAuthException('No signature method parameter. This parameter is required'); + } + + if (!in_array($signature_method, + array_keys($this->signature_methods))) { + throw new OAuthException( + "Signature method '$signature_method' not supported " . + "try one of the following: " . + implode(", ", array_keys($this->signature_methods)) + ); + } + return $this->signature_methods[$signature_method]; + } + + /** + * try to find the consumer for the provided request's consumer key + */ + private function get_consumer($request) { + $consumer_key = $request instanceof OAuthRequest + ? $request->get_parameter("oauth_consumer_key") + : NULL; + + if (!$consumer_key) { + throw new OAuthException("Invalid consumer key"); + } + + $consumer = $this->data_store->lookup_consumer($consumer_key); + if (!$consumer) { + throw new OAuthException("Invalid consumer"); + } + + return $consumer; + } + + /** + * try to find the token for the provided request's token key + */ + private function get_token($request, $consumer, $token_type="access") { + $token_field = $request instanceof OAuthRequest + ? $request->get_parameter('oauth_token') + : NULL; + + $token = $this->data_store->lookup_token( + $consumer, $token_type, $token_field + ); + if (!$token) { + throw new OAuthException("Invalid $token_type token: $token_field"); + } + return $token; + } + + /** + * all-in-one function to check the signature on a request + * should guess the signature method appropriately + */ + private function check_signature($request, $consumer, $token) { + // this should probably be in a different method + $timestamp = $request instanceof OAuthRequest + ? $request->get_parameter('oauth_timestamp') + : NULL; + $nonce = $request instanceof OAuthRequest + ? $request->get_parameter('oauth_nonce') + : NULL; + + $this->check_timestamp($timestamp); + $this->check_nonce($consumer, $token, $nonce, $timestamp); + + $signature_method = $this->get_signature_method($request); + + $signature = $request->get_parameter('oauth_signature'); + $valid_sig = $signature_method->check_signature( + $request, + $consumer, + $token, + $signature + ); + + if (!$valid_sig) { + throw new OAuthException("Invalid signature"); + } + } + + /** + * check that the timestamp is new enough + */ + private function check_timestamp($timestamp) { + if( ! $timestamp ) + throw new OAuthException( + 'Missing timestamp parameter. The parameter is required' + ); + + // verify that timestamp is recentish + $now = time(); + if (abs($now - $timestamp) > $this->timestamp_threshold) { + throw new OAuthException( + "Expired timestamp, yours $timestamp, ours $now" + ); + } + } + + /** + * check that the nonce is not repeated + */ + private function check_nonce($consumer, $token, $nonce, $timestamp) { + if( ! $nonce ) + throw new OAuthException( + 'Missing nonce parameter. The parameter is required' + ); + + // verify that the nonce is uniqueish + $found = $this->data_store->lookup_nonce( + $consumer, + $token, + $nonce, + $timestamp + ); + if ($found) { + throw new OAuthException("Nonce already used: $nonce"); + } + } + +} + +class OAuthDataStore { + function lookup_consumer($consumer_key) { + // implement me + } + + function lookup_token($consumer, $token_type, $token) { + // implement me + } + + function lookup_nonce($consumer, $token, $nonce, $timestamp) { + // implement me + } + + function new_request_token($consumer, $callback = null) { + // return a new token attached to this consumer + } + + function new_access_token($token, $consumer, $verifier = null) { + // return a new access token attached to this consumer + // for the user associated with this token if the request token + // is authorized + // should also invalidate the request token + } + +} + +class OAuthUtil { + public static function urlencode_rfc3986($input) { + if (is_array($input)) { + return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input); + } else if (is_scalar($input)) { + $tmp1=str_replace('%7E', '~', rawurlencode($input)); + $tmp2=str_replace(".","%2E",$tmp1); + $tmp3=str_replace("*","%2A",$tmp2); + $tmp4=str_replace( '+', ' ',$tmp3); + $tmp=str_replace("-","%2D",$tmp4); + return $tmp; + /*$tmp1=str_replace('%7E', '~', rawurlencode($input)); + $tmp2= str_replace(".","%2E",$tmp1); + + + return $tmp;*/ + } + else { + return ''; + } + } + public static function parseQueryString($str) { + $op = array(); + $pairs = explode("&", $str); + foreach ($pairs as $pair) { + list($k, $v) = array_map("urldecode", explode("=", $pair)); + $op[$k] = $v; + } + return $op; + } + //parses string to associative array -modified for PayPal Signature + + + // This decode function isn't taking into consideration the above + // modifications to the encoding process. However, this method doesn't + // seem to be used anywhere so leaving it as is. + public static function urldecode_rfc3986($string) { + return urldecode($string); + } + + // Utility function for turning the Authorization: header into + // parameters, has to do some unescaping + // Can filter out any non-oauth parameters if needed (default behaviour) + // May 28th, 2010 - method updated to tjerk.meesters for a speed improvement. + // see http://code.google.com/p/oauth/issues/detail?id=163 + public static function split_header($header, $only_allow_oauth_parameters = true) { + $params = array(); + if (preg_match_all('/('.($only_allow_oauth_parameters ? 'oauth_' : '').'[a-z_-]*)=(:?"([^"]*)"|([^,]*))/', $header, $matches)) { + foreach ($matches[1] as $i => $h) { + $params[$h] = OAuthUtil::urldecode_rfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]); + } + if (isset($params['realm'])) { + unset($params['realm']); + } + } + return $params; + } + + // helper to try to sort out headers for people who aren't running apache + public static function get_headers() { + if (function_exists('apache_request_headers')) { + // we need this to get the actual Authorization: header + // because apache tends to tell us it doesn't exist + $headers = apache_request_headers(); + + // sanitize the output of apache_request_headers because + // we always want the keys to be Cased-Like-This and arh() + // returns the headers in the same case as they are in the + // request + $out = array(); + foreach ($headers AS $key => $value) { + $key = str_replace( + " ", + "-", + ucwords(strtolower(str_replace("-", " ", $key))) + ); + $out[$key] = $value; + } + } else { + // otherwise we don't have apache and are just going to have to hope + // that $_SERVER actually contains what we need + $out = array(); + if( isset($_SERVER['CONTENT_TYPE']) ) + $out['Content-Type'] = $_SERVER['CONTENT_TYPE']; + if( isset($_ENV['CONTENT_TYPE']) ) + $out['Content-Type'] = $_ENV['CONTENT_TYPE']; + + foreach ($_SERVER as $key => $value) { + if (substr($key, 0, 5) == "HTTP_") { + // this is chaos, basically it is just there to capitalize the first + // letter of every word that is not an initial HTTP and strip HTTP + // code from przemek + $key = str_replace( + " ", + "-", + ucwords(strtolower(str_replace("_", " ", substr($key, 5)))) + ); + $out[$key] = $value; + } + } + } + return $out; + } + + // This function takes a input like a=b&a=c&d=e and returns the parsed + // parameters like this + // array('a' => array('b','c'), 'd' => 'e') + public static function parse_parameters( $input ) { + if (!isset($input) || !$input) return array(); + + $pairs = explode('&', $input); + + $parsed_parameters = array(); + foreach ($pairs as $pair) { + $split = explode('=', $pair, 2); + $parameter = OAuthUtil::urldecode_rfc3986($split[0]); + $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : ''; + + if (isset($parsed_parameters[$parameter])) { + // We have already recieved parameter(s) with this name, so add to the list + // of parameters with this name + + if (is_scalar($parsed_parameters[$parameter])) { + // This is the first duplicate, so transform scalar (string) into an array + // so we can add the duplicates + $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]); + } + + $parsed_parameters[$parameter][] = $value; + } else { + $parsed_parameters[$parameter] = $value; + } + } + return $parsed_parameters; + } + + public static function build_http_query($params) { + if (!$params) return ''; + + // Urlencode both keys and values + $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); + $values = OAuthUtil::urlencode_rfc3986(array_values($params)); + $params = array_combine($keys, $values); + + // Parameters are sorted by name, using lexicographical byte value ordering. + // Ref: Spec: 9.1.1 (1) + uksort($params, 'strcmp'); + + $pairs = array(); + foreach ($params as $parameter => $value) { + if (is_array($value)) { + // If two or more parameters share the same name, they are sorted by their value + // Ref: Spec: 9.1.1 (1) + // June 12th, 2010 - changed to sort because of issue 164 by hidetaka + sort($value, SORT_STRING); + foreach ($value as $duplicate_value) { + $pairs[] = $parameter . '=' . $duplicate_value; + } + } else { + $pairs[] = $parameter . '=' . $value; + } + } + // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61) + // Each name-value pair is separated by an '&' character (ASCII code 38) + return implode('&', $pairs); + } +} \ No newline at end of file diff --git a/lib/auth/PPCertificateCredential.php b/lib/auth/PPCertificateCredential.php new file mode 100644 index 0000000..32994f2 --- /dev/null +++ b/lib/auth/PPCertificateCredential.php @@ -0,0 +1,100 @@ +userName = trim($userName); + $this->password = trim($password); + $this->certificatePath = trim($certPath); + $this->certificatePassPhrase = $certificatePassPhrase; + $this->validate(); + } + + public function validate() { + + if (empty($this->userName)) { + throw new PPMissingCredentialException("username cannot be empty"); + } + if (empty($this->password)) { + throw new PPMissingCredentialException("password cannot be empty"); + } + if (empty($this->certificatePath)) { + throw new PPMissingCredentialException("certificate cannot be empty"); + } + } + + public function getUserName() { + return $this->userName; + } + + public function getPassword() { + return $this->password; + } + + public function getCertificatePath() { + if (realpath($this->certificatePath)) { + return realpath($this->certificatePath); + } else if(defined('PP_CONFIG_PATH')) { + return constant('PP_CONFIG_PATH') . DIRECTORY_SEPARATOR . $this->certificatePath; + } else { + return realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . ".." .DIRECTORY_SEPARATOR . ".." . DIRECTORY_SEPARATOR . "config" . DIRECTORY_SEPARATOR . $this->certificatePath); + } + } + + public function getCertificatePassPhrase() { + return $this->certificatePassPhrase; + } + + public function setApplicationId($applicationId) { + $this->applicationId = trim($applicationId); + } + + public function getApplicationId() { + return $this->applicationId; + } + +} diff --git a/lib/auth/PPSignatureCredential.php b/lib/auth/PPSignatureCredential.php new file mode 100644 index 0000000..7f7da28 --- /dev/null +++ b/lib/auth/PPSignatureCredential.php @@ -0,0 +1,68 @@ +userName = trim($userName); + $this->password = trim($password); + $this->signature = trim($signature); + $this->validate(); + } + + public function validate() { + + if (empty($this->userName)) { + throw new PPMissingCredentialException("username cannot be empty"); + } + if (empty($this->password)) { + throw new PPMissingCredentialException("password cannot be empty"); + } + // Signature can be empty if using 3-rd party auth tokens from permissions API + } + + public function getUserName() { + return $this->userName; + } + public function getPassword() { + return $this->password; + } + public function getSignature() { + return $this->signature; + } + + public function setApplicationId($applicationId) { + $this->applicationId = trim($applicationId); + } + public function getApplicationId() { + return $this->applicationId; + } +} \ No newline at end of file diff --git a/lib/auth/PPSubjectAuthorization.php b/lib/auth/PPSubjectAuthorization.php new file mode 100644 index 0000000..4ada8bc --- /dev/null +++ b/lib/auth/PPSubjectAuthorization.php @@ -0,0 +1,25 @@ +subject = $subject; + } + + public function getSubject() { + return $this->subject; + } +} \ No newline at end of file diff --git a/lib/auth/PPTokenAuthorization.php b/lib/auth/PPTokenAuthorization.php new file mode 100644 index 0000000..e8a0013 --- /dev/null +++ b/lib/auth/PPTokenAuthorization.php @@ -0,0 +1,36 @@ +accessToken = $accessToken; + $this->tokenSecret = $tokenSecret; + } + + public function getAccessToken() { + return $this->accessToken; + } + + public function getTokenSecret() { + return $this->tokenSecret; + } +} \ No newline at end of file diff --git a/lib/cacert.pem b/lib/cacert.pem new file mode 100644 index 0000000..1202c20 --- /dev/null +++ b/lib/cacert.pem @@ -0,0 +1,171 @@ +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA +TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah +WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf +Tqj/ZA1k +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G2 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO +FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71 +lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB +MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT +1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD +Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9 +-----END CERTIFICATE----- + + +Verisign Class 3 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 +EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc +cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw +EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj +055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f +j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 +xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa +t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +Verisign Class 4 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS +tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM +8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW +Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX +Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt +mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd +RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG +UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- +VeriSign Class 3 Public Primary Certification Authority - G5 +============================================================ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh +dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz +j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD +Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r +fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ +X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE +KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC +Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE +ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- +VeriSign Universal Root Certification Authority +=============================================== +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj +1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP +MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 +9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I +AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR +tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G +CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O +a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 +Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx +Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx +P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P +wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 +mJO37M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G4 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC +VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 +b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz +ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo +b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 +Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz +rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw +HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u +Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD +A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx +AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky +CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX +bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/ +D/xwzoiQ +-----END CERTIFICATE----- diff --git a/lib/exceptions/PPConfigurationException.php b/lib/exceptions/PPConfigurationException.php new file mode 100644 index 0000000..448e2e0 --- /dev/null +++ b/lib/exceptions/PPConfigurationException.php @@ -0,0 +1,7 @@ +url = $url; + } + + public function setData($data) { + $this->data = $data; + } + + public function getData() { + return $this->data; + } + + public function getUrl() { + return $this->url; + } +} \ No newline at end of file diff --git a/lib/exceptions/PPInvalidCredentialException.php b/lib/exceptions/PPInvalidCredentialException.php new file mode 100644 index 0000000..0bcf67f --- /dev/null +++ b/lib/exceptions/PPInvalidCredentialException.php @@ -0,0 +1,17 @@ +getLine().' in '.$this->getFile() + .': '.$this->getMessage().''; + return $errorMsg; + } + +} \ No newline at end of file diff --git a/lib/exceptions/PPMissingCredentialException.php b/lib/exceptions/PPMissingCredentialException.php new file mode 100644 index 0000000..f828fe5 --- /dev/null +++ b/lib/exceptions/PPMissingCredentialException.php @@ -0,0 +1,18 @@ +getLine().' in '.$this->getFile() + .': '.$this->getMessage().''; + + return $errorMsg; + } + +} \ No newline at end of file diff --git a/lib/exceptions/PPTransformerException.php b/lib/exceptions/PPTransformerException.php new file mode 100644 index 0000000..02a73c6 --- /dev/null +++ b/lib/exceptions/PPTransformerException.php @@ -0,0 +1,16 @@ +getLine().' in '.$this->getFile() + .': '.$this->getMessage().''; + + return $errorMsg; + } + +} \ No newline at end of file diff --git a/lib/formatters/FormatterFactory.php b/lib/formatters/FormatterFactory.php new file mode 100644 index 0000000..409faed --- /dev/null +++ b/lib/formatters/FormatterFactory.php @@ -0,0 +1,15 @@ +getRequestObject()->toNVPString(); + } + + public function toObject($string, $options=array()) { + throw new BadMethodCallException("Unimplemented"); + } +} diff --git a/lib/formatters/PPSOAPFormatter.php b/lib/formatters/PPSOAPFormatter.php new file mode 100644 index 0000000..c5aead5 --- /dev/null +++ b/lib/formatters/PPSOAPFormatter.php @@ -0,0 +1,28 @@ +getBindingInfo('namespace') != null ) ? $request->getBindingInfo('namespace') : ""; + $soapEnvelope = '"; + + $soapHeader = ''; + if($request->getBindingInfo('securityHeader') != null) { + $soapHeader .= $request->getBindingInfo('securityHeader'); + } + $soapHeader .= ''; + + $soapBody = ''; + $soapBody .= $request->getRequestObject()->toXMLString(); + $soapBody .= ''; + + return $soapEnvelope . $soapHeader . $soapBody . ''; + } + + public function toObject($string, $options=array()) { + throw new BadMethodCallException("Unimplemented"); + } +} diff --git a/lib/handlers/IPPHandler.php b/lib/handlers/IPPHandler.php new file mode 100644 index 0000000..9e3e5fe --- /dev/null +++ b/lib/handlers/IPPHandler.php @@ -0,0 +1,9 @@ +getCredential(); + if(isset($credential)) { + if($credential instanceof PPSignatureCredential) { + $handler = new PPSignatureAuthHandler($credential); + } else if($credential instanceof PPCertificateCredential) { + $handler = new PPCredentialAuthHandler($credential); + } else { + throw new PPInvalidCredentialException(); + } + $handler->handle($httpConfig, $request); + } + } +} \ No newline at end of file diff --git a/lib/handlers/PPCertificateAuthHandler.php b/lib/handlers/PPCertificateAuthHandler.php new file mode 100644 index 0000000..6ce3e75 --- /dev/null +++ b/lib/handlers/PPCertificateAuthHandler.php @@ -0,0 +1,47 @@ +getCredential(); + if(!isset($credential)) { + return; + } + + $httpConfig->setSSLCert($credential->getCertificatePath(), $credential->getCertificatePassPhrase()); + $thirdPartyAuth = $credential->getThirdPartyAuthorization(); + if($thirdPartyAuth && $thirdPartyAuth instanceof PPTokenAuthorization) { + $httpConfig->addHeader('X-PAYPAL-AUTHORIZATION', + AuthSignature::generateFullAuthString($credential->getUsername(), $credential->getPassword(), + $thirdPartyAuth->getAccessToken(), $thirdPartyAuth->getTokenSecret(), + $httpConfig->getMethod(), $httpConfig->getUrl())); + } + + switch($request->getBindingType()) { + case 'NV': + if(!$thirdPartyAuth || !$thirdPartyAuth instanceof PPTokenAuthorization) { + $httpConfig->addHeader('X-PAYPAL-SECURITY-USERID', $credential->getUserName()); + $httpConfig->addHeader('X-PAYPAL-SECURITY-PASSWORD', $credential->getPassword()); + if($thirdPartyAuth) { + $httpConfig->addHeader('X-PAYPAL-SECURITY-SUBJECT', $thirdPartyAuth->getSubject()); + } + } + break; + case 'SOAP': + if($thirdPartyAuth && $thirdPartyAuth instanceof PPTokenAuthorization) { + $securityHeader = ''; + } else { + $securityHeader = ''; + $securityHeader .= '' . $credential->getUserName() . ''; + $securityHeader .= '' . $credential->getPassword() . ''; + if($thirdPartyAuth && $thirdPartyAuth instanceof PPSubjectAuthorization) { + $securityHeader .= '' . $thirdPartyAuth->getSubject() . ''; + } + $securityHeader .= ''; + $request->addBindingInfo('securityHeader' , $securityHeader); + } + break; + } + } + +} \ No newline at end of file diff --git a/lib/handlers/PPGenericServiceHandler.php b/lib/handlers/PPGenericServiceHandler.php new file mode 100644 index 0000000..dd86fb2 --- /dev/null +++ b/lib/handlers/PPGenericServiceHandler.php @@ -0,0 +1,16 @@ +addHeader('X-PAYPAL-REQUEST-DATA-FORMAT', $request->getBindingType()); + $httpConfig->addHeader('X-PAYPAL-RESPONSE-DATA-FORMAT', $request->getBindingType()); + $httpConfig->addHeader('X-PAYPAL-DEVICE-IPADDRESS', PPUtils::getLocalIPAddress()); + $httpConfig->addHeader('X-PAYPAL-REQUEST-SOURCE', PPBaseService::getRequestSource()); + + if( strstr($httpConfig->getUrl(), "/AdaptiveAccounts/") && strstr($httpConfig->getUrl(), "sandbox")) { + $httpConfig->addHeader('X-PAYPAL-SANDBOX-EMAIL-ADDRESS', $config->get('service.SandboxEmailAddress')); + } + } +} \ No newline at end of file diff --git a/lib/handlers/PPMerchantServiceHandler.php b/lib/handlers/PPMerchantServiceHandler.php new file mode 100644 index 0000000..4917029 --- /dev/null +++ b/lib/handlers/PPMerchantServiceHandler.php @@ -0,0 +1,13 @@ +getHeader('X-PAYPAL-AUTHORIZATION')) { + $httpConfig->addHeader('X-PP-AUTHORIZATION', $httpConfig->getHeader('X-PAYPAL-AUTHORIZATION')); + $httpConfig->removeHeader('X-PAYPAL-AUTHORIZATION'); + } + $request->addBindingInfo("namespace", "xmlns:ns=\"urn:ebay:api:PayPalAPI\" xmlns:ebl=\"urn:ebay:apis:eBLBaseComponents\" xmlns:cc=\"urn:ebay:apis:CoreComponentTypes\" xmlns:ed=\"urn:ebay:apis:EnhancedDataTypes\""); + } +} \ No newline at end of file diff --git a/lib/handlers/PPPlatformServiceHandler.php b/lib/handlers/PPPlatformServiceHandler.php new file mode 100644 index 0000000..8b8a876 --- /dev/null +++ b/lib/handlers/PPPlatformServiceHandler.php @@ -0,0 +1,13 @@ +getCredential(); + //TODO: Assuming existence of getApplicationId + if($credential && $credential->getApplicationId() != NULL) { + $httpConfig->addHeader('X-PAYPAL-APPLICATION-ID', $credential->getApplicationId()); + } + } +} \ No newline at end of file diff --git a/lib/handlers/PPSignatureAuthHandler.php b/lib/handlers/PPSignatureAuthHandler.php new file mode 100644 index 0000000..c5f17bf --- /dev/null +++ b/lib/handlers/PPSignatureAuthHandler.php @@ -0,0 +1,47 @@ +getCredential(); + if(!isset($credential)) { + return; + } + $thirdPartyAuth = $credential->getThirdPartyAuthorization(); + if($thirdPartyAuth && $thirdPartyAuth instanceof PPTokenAuthorization) { + $httpConfig->addHeader('X-PAYPAL-AUTHORIZATION', + AuthSignature::generateFullAuthString($credential->getUsername(), $credential->getPassword(), + $thirdPartyAuth->getAccessToken(), $thirdPartyAuth->getTokenSecret(), + $httpConfig->getMethod(), $httpConfig->getUrl())); + } + + switch($request->getBindingType()) { + case 'NV': + if(!$thirdPartyAuth || !$thirdPartyAuth instanceof PPTokenAuthorization) { + $httpConfig->addHeader('X-PAYPAL-SECURITY-USERID', $credential->getUserName()); + $httpConfig->addHeader('X-PAYPAL-SECURITY-PASSWORD', $credential->getPassword()); + $httpConfig->addHeader('X-PAYPAL-SECURITY-SIGNATURE', $credential->getSignature()); + if($thirdPartyAuth) { + $httpConfig->addHeader('X-PAYPAL-SECURITY-SUBJECT', $thirdPartyAuth->getSubject()); + } + } + break; + case 'SOAP': + if($thirdPartyAuth && $thirdPartyAuth instanceof PPTokenAuthorization) { + $request->addBindingInfo('securityHeader' , ''); + } else { + $securityHeader = ''; + $securityHeader .= '' . $credential->getUserName() . ''; + $securityHeader .= '' . $credential->getPassword() . ''; + $securityHeader .= '' . $credential->getSignature() . ''; + if($thirdPartyAuth && $thirdPartyAuth instanceof PPSubjectAuthorization) { + $securityHeader .= '' . $thirdPartyAuth->getSubject() . ''; + } + $securityHeader .= ''; + $request->addBindingInfo('securityHeader' , $securityHeader); + } + break; + } + } + +} \ No newline at end of file diff --git a/lib/ipn/PPIPNMessage.php b/lib/ipn/PPIPNMessage.php new file mode 100644 index 0000000..f86882b --- /dev/null +++ b/lib/ipn/PPIPNMessage.php @@ -0,0 +1,124 @@ +ipnData[$keyValue[0]] = urldecode($keyValue[1]); + } + //var_dump($this->ipnData); + } + + /** + * Returns a hashmap of raw IPN data + * + * @return array + */ + public function getRawData() { + return $this->ipnData; + } + + /** + * Validates a IPN message + * + * @return boolean + */ + public function validate() { + if(isset($this->isIpnVerified)) + { + return $this->isIpnVerified; + } + else + { + $request = self::IPN_CMD; + if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() == 1) { + $get_magic_quotes_exists = true; + } else { + $get_magic_quotes_exists = false; + } + foreach ($this->ipnData as $key => $value) { + if($get_magic_quotes_exists) { + $value = urlencode(stripslashes($value)); + } else { + $value = urlencode($value); + } + $request .= "&$key=$value"; + } + $httpConfig = new PPHttpConfig(PPConfigManager::getInstance()->get('service.EndPoint.IPN')); + $httpConfig->addCurlOption(CURLOPT_FORBID_REUSE, 1); + $httpConfig->addCurlOption(CURLOPT_HTTPHEADER, array('Connection: Close')); + + $connection = PPConnectionManager::getInstance()->getConnection($httpConfig); + $response = $connection->execute($request); + if($response == 'VERIFIED') { + $this->isIpnVerified = true; + return true; + } + $this->isIpnVerified = false; + return false; // value is 'INVALID' + } + } + + /** + * Returns the transaction id for which + * this IPN was generated, if one is available + * + * @return string + */ + public function getTransactionId() { + if(isset($this->ipnData['txn_id'])) { + return $this->ipnData['txn_id']; + } else if(isset($this->ipnData['transaction[0].id'])) { + $idx = 0; + do { + $transId[] = $this->ipnData["transaction[$idx].id"]; + $idx++; + } while(isset($this->ipnData["transaction[$idx].id"])); + return $transId; + } + } + + /** + * Returns the transaction type for which + * this IPN was generated + * + * @return string + */ + public function getTransactionType() { + return $this->ipnData['transaction_type']; + } + +} diff --git a/tests/PPAPIServiceTest.php b/tests/PPAPIServiceTest.php new file mode 100644 index 0000000..18fa973 --- /dev/null +++ b/tests/PPAPIServiceTest.php @@ -0,0 +1,49 @@ +object = new PPAPIService(null,'AdaptiveAccounts', 'SOAP'); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /** + * @test + */ + public function testSetServiceName() + { + $this->assertEquals('AdaptiveAccounts',$this->object->serviceName); + $this->object->setServiceName('Invoice'); + $this->assertEquals('Invoice',$this->object->serviceName); + } + + /** + * @test + */ + public function testMakeRequest() + { + + } +} +?> diff --git a/tests/PPBaseServiceTest.php b/tests/PPBaseServiceTest.php new file mode 100644 index 0000000..b283ae2 --- /dev/null +++ b/tests/PPBaseServiceTest.php @@ -0,0 +1,45 @@ +object = new PPBaseService('serviceName', 'serviceBinding'); + $this->object->setAccessToken('sampletoken'); + $this->object->setTokenSecret('sampleSrcret'); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /** + * @test + */ + public function testGetServiceName() + { + $this->assertEquals('serviceName',$this->object->getServiceName() ); + $this->assertEquals('sampletoken',$this->object->getAccessToken() ); + $this->assertEquals('sampleSrcret',$this->object->getTokenSecret() ); + } + + +} +?> diff --git a/tests/PPBootStrap.php b/tests/PPBootStrap.php new file mode 100644 index 0000000..e420847 --- /dev/null +++ b/tests/PPBootStrap.php @@ -0,0 +1,8 @@ +credential = new PPCertificateCredential("platfo_1255077030_biz_api1.gmail.com", "1255077037", "cacert.pem"); + $this->credential->setApplicationId('APP-80W284485P519543T'); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /**@test + */ + public function testValidateUname() + { + $this->setExpectedException('PPMissingCredentialException'); + $credUname = new PPCertificateCredential("", "1255077037", "cacert.pem"); + $credUname->validate(); + $setNotExpectedException('PPMissingCredentialException'); + $credCorrect = new PPCertificateCredential("platfo_1255077030_biz_api1.gmail.com", "1255077037", "cacert.pem"); + $var = $credCorrect->validate(); + $this->assertNull($var); + } + /** + * @test + */ + public function testValidatePwd() + { + $this->setExpectedException('PPMissingCredentialException'); + $credpwd = new PPCertificateCredential("platfo_1255077030_biz_api1.gmail.com", "", "cacert.pem"); + $credpwd->validate(); + + } + /** + * @test + */ + public function testValidateCertPath() + { + $this->setExpectedException('PPMissingCredentialException'); + $credCertPath = new PPCertificateCredential("platfo_1255077030_biz_api1.gmail.com", "1255077037", ""); + $credCertPath->validate(); + } + /** + * @test + */ + public function testGetAppId() + { + $credAppid = new PPCertificateCredential("platfo_1255077030_biz_api1.gmail.com", "1255077037", "cacert.pem"); + $credAppid->setApplicationId("APP-ID"); + $this->assertEquals($credAppid->getApplicationId(), "APP-ID"); + } + + /** + * @test + */ + public function testGetUserName() + { + $this->assertEquals('platfo_1255077030_biz_api1.gmail.com', $this->credential->getUserName()); + + } + + /** + * @test + */ + public function testGetPassword() + { + $this->assertEquals('1255077037', $this->credential->getPassword()); + } + + /** + * @test + */ + public function testGetCertificatePath() + { + $this->assertStringEndsWith(dirname(__FILE__). DIRECTORY_SEPARATOR .'cacert.pem', $this->credential->getCertificatePath()); + } + + /**@test + */ + public function testGetApplicationId() + { + $this->assertEquals('APP-80W284485P519543T', $this->credential->getApplicationId()); + } +} +?> diff --git a/tests/PPConfigManagerTest.php b/tests/PPConfigManagerTest.php new file mode 100644 index 0000000..7ebb7f8 --- /dev/null +++ b/tests/PPConfigManagerTest.php @@ -0,0 +1,71 @@ +object = PPConfigManager::getInstance(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + + + /** + * @test + */ + public function testGetInstance() + { + $instance = $this->object->getInstance(); + $this->assertTrue($instance instanceof PPConfigManager); + } + + /** + * @test + */ + public function testGet() + { + $ret = $this->object->get('acct1'); + $this->assertContains('jb-us-seller_api1.paypal.com', $ret); + $this->assertArrayHasKey('acct1.UserName', $ret); + $this->assertTrue(sizeof($ret) == 5); + + $ret = $this->object->get('acct1.UserName'); + $this->assertEquals('jb-us-seller_api1.paypal.com', $ret); + $ret = $this->object->get("acct"); + $this->assertEquals(sizeof($ret), 8); + + } + + /** + * @test + */ + public function testGetIniPrefix() + { + $ret = $this->object->getIniPrefix(); + $this->assertContains('acct1', $ret); + $this->assertEquals(sizeof($ret), 2); + + $ret = $this->object->getIniPrefix('jb-us-seller_api1.paypal.com'); + $this->assertEquals('acct1', $ret); + } +} +?> diff --git a/tests/PPConfigurationExceptionTest.php b/tests/PPConfigurationExceptionTest.php new file mode 100644 index 0000000..b671bcb --- /dev/null +++ b/tests/PPConfigurationExceptionTest.php @@ -0,0 +1,37 @@ +object = new PPConfigurationException('Test PPConfigurationException'); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + public function testPPConfigurationException() + { + $this->setExpectedException('PPConfigurationException'); + throw new PPConfigurationException('Test PPConfigurationException'); + + } +} +?> diff --git a/tests/PPConnectionExceptionTest.php b/tests/PPConnectionExceptionTest.php new file mode 100644 index 0000000..13fa87c --- /dev/null +++ b/tests/PPConnectionExceptionTest.php @@ -0,0 +1,48 @@ +object = new PPConnectionException('http://testURL', 'test message'); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /** + * @test + */ + public function testGetUrl() + { + $this->assertEquals('http://testURL',$this->object->getUrl()); + } + /** + * @test + */ + public function testPPConnectionException() + { + $this->setExpectedException('PPConnectionException'); + throw new PPConnectionException('http://testURL','Test msg PPConnectionException'); + + } +} +?> diff --git a/tests/PPConnectionManagerTest.php b/tests/PPConnectionManagerTest.php new file mode 100644 index 0000000..c474bd0 --- /dev/null +++ b/tests/PPConnectionManagerTest.php @@ -0,0 +1,51 @@ +object = PPConnectionManager::getInstance(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /** + * @test + */ + public function testGetInstance() + { + $instance = $this->object->getInstance(); + $this->assertTrue($instance instanceof PPConnectionManager); + } + + /** + * @test + */ + public function testGetConnection() + { + $conn = $this->object->getConnection(new PPHttpConfig("http://domain.com")); + $this->assertNotNull($conn); + $this->assertTrue($conn instanceof PPHttpConnection); + $this->assertEquals(get_class($conn), "PPHttpConnection"); + } +} +?> diff --git a/tests/PPCredentialManagerTest.php b/tests/PPCredentialManagerTest.php new file mode 100644 index 0000000..5db8179 --- /dev/null +++ b/tests/PPCredentialManagerTest.php @@ -0,0 +1,81 @@ +object = PPCredentialManager::getInstance(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /** + * @test + */ + public function testGetInstance() + { + $instance = $this->object->getInstance(); + $this->assertTrue($instance instanceof PPCredentialManager); + } + + /** + * @test + */ + public function testGetSpecificCredentialObject() + { + $cred = $this->object->getCredentialObject('jb-us-seller_api1.paypal.com'); + $this->assertNotNull($cred); + $this->assertEquals('jb-us-seller_api1.paypal.com', $cred->getUsername()); + + $cred = $this->object->getCredentialObject('platfo_1255170694_biz_api1.gmail.com'); + $this->assertNotNull($cred); + $this->assertEquals('platfo_1255170694_biz_api1.gmail.com', $cred->getUsername()); + $this->assertStringEndsWith('cacert.pem', $cred->getCertificatePath()); + } + + /** + * @test + */ + public function testGetInvalidCredentialObject() + { + $this->setExpectedException('PPInvalidCredentialException'); + $cred = $this->object->getCredentialObject('invalid_biz_api1.gmail.com'); + } + + /** + * @test + */ + public function testGetDefaultCredentialObject() + { + $cred = $this->object->getCredentialObject(); + $this->assertEquals('jb-us-seller_api1.paypal.com', $cred->getUsername()); + } + + /** + * @test + */ + public function testGetPlatformCredentialObject() + { + $cred = $this->object->getCredentialObject(); + $this->assertEquals('APP-80W284485P519543T', $cred->getApplicationId()); + } +} +?> diff --git a/tests/PPIPNMessageTest.php b/tests/PPIPNMessageTest.php new file mode 100644 index 0000000..52a31ca --- /dev/null +++ b/tests/PPIPNMessageTest.php @@ -0,0 +1,58 @@ +assertEquals(false, $ipn->validate()); + } + + + /** + * @test + */ + + public function processIPNWithArrayElements() { + $ipnData = 'transaction[0].id=6WM123443434&transaction[0].status=Completed&transaction[1].id=2F12129812A1&transaction[1].status=Pending'; + $ipn = new PPIPNMessage($ipnData); + + $rawData = $ipn->getRawData(); + $this->assertEquals(4, count($rawData)); + $this->assertEquals('6WM123443434', $rawData['transaction[0].id']); + } + + /** + * @test + */ + public function processIPNWithSpecialCharacters() { + $ipnData = "description=Jake's store"; + + ini_set('get_magic_quotes_gpc', true); + $ipn = new PPIPNMessage($ipnData); + $rawData = $ipn->getRawData(); + $this->assertEquals($rawData['description'], "Jake's store"); + + ini_set('get_magic_quotes_gpc', false); + $ipn = new PPIPNMessage($ipnData); + $rawData = $ipn->getRawData(); + $this->assertEquals($rawData['description'], "Jake's store"); + $this->assertEquals($rawData['description'], "Jake's store"); + } + +} \ No newline at end of file diff --git a/tests/PPInvalidCredentialExceptionTest.php b/tests/PPInvalidCredentialExceptionTest.php new file mode 100644 index 0000000..f6b1907 --- /dev/null +++ b/tests/PPInvalidCredentialExceptionTest.php @@ -0,0 +1,40 @@ +object = new PPInvalidCredentialException; + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /** + * @test + */ + public function testErrorMessage() + { + $msg = $this->object->errorMessage(); + $this->assertContains('Error on line', $msg); + } +} +?> diff --git a/tests/PPLoggingManagerTest.php b/tests/PPLoggingManagerTest.php new file mode 100644 index 0000000..8124518 --- /dev/null +++ b/tests/PPLoggingManagerTest.php @@ -0,0 +1,72 @@ +object = new PPLoggingManager('InvoiceTest'); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /** + * @test + */ + public function testLog() + { + $this->object->log('Test Error Message', 'Debug'); + } + + /** + * @test + */ + public function testError() + { + $this->object->error('Test Error Message'); + + } + + /** + * @test + */ + public function testWarning() + { + $this->object->warning('Test Warning Message'); + } + + /** + * @test + */ + public function testInfo() + { + $this->object->info('Test info Message'); + } + + /** + * @test + */ + public function testFine() + { + $this->object->fine('Test fine Message'); + } +} +?> diff --git a/tests/PPMessageTest.php b/tests/PPMessageTest.php new file mode 100644 index 0000000..31612a5 --- /dev/null +++ b/tests/PPMessageTest.php @@ -0,0 +1,440 @@ +attrib1 = "abc"; + $o->attrib2 = "random value"; + $c = new AttributeContainerTestClass(); + $c->member = $o; + + $this->assertEquals("attrib1=abc&attrib2=random+value", $o->toNVPString()); + $this->assertEquals("member.attrib1=abc&member.attrib2=random+value", $c->toNVPString()); + + $o->value = "value"; + $this->assertEquals("attrib1=abc&attrib2=random+value&=value", $o->toNVPString()); + $this->assertEquals("member.attrib1=abc&member.attrib2=random+value&member=value", $c->toNVPString()); + + + } + + /** + * @test + */ + public function attributeSerializationInArrays() { + $o = new AttributeTestClass(); + $o->attrib1 = "abc"; + $o->attrib2 = "random value"; + $o->value = "value"; + + $c = new AttributeContainerTestClass(); + $c->member = $o; + + $o = new AttributeTestClass(); + $o->attrib1 = "abc"; + $o->attrib2 = "random value"; + $c->arrayMember = array($o); + + $this->assertEquals("member.attrib1=abc&member.attrib2=random+value&member=value&arrayMember(0).attrib1=abc&arrayMember(0).attrib2=random+value", + $c->toNVPString()); + + $c->arrayMember[0]->value = "value"; + $this->assertEquals("member.attrib1=abc&member.attrib2=random+value&member=value&arrayMember(0).attrib1=abc&arrayMember(0).attrib2=random+value&arrayMember(0)=value", + $c->toNVPString()); + + } + + /** + * @test + */ + public function attributeDeserialization() { + + // Attributes and value present + $responseMap = array( + "member.attrib1" => "abc", + "member.attrib2" => "random+value", + "member" => "value" + ); + $c = new AttributeContainerTestClass(); + $c->init($responseMap); + + $this->assertNotNull($c->member); + $this->assertEquals("abc", $c->member->attrib1); + $this->assertEquals("random value", $c->member->attrib2); + $this->assertEquals("value", $c->member->value); + + // Only value present + $responseMap = array( + "member" => "value" + ); + $c = new AttributeContainerTestClass(); + $c->init($responseMap); + + $this->assertNotNull($c->member); + $this->assertEquals("value", $c->member->value); + + + // Only attributes present + $responseMap = array( + "member.attrib1" => "abc", + "member.attrib2" => "random+value" + ); + $c = new AttributeContainerTestClass(); + $c->init($responseMap); + + $this->assertNotNull($c->member); + $this->assertEquals("abc", $c->member->attrib1); + $this->assertEquals("random value", $c->member->attrib2); + + } + + /** + * @test + */ + public function attributeDeserializationInArrays() { + + // Only value present. Single item in list + $responseMap = array( + "arrayMember(0)" => "value+1" + ); + $c = new AttributeContainerTestClass(); + $c->init($responseMap); + $this->assertNotNull($c->arrayMember[0]); + $this->assertEquals("value 1", $c->arrayMember[0]->value); + + + // Only attributes present. Single item in list + $responseMap = array( + "arrayMember(0).attrib1" => "abc", + "arrayMember(0).attrib2" => "random+value", + ); + $c = new AttributeContainerTestClass(); + $c->init($responseMap); + + $this->assertNotNull($c->arrayMember[0]); + $this->assertEquals("abc", $c->arrayMember[0]->attrib1); + $this->assertEquals("random value", $c->arrayMember[0]->attrib2); + + + // Attributes and value present. Mulitple items in list + $responseMap = array( + "arrayMember(0).attrib1" => "abc", + "arrayMember(0).attrib2" => "random+value", + "arrayMember(0)" => "value", + "arrayMember(0).attrib1" => "xyz", + "arrayMember(1).attrib1" => "attribute1" + ); + $c->init($responseMap); + + $this->assertEquals("value", $c->arrayMember[0]->value); + $this->assertEquals("xyz", $c->arrayMember[0]->attrib1); + $this->assertEquals("random value", $c->arrayMember[0]->attrib2); + + $this->assertEquals("attribute1", $c->arrayMember[1]->attrib1); + $this->assertNull($c->arrayMember[1]->value); + + } + + + /** + * @test + */ + public function simpleSerialization() { + + $o = new SimpleTestClass(); + $o->field1 = "fieldvalue1"; + $o->field2 = "fieldvalue2"; + + $this->assertEquals("field1=fieldvalue1&field2=fieldvalue2", $o->toNVPString('')); + } + + + /** + * @test + */ + public function simpleDeserialization() { + + $map = array( + "field1" => "fieldvalue1", + "field2" => "field+value2" + ); + $o = new SimpleTestClass(); + $o->init($map); + + $this->assertEquals("fieldvalue1", $o->field1); + $this->assertEquals("field value2", $o->field2); + } + + + /** + * @test + */ + public function nestedSerialization() { + + $o = new SimpleTestClass(); + $o->field1 = "fieldvalue1"; + $o->field2 = "fieldvalue2"; + + $c = new SimpleContainerTestClass(); + $c->nestedField = $o; + $c->field1 = "abc"; + + $this->assertEquals("field1=abc&nestedField.field1=fieldvalue1&nestedField.field2=fieldvalue2", $c->toNVPString('')); + } + + + /** + * @test + */ + public function nestedDeserialization() { + + $map = array( + "field1" => "abc", + "nestedField.field1" => "fieldvalue1", + "nestedField.field2" => "field+value2" + ); + + $c = new SimpleContainerTestClass(); + $c->init($map); + + $this->assertEquals("abc", $c->field1); + $this->assertEquals("fieldvalue1", $c->nestedField->field1); + $this->assertEquals("field value2", $c->nestedField->field2); + } + + + /** + * @test + */ + public function simpleListSerialization() { + + $c = new SimpleContainerTestClass(); + $c->list1 = array('Array', "of", "some strings"); + $c->field1 = "abc"; + + $this->assertEquals("field1=abc&list1(0)=Array&list1(1)=of&list1(2)=some+strings", $c->toNVPString('')); + } + + /** + * @test + */ + public function simpleListDeserialization() { + + $map = array( + "field1" => "abc", + "list1(0)" => "Array", + "list1(1)" => "of", + "list1(2)" => "some+strings" + ); + + $c = new SimpleContainerTestClass(); + $c->init($map); + + $this->assertEquals("abc", $c->field1); + $this->assertEquals(3, count($c->list1)); + $this->assertEquals("some strings", $c->list1[2]); + } + + /** + * @test + */ + public function complexListSerialization() { + + $o1 = new SimpleTestClass(); + $o1->field1 = "somevalue1"; + $o1->field2 = "somevalue2"; + + $o2 = new SimpleTestClass(); + $o2->field1 = "another value1"; + $o2->field2 = "anothervalue2"; + + $c = new SimpleContainerTestClass(); + $c->list2 = array($o1, $o2); + + $this->assertEquals("list2(0).field1=somevalue1&list2(0).field2=somevalue2&list2(1).field1=another+value1&list2(1).field2=anothervalue2", + $c->toNVPString('')); + } + + /** + * @test + */ + public function complexListDeserialization() { + + $map = array( + "list2(0).field1" => "somevalue1", + "list2(0).field2" => "somevalue2", + "list2(1).field1" => "another+value1", + "list2(1).field2" => "anothervalue2" + ); + + $c = new SimpleContainerTestClass(); + $c->init($map); + + $this->assertEquals(2, count($c->list2)); + $this->assertEquals("somevalue1", $c->list2[0]->field1); + $this->assertEquals("another value1", $c->list2[1]->field1); + } + + + /** + * @test + */ + public function serializeAndDeserialize() { + + $o1 = new AttributeTestClass(); + $o1->value = "some value"; + $o1->attrib1 = "someattrib"; + + $o2 = new AttributeTestClass(); + $o2->value = "some value2"; + + $o3 = new AttributeTestClass(); + $o3->attrib1 = "attribute"; + $o3->value = "some value3"; + + $c = new SimpleContainerTestClass(); + $c->list3 = array($o1, $o2, $o3); + + $newC = new SimpleContainerTestClass(); + $newC->init(PPUtils::nvpToMap($c->toNVPString(''))); //TODO: Mock nvpToMap + + $this->assertEquals($c, $newC); + } + + /** + * @test + */ + public function deserializeAndSerialize() { + $nvpString = "list2(0).field1=somevalue1&list2(0).field2=somevalue2&list2(1).field1=another+value1&list2(1).field2=anothervalue2&list3(0).attrib1=somevalue1&list3(0).attrib2=somevalue2&list3(0)=value+field&list3(1).attrib1=another+value1&list3(2)=anothervalue2"; + $newC = new SimpleContainerTestClass(); + $newC->init(PPUtils::nvpToMap($nvpString)); //TODO: Mock nvpToMap + $this->assertEquals($nvpString, $newC->toNVPString()); + } +} \ No newline at end of file diff --git a/tests/PPMissingCredentialExceptionTest.php b/tests/PPMissingCredentialExceptionTest.php new file mode 100644 index 0000000..6d510c2 --- /dev/null +++ b/tests/PPMissingCredentialExceptionTest.php @@ -0,0 +1,41 @@ +object = new PPMissingCredentialException; + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /** + * @test + */ + public function testErrorMessage() + { + $msg = $this->object->errorMessage(); + $this->assertContains('Error on line', $msg); + } +} +?> diff --git a/tests/PPSignatureCredentialTest.php b/tests/PPSignatureCredentialTest.php new file mode 100644 index 0000000..d5f8902 --- /dev/null +++ b/tests/PPSignatureCredentialTest.php @@ -0,0 +1,101 @@ +merchantCredential = new PPSignatureCredential("platfo_1255077030_biz_api1.gmail.com", "1255077037","Abg0gYcQyxQvnf2HDJkKtA-p6pqhA1k-KTYE0Gcy1diujFio4io5Vqjf"); + + $this->platformCredential = new PPSignatureCredential("platfo_1255077030_biz_api1.gmail.com", "1255077037","Abg0gYcQyxQvnf2HDJkKtA-p6pqhA1k-KTYE0Gcy1diujFio4io5Vqjf"); + $this->platformCredential->setApplicationId("APP-80W284485P519543T"); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /** + * @test + */ + public function testValidateUsername() + { + $this->setExpectedException('PPMissingCredentialException'); + $cred = new PPSignatureCredential("", "1255077037","Abg0gYcQyxQvnf2HDJkKtA-p6pqhA1k-KTYE0Gcy1diujFio4io5Vqjf"); + $cred->validate(); + } + + /** + * @test + */ + public function testValidatepwd() + { + $this->setExpectedException('PPMissingCredentialException'); + $cred = new PPSignatureCredential("platfo_1255077030_biz_api1.gmail.com", "","Abg0gYcQyxQvnf2HDJkKtA-p6pqhA1k-KTYE0Gcy1diujFio4io5Vqjf"); + $cred->validate(); + } + + /** + * @test + */ + public function testGetSignature() + { + $this->assertEquals('Abg0gYcQyxQvnf2HDJkKtA-p6pqhA1k-KTYE0Gcy1diujFio4io5Vqjf', $this->merchantCredential->getSignature()); + } + /** + * @test + */ + public function testGetUserName() + { + $this->assertEquals('platfo_1255077030_biz_api1.gmail.com', $this->merchantCredential->getUserName()); + } + /** + * @test + */ + public function testGetPassword() + { + $this->assertEquals('1255077037', $this->merchantCredential->getPassword()); + } + /** + * @test + */ + public function testGetAppId() + { + $this->assertEquals('APP-80W284485P519543T', $this->platformCredential->getApplicationId()); + } + + public function testThirdPartyAuthorization() { + $authorizerEmail = "merchant@domain.com"; + $thirdPartyAuth = new PPSubjectAuthorization($authorizerEmail); + $cred = new PPSignatureCredential("username", "pwd", "signature"); + $cred->setThirdPartyAuthorization($thirdPartyAuth); + $this->assertEquals($cred->getThirdPartyAuthorization()->getSubject(), $authorizerEmail); + + $accessToken = "atoken"; + $tokenSecret = "asecret"; + $thirdPartyAuth = new PPTokenAuthorization($accessToken, $tokenSecret); + $cred->setThirdPartyAuthorization($thirdPartyAuth); + $this->assertEquals($cred->getThirdPartyAuthorization()->getAccessToken(), $accessToken); + $this->assertEquals($cred->getThirdPartyAuthorization()->getTokenSecret(), $tokenSecret); + } + +} +?> diff --git a/tests/PPUtilsTest.php b/tests/PPUtilsTest.php new file mode 100644 index 0000000..f2026d6 --- /dev/null +++ b/tests/PPUtilsTest.php @@ -0,0 +1,62 @@ +object = new PPUtils; + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /** + * @test + */ + public function testNvpToMap() + { + $arr = $this->object->nvpToMap('requestEnvelope.detailLevel=ReturnAll&requestEnvelope.errorLanguage=en_US&invoice.merchantEmail=jb-us-seller1@paypal.com&invoice.payerEmail=jbui-us-personal1@paypal.com&invoice.items[0].name=product1&invoice.items[0].quantity=10.0&invoice.items[0].unitPrice=1.2&invoice.currencyCode=USD&invoice.paymentTerms=DueOnReceipt'); + $this->assertArrayHasKey('requestEnvelope.detailLevel', $arr); + $this->assertArrayHasKey('requestEnvelope.errorLanguage', $arr); + $this->assertEquals(is_array($arr),true); + } + + /** + * @test + */ + public function testArray_match_key() + { + $arr = array('key1' => 'somevalue', 'key2' => 'someothervalue'); + $this->assertEquals(PPUtils::array_match_key($arr, "key"), true); + $arr = unserialize('a:10:{s:26:"responseEnvelope.timestamp";s:35:"2011-04-19T04%3A32%3A29.469-07%3A00";s:20:"responseEnvelope.ack";s:7:"Failure";s:30:"responseEnvelope.correlationId";s:13:"c2514f258ddf1";s:22:"responseEnvelope.build";s:7:"1829457";s:16:"error(0).errorId";s:6:"580027";s:15:"error(0).domain";s:8:"PLATFORM";s:17:"error(0).severity";s:5:"Error";s:17:"error(0).category";s:11:"Application";s:16:"error(0).message";s:44:"Prohibited+request+parameter%3A+businessInfo";s:21:"error(0).parameter(0)";s:12:"businessInfo";}'); + $this->assertEquals(PPUtils::array_match_key($arr, "error(0)."), true); + } + + /** + * @test + */ + public function testGetLocalIPAddress() + { + $ip = $this->object->getLocalIPAddress(); + //$this->assertEquals('127.0.0.1',$ip); + } + +} +?> diff --git a/tests/sdk_config.ini b/tests/sdk_config.ini new file mode 100644 index 0000000..b9b1556 --- /dev/null +++ b/tests/sdk_config.ini @@ -0,0 +1,55 @@ +;Account credentials +[Account] +acct1.UserName = jb-us-seller_api1.paypal.com +acct1.Password = WX4WTU3S8MY44S7F +acct1.Signature = AFcWxV21C7fd0v3bYYYRCpSSRl31A7yDhhsPUU2XhtMoZXsWHFxu-RWy +acct1.AppId = APP-80W284485P519543T +# Subject is optional and is required only in case of third party authorization +acct1.Subject = + +; Certificate Credentials Test Account +acct2.UserName = platfo_1255170694_biz_api1.gmail.com +acct2.Password = 2DPPKUPKB7DQLXNR +; Certificate path relative to config folder or absolute path in file system +acct2.CertPath = cacert.pem + + +;Connection Information +[Http] +http.ConnectionTimeOut = 30 +http.Retry = 5 +;http.Proxy + + +;Service Configuration +[Service] +; ------------------------------SANDBOX------------------------------ # +; NOTE: both the URLs below are required (PayPalAPI, PayPalAPIAA) +service.EndPoint.PayPalAPI = "https://api-3t.sandbox.paypal.com/2.0" ; Endpoint for 3-token credentials +service.EndPoint.PayPalAPIAA = "https://api-3t.sandbox.paypal.com/2.0" ; Endpoint for 3-token credentials +; Uncomment line below if you are using certificate credentials +; service.EndPoint.PayPalAPI = "https://api.sandbox.paypal.com/2.0" +; service.EndPoint.PayPalAPIAA = "https://api.sandbox.paypal.com/2.0" + +service.EndPoint.IPN = "https://ipnpb.sandbox.paypal.com/cgi-bin/webscr" +service.RedirectURL = "https://www.sandbox.paypal.com/webscr&cmd=" + +; Multiple end-points configuration - while using multiple SDKs in combination, like merchant APIs(expresscheckout etc) and Permissions etc, uncomment the respective endpoint. refer README for more information +; Permissions Platform Service +service.EndPoint.Permissions = "https://svcs.sandbox.paypal.com/" + +; ------------------------------PRODUCTION------------------------------ # +;service.EndPoint.PayPalAPI = "https://api-3t.paypal.com/2.0" ; Endpoint for 3-token credentials +;service.EndPoint.PayPalAPIAA = "https://api-3t.paypal.com/2.0" ; Endpoint for 3-token credentials +;service.EndPoint.PayPalAPI = "https://api.paypal.com/2.0" ; Certificate credential +;service.EndPoint.PayPalAPIAA = "https://api.paypal.com/2.0" ; Certificate credential +;service.EndPoint.Permissions = "https://svcs.paypal.com/" +;service.EndPoint.IPN = "https://ipnpb.paypal.com/cgi-bin/webscr" +;service.RedirectURL="https://www.paypal.com/webscr&cmd=" + +;Logging Information +[Log] +log.FileName=../PayPal.log +log.LogLevel=INFO +log.LogEnabled=true + \ No newline at end of file