PluginProbe
InfiniteWP Client / trunk
InfiniteWP Client vtrunk
1.13.10 1.13.7 trunk 0.1.4 0.1.5 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1.0 1.1.1 1.1.10 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.11.0 1.11.1 1.12.1 1.12.3 All 92 releases
iwp-client / lib / Dropbox / OAuth / Consumer / Curl.php

Curl.php in InfiniteWP Client trunk, at lib/Dropbox/OAuth/Consumer/Curl.php

293 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * OAuth consumer using PHP cURL
5 * @author Ben Tadiar <ben@handcraftedbyben.co.uk>
6 * @link https://github.com/benthedesigner/dropbox
7 * @package Dropbox\OAuth
8 * @subpackage Consumer
9 */
10 error_reporting(0);
11 class IWP_Dropbox_OAuth_Consumer_Curl extends IWP_Dropbox_OAuth_Consumer_ConsumerAbstract {
12
13 /**
14 * Default cURL options
15 * @var array
16 */
17
18 protected $defaultOptions = array(
19 CURLOPT_SSL_VERIFYPEER => true,
20 CURLOPT_VERBOSE => true,
21 CURLOPT_HEADER => true,
22 CURLINFO_HEADER_OUT => false,
23 CURLOPT_RETURNTRANSFER => true,
24 CURLOPT_FOLLOWLOCATION => false,
25 );
26
27 /**
28 * Store the last response form the API
29 * @var mixed
30 */
31 protected $lastResponse = null;
32
33 /**
34 * Set properties and begin authentication
35 * @param string $key
36 * @param string $secret
37 */
38 public function __construct($key, $secret) {
39 // Check the cURL extension is loaded
40 if (!extension_loaded('curl')) {
41 throw new Exception('The cURL OAuth consumer requires the cURL extension');
42 }
43
44 $this->consumerKey = $key;
45 $this->consumerSecret = $secret;
46 }
47
48 /**
49 * Execute an API call
50 * @todo Improve error handling
51 * @param string $method The HTTP method
52 * @param string $url The API endpoint
53 * @param string $call The API method to call
54 * @param array $additional Additional parameters
55 * @return string|object stdClass
56 */
57
58
59 public function fetch($method, $url, $call, array $additional = array(), $isChunkDownload = array())
60 {
61 // Get the signed request URL
62 $request = $this->getSignedRequest($method, $url, $call, $additional);
63 if ($request === false) {
64 throw new Exception("Upgrade failed", 401);
65 }
66 // Initialise and execute a cURL request
67 $handle = curl_init($request['url']);
68
69 // Get the default options array
70 $options = $this->defaultOptions;
71 $options[CURLOPT_CAINFO] = dirname(__FILE__) . '/ca-bundle.pem';
72
73 //Disabling this as of now
74
75 if (!defined('WPTC_BRIDGE')) {
76 if (!class_exists('WP_HTTP_Proxy')){
77 if (!defined('WPTC_BRIDGE')) {
78 require_once(ABSPATH.WPINC.'/class-http.php');
79 } else {
80 throw new Exception("WP_HTTP_Proxy Class not foound", 500);
81 }
82 }
83 $proxy = new WP_HTTP_Proxy();
84
85 if ($proxy->is_enabled()) {
86 # WP_HTTP_Proxy returns empty strings if nothing is set
87 $user = $proxy->username();
88 $pass = $proxy->password();
89 $host = $proxy->host();
90 $port = (int)$proxy->port();
91 if (empty($port)) $port = 8080;
92 if (!empty($host) && $proxy->send_through_proxy($request['url'])) {
93 $options[CURLOPT_PROXY] = $host;
94 $options[CURLOPT_PROXYTYPE] = CURLPROXY_HTTP;
95 $options[CURLOPT_PROXYPORT] = $port;
96 if (!empty($user) && !empty($pass)) {
97 $options[CURLOPT_PROXYAUTH] = CURLAUTH_ANY;
98 $options[CURLOPT_PROXYUSERPWD] = sprintf('%s:%s', $user, $pass);
99 }
100 }
101 }
102 }
103 if (isset($request['headers'])) $options[CURLOPT_HTTPHEADER] = $request['headers'];
104
105 /*
106 Add check to see if it's an API v2 call if so then json encode the contents. This is so that it is backwards compatible with API v1 endpoints.
107 */
108 if (isset($additional['api_v2']) && !empty($request['postfields'])) {
109 $request['postfields'] = json_encode($request['postfields']);
110 }else{
111 $request['postfields'] = json_encode(null);
112 }
113
114 if ($method == 'GET' && $this->outFile) { // GET
115 $options[CURLOPT_RETURNTRANSFER] = false;
116 $options[CURLOPT_HEADER] = false;
117 $options[CURLOPT_FILE] = $this->outFile;
118 $options[CURLOPT_BINARYTRANSFER] = true;
119 $options[CURLOPT_FAILONERROR] = true;
120 /*
121 Not sure if this is used, keeping it here for backwards compatibility at the moment.
122 With API v2 the headers are set in the $request they are set above if they are set.
123 */
124 if (isset($additional['headers'])) $options[CURLOPT_HTTPHEADER] = $additional['headers'];
125 $this->outFile = null;
126 } elseif ($method == 'POST' && $this->outFile) { // POST request for download a file
127 $options[CURLOPT_POST] = true;
128 $options[CURLOPT_RETURNTRANSFER] = false;
129 $options[CURLOPT_HEADER] = false;
130 $options[CURLOPT_FILE] = $this->outFile;
131 $options[CURLOPT_BINARYTRANSFER] = true;
132 $options[CURLOPT_FAILONERROR] = true;
133 $this->outFile = null;
134 } elseif ($method == 'POST' && $this->inFile) { // POST request for upload a file
135 $options[CURLOPT_POST] = true;
136 $options[CURLOPT_POSTFIELDS] = $this->inFile;
137 } elseif ($method == 'POST') { // POST request
138 $options[CURLOPT_POST] = true;
139 $options[CURLOPT_POSTFIELDS] = $request['postfields'];
140 } elseif ($method == 'PUT' && $this->inFile) { // PUT request
141 $options[CURLOPT_PUT] = true;
142 $options[CURLOPT_INFILE] = $this->inFile;
143 // @todo Update so the data is not loaded into memory to get its size
144 $options[CURLOPT_INFILESIZE] = strlen(stream_get_contents($this->inFile));
145 fseek($this->inFile, 0);
146 $this->inFile = null;
147 }
148
149
150 // Set the cURL options at once
151 curl_setopt_array($handle, $options);
152
153 // Execute, get any error and close
154 $response = curl_exec($handle);
155 $error = curl_error($handle);
156 $getinfo = curl_getinfo($handle);
157 curl_close($handle);
158
159 //Check if a cURL error has occured
160 if ($response === false) {
161 throw new IWP_Dropbox_CurlException($error);
162 } else {
163 // Parse the response if it is a string
164 if (is_string($response)) {
165 $response = $this->parse($response);
166 }
167
168 // Set the last response
169 $this->lastResponse = $response;
170
171 $code = (!empty($response['code'])) ? $response['code'] : $getinfo['http_code'];
172
173 // The API doesn't return an error message for the 304 status code...
174 // 304's are only returned when the path supplied during metadata calls has not been modified
175 if ($code == 304) {
176 $response['body'] = new stdClass;
177 $response['body']->error = 'The folder contents have not changed';
178 }
179
180 // Check if an error occurred and throw an Exception
181 if (!empty($response['body']->error) || $code >= 400) {
182 // Dropbox returns error messages inconsistently...
183 if (!empty($response['body']->error) && $response['body']->error instanceof stdClass) {
184 $array = array_values((array) $response['body']->error);
185 //Dropbox API v2 only throws 409 errors if this error is a incorrect_offset then we need the entire error array not just the message. PHP Exception messages have to be a string so JSON encode the array.
186 if (strpos($array[0] , 'incorrect_offset') !== false) {
187 $message = json_encode($array);
188 } elseif (strpos($array[0] , 'lookup_failed') !== false ) {
189 //re-structure the array so it is correctly formatted for API
190 //Note: Dropbox v2 returns different errors at different stages hence this fix
191 $correctOffset = array(
192 '0' => $array[1]->{'.tag'},
193 '1' => $array[1]->correct_offset
194 );
195
196 $message = json_encode($correctOffset);
197 } else {
198 $message = $array[0];
199 }
200 } elseif (!empty($response['body']->error)) {
201 $message = $response['body']->error;
202 } elseif (is_string($response['body'])) {
203 // 31 Mar 2017 - This case has been found to exist; though the docs imply that there's always an 'error' property and that what is returned in JSON, we found a case of this being returned just as a simple string, but detectable via an HTTP 400: Error in call to API function "files/upload_session/append_v2": HTTP header "Dropbox-API-Arg": cursor.offset: expected integer, got string
204 $message = $response['body'];
205 } else {
206 $message = "HTTP bad response code: $code";
207 }
208
209 // Throw an Exception with the appropriate with the appropriate message and code
210 switch ($code) {
211 case 304:
212 throw new IWP_Dropbox_NotModifiedException($message, 304);
213 case 400:
214 throw new IWP_Dropbox_BadRequestException($message, 400);
215 case 404:
216 throw new IWP_Dropbox_NotFoundException($message, 404);
217 case 406:
218 throw new IWP_Dropbox_NotAcceptableException($message, 406);
219 case 415:
220 throw new IWP_Dropbox_UnsupportedMediaTypeException($message, 415);
221 case 401:
222 //401 means oauth token is expired continue to manually handle the exception depending on the situation
223 throw new IWP_Dropbox_TokenExpired($message, 401);
224 // continue;
225 case 409:
226 //409 in API V2 every error will return with a 409 to find out what the error is the error description should be checked.
227 throw new IWP_Dropbox_Exception($message, $code);
228 default:
229 throw new IWP_Dropbox_Exception($message, $code);
230 }
231 }
232
233 return $response;
234 }
235 }
236 /**
237 * Parse a cURL response
238 * @param string $response
239 * @return array
240 */
241 private function parse($response)
242 {
243 // Explode the response into headers and body parts (separated by double EOL)
244 list($headers, $response) = explode("\r\n\r\n", $response, 2);
245
246 // Explode response headers
247 $lines = explode("\r\n", $headers);
248
249 // If the status code is 100, the API server must send a final response
250 // We need to explode the response again to get the actual response
251 if (preg_match('#^HTTP/1.1 100#i', $lines[0])) {
252 list($headers, $response) = explode("\r\n\r\n", $response, 2);
253 $lines = explode("\r\n", $headers);
254 }
255
256 // Get the HTTP response code from the first line
257 $first = array_shift($lines);
258 $pattern = '#^HTTP/1.1 ([0-9]{3})#i';
259 preg_match($pattern, $first, $matches);
260 $code = $matches[1];
261
262 // Parse the remaining headers into an associative array
263 $headers = array();
264 foreach ($lines as $line) {
265 list($k, $v) = explode(': ', $line, 2);
266 $headers[strtolower($k)] = $v;
267 }
268
269 // If the response body is not a JSON encoded string
270 // we'll return the entire response body
271 if (!$body = json_decode($response)) {
272 $body = $response;
273 }
274
275 if (is_string($body)) {
276 $body_lines = explode("\r\n", $body);
277 if (preg_match('#^HTTP/1.1 100#i', $body_lines[0]) && preg_match('#^HTTP/1.#i', $body_lines[2])) {
278 return $this->parse($body);
279 }
280 }
281
282 return array('code' => $code, 'body' => $body, 'headers' => $headers);
283 }
284
285 /**
286 * Return the response for the last API request
287 * @return mixed
288 */
289 public function getlastResponse() {
290 return $this->lastResponse;
291 }
292 }
293