Completed
Pull Request — master (#8259)
by Robin
18:45
created
lib/private/Http/Client/ClientService.php 1 patch
Indentation   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -36,25 +36,25 @@
 block discarded – undo
36 36
  * @package OC\Http
37 37
  */
38 38
 class ClientService implements IClientService {
39
-	/** @var IConfig */
40
-	private $config;
41
-	/** @var ICertificateManager */
42
-	private $certificateManager;
39
+    /** @var IConfig */
40
+    private $config;
41
+    /** @var ICertificateManager */
42
+    private $certificateManager;
43 43
 
44
-	/**
45
-	 * @param IConfig $config
46
-	 * @param ICertificateManager $certificateManager
47
-	 */
48
-	public function __construct(IConfig $config,
49
-								ICertificateManager $certificateManager) {
50
-		$this->config = $config;
51
-		$this->certificateManager = $certificateManager;
52
-	}
44
+    /**
45
+     * @param IConfig $config
46
+     * @param ICertificateManager $certificateManager
47
+     */
48
+    public function __construct(IConfig $config,
49
+                                ICertificateManager $certificateManager) {
50
+        $this->config = $config;
51
+        $this->certificateManager = $certificateManager;
52
+    }
53 53
 
54
-	/**
55
-	 * @return Client
56
-	 */
57
-	public function newClient(): IClient {
58
-		return new Client($this->config, $this->certificateManager, new GuzzleClient(), HandlerStack::create());
59
-	}
54
+    /**
55
+     * @return Client
56
+     */
57
+    public function newClient(): IClient {
58
+        return new Client($this->config, $this->certificateManager, new GuzzleClient(), HandlerStack::create());
59
+    }
60 60
 }
Please login to merge, or discard this patch.
lib/private/Http/Client/Client.php 2 patches
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php
2
-declare(strict_types=1);
2
+declare(strict_types = 1);
3 3
 /**
4 4
  * @copyright Copyright (c) 2016, ownCloud, Inc.
5 5
  *
@@ -75,7 +75,7 @@  discard block
 block discarded – undo
75 75
 		}
76 76
 		$this->configured = true;
77 77
 
78
-		$this->stack->push(Middleware::mapRequest(function (RequestInterface $request) {
78
+		$this->stack->push(Middleware::mapRequest(function(RequestInterface $request) {
79 79
 			return $request
80 80
 				->withHeader('User-Agent', 'Nextcloud Server Crawler');
81 81
 		}));
@@ -101,7 +101,7 @@  discard block
 block discarded – undo
101 101
 			if ($this->config->getSystemValue('installed', false)) {
102 102
 				return $this->certificateManager->getAbsoluteBundlePath(null);
103 103
 			} else {
104
-				return \OC::$SERVERROOT . '/resources/config/ca-bundle.crt';
104
+				return \OC::$SERVERROOT.'/resources/config/ca-bundle.crt';
105 105
 			}
106 106
 		}
107 107
 	}
@@ -117,7 +117,7 @@  discard block
 block discarded – undo
117 117
 		$proxyUri = '';
118 118
 
119 119
 		if ($proxyUserPwd !== null) {
120
-			$proxyUri .= $proxyUserPwd . '@';
120
+			$proxyUri .= $proxyUserPwd.'@';
121 121
 		}
122 122
 		if ($proxyHost !== null) {
123 123
 			$proxyUri .= $proxyHost;
Please login to merge, or discard this patch.
Indentation   +279 added lines, -279 removed lines patch added patch discarded remove patch
@@ -39,298 +39,298 @@
 block discarded – undo
39 39
  * @package OC\Http
40 40
  */
41 41
 class Client implements IClient {
42
-	/** @var GuzzleClient */
43
-	private $client;
44
-	/** @var IConfig */
45
-	private $config;
46
-	/** @var ICertificateManager */
47
-	private $certificateManager;
48
-	private $configured = false;
49
-	/** @var HandlerStack */
50
-	private $stack;
42
+    /** @var GuzzleClient */
43
+    private $client;
44
+    /** @var IConfig */
45
+    private $config;
46
+    /** @var ICertificateManager */
47
+    private $certificateManager;
48
+    private $configured = false;
49
+    /** @var HandlerStack */
50
+    private $stack;
51 51
 
52
-	/**
53
-	 * @param IConfig $config
54
-	 * @param ICertificateManager $certificateManager
55
-	 * @param GuzzleClient $client
56
-	 */
57
-	public function __construct(
58
-		IConfig $config,
59
-		ICertificateManager $certificateManager,
60
-		GuzzleClient $client,
61
-		HandlerStack $stack
62
-	) {
63
-		$this->config = $config;
64
-		$this->client = $client;
65
-		$this->stack = $stack;
66
-		$this->certificateManager = $certificateManager;
67
-	}
52
+    /**
53
+     * @param IConfig $config
54
+     * @param ICertificateManager $certificateManager
55
+     * @param GuzzleClient $client
56
+     */
57
+    public function __construct(
58
+        IConfig $config,
59
+        ICertificateManager $certificateManager,
60
+        GuzzleClient $client,
61
+        HandlerStack $stack
62
+    ) {
63
+        $this->config = $config;
64
+        $this->client = $client;
65
+        $this->stack = $stack;
66
+        $this->certificateManager = $certificateManager;
67
+    }
68 68
 
69
-	/**
70
-	 * Sets the default options to the client
71
-	 */
72
-	private function setDefaultOptions() {
73
-		if ($this->configured) {
74
-			return;
75
-		}
76
-		$this->configured = true;
69
+    /**
70
+     * Sets the default options to the client
71
+     */
72
+    private function setDefaultOptions() {
73
+        if ($this->configured) {
74
+            return;
75
+        }
76
+        $this->configured = true;
77 77
 
78
-		$this->stack->push(Middleware::mapRequest(function (RequestInterface $request) {
79
-			return $request
80
-				->withHeader('User-Agent', 'Nextcloud Server Crawler');
81
-		}));
82
-	}
78
+        $this->stack->push(Middleware::mapRequest(function (RequestInterface $request) {
79
+            return $request
80
+                ->withHeader('User-Agent', 'Nextcloud Server Crawler');
81
+        }));
82
+    }
83 83
 
84
-	private function getRequestOptions() {
85
-		$options = [
86
-			'verify' => $this->getCertBundle(),
87
-		];
88
-		$proxyUri = $this->getProxyUri();
89
-		if ($proxyUri !== '') {
90
-			$options['proxy'] = $proxyUri;
91
-		}
92
-		return $options;
93
-	}
84
+    private function getRequestOptions() {
85
+        $options = [
86
+            'verify' => $this->getCertBundle(),
87
+        ];
88
+        $proxyUri = $this->getProxyUri();
89
+        if ($proxyUri !== '') {
90
+            $options['proxy'] = $proxyUri;
91
+        }
92
+        return $options;
93
+    }
94 94
 
95
-	private function getCertBundle() {
96
-		if ($this->certificateManager->listCertificates() !== []) {
97
-			return $this->certificateManager->getAbsoluteBundlePath();
98
-		} else {
99
-			// If the instance is not yet setup we need to use the static path as
100
-			// $this->certificateManager->getAbsoluteBundlePath() tries to instantiiate
101
-			// a view
102
-			if ($this->config->getSystemValue('installed', false)) {
103
-				return $this->certificateManager->getAbsoluteBundlePath(null);
104
-			} else {
105
-				return \OC::$SERVERROOT . '/resources/config/ca-bundle.crt';
106
-			}
107
-		}
108
-	}
95
+    private function getCertBundle() {
96
+        if ($this->certificateManager->listCertificates() !== []) {
97
+            return $this->certificateManager->getAbsoluteBundlePath();
98
+        } else {
99
+            // If the instance is not yet setup we need to use the static path as
100
+            // $this->certificateManager->getAbsoluteBundlePath() tries to instantiiate
101
+            // a view
102
+            if ($this->config->getSystemValue('installed', false)) {
103
+                return $this->certificateManager->getAbsoluteBundlePath(null);
104
+            } else {
105
+                return \OC::$SERVERROOT . '/resources/config/ca-bundle.crt';
106
+            }
107
+        }
108
+    }
109 109
 
110
-	/**
111
-	 * Get the proxy URI
112
-	 *
113
-	 * @return string
114
-	 */
115
-	private function getProxyUri(): string {
116
-		$proxyHost = $this->config->getSystemValue('proxy', null);
117
-		$proxyUserPwd = $this->config->getSystemValue('proxyuserpwd', null);
118
-		$proxyUri = '';
110
+    /**
111
+     * Get the proxy URI
112
+     *
113
+     * @return string
114
+     */
115
+    private function getProxyUri(): string {
116
+        $proxyHost = $this->config->getSystemValue('proxy', null);
117
+        $proxyUserPwd = $this->config->getSystemValue('proxyuserpwd', null);
118
+        $proxyUri = '';
119 119
 
120
-		if ($proxyUserPwd !== null) {
121
-			$proxyUri .= $proxyUserPwd . '@';
122
-		}
123
-		if ($proxyHost !== null) {
124
-			$proxyUri .= $proxyHost;
125
-		}
120
+        if ($proxyUserPwd !== null) {
121
+            $proxyUri .= $proxyUserPwd . '@';
122
+        }
123
+        if ($proxyHost !== null) {
124
+            $proxyUri .= $proxyHost;
125
+        }
126 126
 
127
-		return $proxyUri;
128
-	}
127
+        return $proxyUri;
128
+    }
129 129
 
130
-	/**
131
-	 * Sends a GET request
132
-	 *
133
-	 * @param string $uri
134
-	 * @param array $options Array such as
135
-	 *              'query' => [
136
-	 *                  'field' => 'abc',
137
-	 *                  'other_field' => '123',
138
-	 *                  'file_name' => fopen('/path/to/file', 'r'),
139
-	 *              ],
140
-	 *              'headers' => [
141
-	 *                  'foo' => 'bar',
142
-	 *              ],
143
-	 *              'cookies' => ['
144
-	 *                  'foo' => 'bar',
145
-	 *              ],
146
-	 *              'allow_redirects' => [
147
-	 *                   'max'       => 10,  // allow at most 10 redirects.
148
-	 *                   'strict'    => true,     // use "strict" RFC compliant redirects.
149
-	 *                   'referer'   => true,     // add a Referer header
150
-	 *                   'protocols' => ['https'] // only allow https URLs
151
-	 *              ],
152
-	 *              'save_to' => '/path/to/file', // save to a file or a stream
153
-	 *              'verify' => true, // bool or string to CA file
154
-	 *              'debug' => true,
155
-	 *              'timeout' => 5,
156
-	 * @return IResponse
157
-	 * @throws \Exception If the request could not get completed
158
-	 */
159
-	public function get(string $uri, array $options = []): IResponse {
160
-		$this->setDefaultOptions();
161
-		$response = $this->client->request('get', $uri, array_merge($options, $this->getRequestOptions()));
162
-		$isStream = isset($options['stream']) && $options['stream'];
163
-		return new Response($response, $isStream);
164
-	}
130
+    /**
131
+     * Sends a GET request
132
+     *
133
+     * @param string $uri
134
+     * @param array $options Array such as
135
+     *              'query' => [
136
+     *                  'field' => 'abc',
137
+     *                  'other_field' => '123',
138
+     *                  'file_name' => fopen('/path/to/file', 'r'),
139
+     *              ],
140
+     *              'headers' => [
141
+     *                  'foo' => 'bar',
142
+     *              ],
143
+     *              'cookies' => ['
144
+     *                  'foo' => 'bar',
145
+     *              ],
146
+     *              'allow_redirects' => [
147
+     *                   'max'       => 10,  // allow at most 10 redirects.
148
+     *                   'strict'    => true,     // use "strict" RFC compliant redirects.
149
+     *                   'referer'   => true,     // add a Referer header
150
+     *                   'protocols' => ['https'] // only allow https URLs
151
+     *              ],
152
+     *              'save_to' => '/path/to/file', // save to a file or a stream
153
+     *              'verify' => true, // bool or string to CA file
154
+     *              'debug' => true,
155
+     *              'timeout' => 5,
156
+     * @return IResponse
157
+     * @throws \Exception If the request could not get completed
158
+     */
159
+    public function get(string $uri, array $options = []): IResponse {
160
+        $this->setDefaultOptions();
161
+        $response = $this->client->request('get', $uri, array_merge($options, $this->getRequestOptions()));
162
+        $isStream = isset($options['stream']) && $options['stream'];
163
+        return new Response($response, $isStream);
164
+    }
165 165
 
166
-	/**
167
-	 * Sends a HEAD request
168
-	 *
169
-	 * @param string $uri
170
-	 * @param array $options Array such as
171
-	 *              'headers' => [
172
-	 *                  'foo' => 'bar',
173
-	 *              ],
174
-	 *              'cookies' => ['
175
-	 *                  'foo' => 'bar',
176
-	 *              ],
177
-	 *              'allow_redirects' => [
178
-	 *                   'max'       => 10,  // allow at most 10 redirects.
179
-	 *                   'strict'    => true,     // use "strict" RFC compliant redirects.
180
-	 *                   'referer'   => true,     // add a Referer header
181
-	 *                   'protocols' => ['https'] // only allow https URLs
182
-	 *              ],
183
-	 *              'save_to' => '/path/to/file', // save to a file or a stream
184
-	 *              'verify' => true, // bool or string to CA file
185
-	 *              'debug' => true,
186
-	 *              'timeout' => 5,
187
-	 * @return IResponse
188
-	 * @throws \Exception If the request could not get completed
189
-	 */
190
-	public function head(string $uri, array $options = []): IResponse {
191
-		$this->setDefaultOptions();
192
-		$response = $this->client->request('head', $uri, array_merge($options, $this->getRequestOptions()));
193
-		return new Response($response);
194
-	}
166
+    /**
167
+     * Sends a HEAD request
168
+     *
169
+     * @param string $uri
170
+     * @param array $options Array such as
171
+     *              'headers' => [
172
+     *                  'foo' => 'bar',
173
+     *              ],
174
+     *              'cookies' => ['
175
+     *                  'foo' => 'bar',
176
+     *              ],
177
+     *              'allow_redirects' => [
178
+     *                   'max'       => 10,  // allow at most 10 redirects.
179
+     *                   'strict'    => true,     // use "strict" RFC compliant redirects.
180
+     *                   'referer'   => true,     // add a Referer header
181
+     *                   'protocols' => ['https'] // only allow https URLs
182
+     *              ],
183
+     *              'save_to' => '/path/to/file', // save to a file or a stream
184
+     *              'verify' => true, // bool or string to CA file
185
+     *              'debug' => true,
186
+     *              'timeout' => 5,
187
+     * @return IResponse
188
+     * @throws \Exception If the request could not get completed
189
+     */
190
+    public function head(string $uri, array $options = []): IResponse {
191
+        $this->setDefaultOptions();
192
+        $response = $this->client->request('head', $uri, array_merge($options, $this->getRequestOptions()));
193
+        return new Response($response);
194
+    }
195 195
 
196
-	/**
197
-	 * Sends a POST request
198
-	 *
199
-	 * @param string $uri
200
-	 * @param array $options Array such as
201
-	 *              'body' => [
202
-	 *                  'field' => 'abc',
203
-	 *                  'other_field' => '123',
204
-	 *                  'file_name' => fopen('/path/to/file', 'r'),
205
-	 *              ],
206
-	 *              'headers' => [
207
-	 *                  'foo' => 'bar',
208
-	 *              ],
209
-	 *              'cookies' => ['
210
-	 *                  'foo' => 'bar',
211
-	 *              ],
212
-	 *              'allow_redirects' => [
213
-	 *                   'max'       => 10,  // allow at most 10 redirects.
214
-	 *                   'strict'    => true,     // use "strict" RFC compliant redirects.
215
-	 *                   'referer'   => true,     // add a Referer header
216
-	 *                   'protocols' => ['https'] // only allow https URLs
217
-	 *              ],
218
-	 *              'save_to' => '/path/to/file', // save to a file or a stream
219
-	 *              'verify' => true, // bool or string to CA file
220
-	 *              'debug' => true,
221
-	 *              'timeout' => 5,
222
-	 * @return IResponse
223
-	 * @throws \Exception If the request could not get completed
224
-	 */
225
-	public function post(string $uri, array $options = []): IResponse {
226
-		$this->setDefaultOptions();
227
-		$response = $this->client->request('post', $uri, array_merge($options, $this->getRequestOptions()));
228
-		return new Response($response);
229
-	}
196
+    /**
197
+     * Sends a POST request
198
+     *
199
+     * @param string $uri
200
+     * @param array $options Array such as
201
+     *              'body' => [
202
+     *                  'field' => 'abc',
203
+     *                  'other_field' => '123',
204
+     *                  'file_name' => fopen('/path/to/file', 'r'),
205
+     *              ],
206
+     *              'headers' => [
207
+     *                  'foo' => 'bar',
208
+     *              ],
209
+     *              'cookies' => ['
210
+     *                  'foo' => 'bar',
211
+     *              ],
212
+     *              'allow_redirects' => [
213
+     *                   'max'       => 10,  // allow at most 10 redirects.
214
+     *                   'strict'    => true,     // use "strict" RFC compliant redirects.
215
+     *                   'referer'   => true,     // add a Referer header
216
+     *                   'protocols' => ['https'] // only allow https URLs
217
+     *              ],
218
+     *              'save_to' => '/path/to/file', // save to a file or a stream
219
+     *              'verify' => true, // bool or string to CA file
220
+     *              'debug' => true,
221
+     *              'timeout' => 5,
222
+     * @return IResponse
223
+     * @throws \Exception If the request could not get completed
224
+     */
225
+    public function post(string $uri, array $options = []): IResponse {
226
+        $this->setDefaultOptions();
227
+        $response = $this->client->request('post', $uri, array_merge($options, $this->getRequestOptions()));
228
+        return new Response($response);
229
+    }
230 230
 
231
-	/**
232
-	 * Sends a PUT request
233
-	 *
234
-	 * @param string $uri
235
-	 * @param array $options Array such as
236
-	 *              'body' => [
237
-	 *                  'field' => 'abc',
238
-	 *                  'other_field' => '123',
239
-	 *                  'file_name' => fopen('/path/to/file', 'r'),
240
-	 *              ],
241
-	 *              'headers' => [
242
-	 *                  'foo' => 'bar',
243
-	 *              ],
244
-	 *              'cookies' => ['
245
-	 *                  'foo' => 'bar',
246
-	 *              ],
247
-	 *              'allow_redirects' => [
248
-	 *                   'max'       => 10,  // allow at most 10 redirects.
249
-	 *                   'strict'    => true,     // use "strict" RFC compliant redirects.
250
-	 *                   'referer'   => true,     // add a Referer header
251
-	 *                   'protocols' => ['https'] // only allow https URLs
252
-	 *              ],
253
-	 *              'save_to' => '/path/to/file', // save to a file or a stream
254
-	 *              'verify' => true, // bool or string to CA file
255
-	 *              'debug' => true,
256
-	 *              'timeout' => 5,
257
-	 * @return IResponse
258
-	 * @throws \Exception If the request could not get completed
259
-	 */
260
-	public function put(string $uri, array $options = []): IResponse {
261
-		$this->setDefaultOptions();
262
-		$response = $this->client->request('put', $uri, array_merge($options, $this->getRequestOptions()));
263
-		return new Response($response);
264
-	}
231
+    /**
232
+     * Sends a PUT request
233
+     *
234
+     * @param string $uri
235
+     * @param array $options Array such as
236
+     *              'body' => [
237
+     *                  'field' => 'abc',
238
+     *                  'other_field' => '123',
239
+     *                  'file_name' => fopen('/path/to/file', 'r'),
240
+     *              ],
241
+     *              'headers' => [
242
+     *                  'foo' => 'bar',
243
+     *              ],
244
+     *              'cookies' => ['
245
+     *                  'foo' => 'bar',
246
+     *              ],
247
+     *              'allow_redirects' => [
248
+     *                   'max'       => 10,  // allow at most 10 redirects.
249
+     *                   'strict'    => true,     // use "strict" RFC compliant redirects.
250
+     *                   'referer'   => true,     // add a Referer header
251
+     *                   'protocols' => ['https'] // only allow https URLs
252
+     *              ],
253
+     *              'save_to' => '/path/to/file', // save to a file or a stream
254
+     *              'verify' => true, // bool or string to CA file
255
+     *              'debug' => true,
256
+     *              'timeout' => 5,
257
+     * @return IResponse
258
+     * @throws \Exception If the request could not get completed
259
+     */
260
+    public function put(string $uri, array $options = []): IResponse {
261
+        $this->setDefaultOptions();
262
+        $response = $this->client->request('put', $uri, array_merge($options, $this->getRequestOptions()));
263
+        return new Response($response);
264
+    }
265 265
 
266
-	/**
267
-	 * Sends a DELETE request
268
-	 *
269
-	 * @param string $uri
270
-	 * @param array $options Array such as
271
-	 *              'body' => [
272
-	 *                  'field' => 'abc',
273
-	 *                  'other_field' => '123',
274
-	 *                  'file_name' => fopen('/path/to/file', 'r'),
275
-	 *              ],
276
-	 *              'headers' => [
277
-	 *                  'foo' => 'bar',
278
-	 *              ],
279
-	 *              'cookies' => ['
280
-	 *                  'foo' => 'bar',
281
-	 *              ],
282
-	 *              'allow_redirects' => [
283
-	 *                   'max'       => 10,  // allow at most 10 redirects.
284
-	 *                   'strict'    => true,     // use "strict" RFC compliant redirects.
285
-	 *                   'referer'   => true,     // add a Referer header
286
-	 *                   'protocols' => ['https'] // only allow https URLs
287
-	 *              ],
288
-	 *              'save_to' => '/path/to/file', // save to a file or a stream
289
-	 *              'verify' => true, // bool or string to CA file
290
-	 *              'debug' => true,
291
-	 *              'timeout' => 5,
292
-	 * @return IResponse
293
-	 * @throws \Exception If the request could not get completed
294
-	 */
295
-	public function delete(string $uri, array $options = []): IResponse {
296
-		$this->setDefaultOptions();
297
-		$response = $this->client->request('delete', $uri, array_merge($options, $this->getRequestOptions()));
298
-		return new Response($response);
299
-	}
266
+    /**
267
+     * Sends a DELETE request
268
+     *
269
+     * @param string $uri
270
+     * @param array $options Array such as
271
+     *              'body' => [
272
+     *                  'field' => 'abc',
273
+     *                  'other_field' => '123',
274
+     *                  'file_name' => fopen('/path/to/file', 'r'),
275
+     *              ],
276
+     *              'headers' => [
277
+     *                  'foo' => 'bar',
278
+     *              ],
279
+     *              'cookies' => ['
280
+     *                  'foo' => 'bar',
281
+     *              ],
282
+     *              'allow_redirects' => [
283
+     *                   'max'       => 10,  // allow at most 10 redirects.
284
+     *                   'strict'    => true,     // use "strict" RFC compliant redirects.
285
+     *                   'referer'   => true,     // add a Referer header
286
+     *                   'protocols' => ['https'] // only allow https URLs
287
+     *              ],
288
+     *              'save_to' => '/path/to/file', // save to a file or a stream
289
+     *              'verify' => true, // bool or string to CA file
290
+     *              'debug' => true,
291
+     *              'timeout' => 5,
292
+     * @return IResponse
293
+     * @throws \Exception If the request could not get completed
294
+     */
295
+    public function delete(string $uri, array $options = []): IResponse {
296
+        $this->setDefaultOptions();
297
+        $response = $this->client->request('delete', $uri, array_merge($options, $this->getRequestOptions()));
298
+        return new Response($response);
299
+    }
300 300
 
301 301
 
302
-	/**
303
-	 * Sends a options request
304
-	 *
305
-	 * @param string $uri
306
-	 * @param array $options Array such as
307
-	 *              'body' => [
308
-	 *                  'field' => 'abc',
309
-	 *                  'other_field' => '123',
310
-	 *                  'file_name' => fopen('/path/to/file', 'r'),
311
-	 *              ],
312
-	 *              'headers' => [
313
-	 *                  'foo' => 'bar',
314
-	 *              ],
315
-	 *              'cookies' => ['
316
-	 *                  'foo' => 'bar',
317
-	 *              ],
318
-	 *              'allow_redirects' => [
319
-	 *                   'max'       => 10,  // allow at most 10 redirects.
320
-	 *                   'strict'    => true,     // use "strict" RFC compliant redirects.
321
-	 *                   'referer'   => true,     // add a Referer header
322
-	 *                   'protocols' => ['https'] // only allow https URLs
323
-	 *              ],
324
-	 *              'save_to' => '/path/to/file', // save to a file or a stream
325
-	 *              'verify' => true, // bool or string to CA file
326
-	 *              'debug' => true,
327
-	 *              'timeout' => 5,
328
-	 * @return IResponse
329
-	 * @throws \Exception If the request could not get completed
330
-	 */
331
-	public function options(string $uri, array $options = []): IResponse {
332
-		$this->setDefaultOptions();
333
-		$response = $this->client->request('options', $uri, array_merge($options, $this->getRequestOptions()));
334
-		return new Response($response);
335
-	}
302
+    /**
303
+     * Sends a options request
304
+     *
305
+     * @param string $uri
306
+     * @param array $options Array such as
307
+     *              'body' => [
308
+     *                  'field' => 'abc',
309
+     *                  'other_field' => '123',
310
+     *                  'file_name' => fopen('/path/to/file', 'r'),
311
+     *              ],
312
+     *              'headers' => [
313
+     *                  'foo' => 'bar',
314
+     *              ],
315
+     *              'cookies' => ['
316
+     *                  'foo' => 'bar',
317
+     *              ],
318
+     *              'allow_redirects' => [
319
+     *                   'max'       => 10,  // allow at most 10 redirects.
320
+     *                   'strict'    => true,     // use "strict" RFC compliant redirects.
321
+     *                   'referer'   => true,     // add a Referer header
322
+     *                   'protocols' => ['https'] // only allow https URLs
323
+     *              ],
324
+     *              'save_to' => '/path/to/file', // save to a file or a stream
325
+     *              'verify' => true, // bool or string to CA file
326
+     *              'debug' => true,
327
+     *              'timeout' => 5,
328
+     * @return IResponse
329
+     * @throws \Exception If the request could not get completed
330
+     */
331
+    public function options(string $uri, array $options = []): IResponse {
332
+        $this->setDefaultOptions();
333
+        $response = $this->client->request('options', $uri, array_merge($options, $this->getRequestOptions()));
334
+        return new Response($response);
335
+    }
336 336
 }
Please login to merge, or discard this patch.
lib/private/Http/Client/Response.php 1 patch
Indentation   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -33,51 +33,51 @@
 block discarded – undo
33 33
  * @package OC\Http
34 34
  */
35 35
 class Response implements IResponse {
36
-	/** @var ResponseInterface */
37
-	private $response;
36
+    /** @var ResponseInterface */
37
+    private $response;
38 38
 
39
-	/**
40
-	 * @var bool
41
-	 */
42
-	private $stream;
39
+    /**
40
+     * @var bool
41
+     */
42
+    private $stream;
43 43
 
44
-	/**
45
-	 * @param ResponseInterface $response
46
-	 * @param bool $stream
47
-	 */
48
-	public function __construct(ResponseInterface $response, $stream = false) {
49
-		$this->response = $response;
50
-		$this->stream = $stream;
51
-	}
44
+    /**
45
+     * @param ResponseInterface $response
46
+     * @param bool $stream
47
+     */
48
+    public function __construct(ResponseInterface $response, $stream = false) {
49
+        $this->response = $response;
50
+        $this->stream = $stream;
51
+    }
52 52
 
53
-	/**
54
-	 * @return string|resource
55
-	 */
56
-	public function getBody() {
57
-		return $this->stream ?
58
-			$this->response->getBody()->detach():
59
-			$this->response->getBody()->getContents();
60
-	}
53
+    /**
54
+     * @return string|resource
55
+     */
56
+    public function getBody() {
57
+        return $this->stream ?
58
+            $this->response->getBody()->detach():
59
+            $this->response->getBody()->getContents();
60
+    }
61 61
 
62
-	/**
63
-	 * @return int
64
-	 */
65
-	public function getStatusCode(): int {
66
-		return $this->response->getStatusCode();
67
-	}
62
+    /**
63
+     * @return int
64
+     */
65
+    public function getStatusCode(): int {
66
+        return $this->response->getStatusCode();
67
+    }
68 68
 
69
-	/**
70
-	 * @param string $key
71
-	 * @return string
72
-	 */
73
-	public function getHeader(string $key): string {
74
-		return $this->response->getHeader($key)[0];
75
-	}
69
+    /**
70
+     * @param string $key
71
+     * @return string
72
+     */
73
+    public function getHeader(string $key): string {
74
+        return $this->response->getHeader($key)[0];
75
+    }
76 76
 
77
-	/**
78
-	 * @return array
79
-	 */
80
-	public function getHeaders(): array {
81
-		return $this->response->getHeaders();
82
-	}
77
+    /**
78
+     * @return array
79
+     */
80
+    public function getHeaders(): array {
81
+        return $this->response->getHeaders();
82
+    }
83 83
 }
Please login to merge, or discard this patch.
lib/private/Files/Storage/DAV.php 2 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -34,7 +34,6 @@
 block discarded – undo
34 34
 namespace OC\Files\Storage;
35 35
 
36 36
 use Exception;
37
-use GuzzleHttp\Exception\RequestException;
38 37
 use Psr\Http\Message\ResponseInterface;
39 38
 use Icewind\Streams\CallbackWrapper;
40 39
 use OC\Files\Filesystem;
Please login to merge, or discard this patch.
Indentation   +796 added lines, -796 removed lines patch added patch discarded remove patch
@@ -57,801 +57,801 @@
 block discarded – undo
57 57
  * @package OC\Files\Storage
58 58
  */
59 59
 class DAV extends Common {
60
-	/** @var string */
61
-	protected $password;
62
-	/** @var string */
63
-	protected $user;
64
-	/** @var string */
65
-	protected $authType;
66
-	/** @var string */
67
-	protected $host;
68
-	/** @var bool */
69
-	protected $secure;
70
-	/** @var string */
71
-	protected $root;
72
-	/** @var string */
73
-	protected $certPath;
74
-	/** @var bool */
75
-	protected $ready;
76
-	/** @var Client */
77
-	protected $client;
78
-	/** @var ArrayCache */
79
-	protected $statCache;
80
-	/** @var \OCP\Http\Client\IClientService */
81
-	protected $httpClientService;
82
-
83
-	/**
84
-	 * @param array $params
85
-	 * @throws \Exception
86
-	 */
87
-	public function __construct($params) {
88
-		$this->statCache = new ArrayCache();
89
-		$this->httpClientService = \OC::$server->getHTTPClientService();
90
-		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
91
-			$host = $params['host'];
92
-			//remove leading http[s], will be generated in createBaseUri()
93
-			if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
94
-			else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
95
-			$this->host = $host;
96
-			$this->user = $params['user'];
97
-			$this->password = $params['password'];
98
-			if (isset($params['authType'])) {
99
-				$this->authType = $params['authType'];
100
-			}
101
-			if (isset($params['secure'])) {
102
-				if (is_string($params['secure'])) {
103
-					$this->secure = ($params['secure'] === 'true');
104
-				} else {
105
-					$this->secure = (bool)$params['secure'];
106
-				}
107
-			} else {
108
-				$this->secure = false;
109
-			}
110
-			if ($this->secure === true) {
111
-				// inject mock for testing
112
-				$certManager = \OC::$server->getCertificateManager();
113
-				if (is_null($certManager)) { //no user
114
-					$certManager = \OC::$server->getCertificateManager(null);
115
-				}
116
-				$certPath = $certManager->getAbsoluteBundlePath();
117
-				if (file_exists($certPath)) {
118
-					$this->certPath = $certPath;
119
-				}
120
-			}
121
-			$this->root = $params['root'] ?? '/';
122
-			$this->root = '/' . ltrim($this->root, '/');
123
-			$this->root = rtrim($this->root, '/') . '/';
124
-		} else {
125
-			throw new \Exception('Invalid webdav storage configuration');
126
-		}
127
-	}
128
-
129
-	protected function init() {
130
-		if ($this->ready) {
131
-			return;
132
-		}
133
-		$this->ready = true;
134
-
135
-		$settings = [
136
-			'baseUri' => $this->createBaseUri(),
137
-			'userName' => $this->user,
138
-			'password' => $this->password,
139
-		];
140
-		if (isset($this->authType)) {
141
-			$settings['authType'] = $this->authType;
142
-		}
143
-
144
-		$proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
145
-		if ($proxy !== '') {
146
-			$settings['proxy'] = $proxy;
147
-		}
148
-
149
-		$this->client = new Client($settings);
150
-		$this->client->setThrowExceptions(true);
151
-		if ($this->secure === true && $this->certPath) {
152
-			$this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
153
-		}
154
-	}
155
-
156
-	/**
157
-	 * Clear the stat cache
158
-	 */
159
-	public function clearStatCache() {
160
-		$this->statCache->clear();
161
-	}
162
-
163
-	/** {@inheritdoc} */
164
-	public function getId() {
165
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
166
-	}
167
-
168
-	/** {@inheritdoc} */
169
-	public function createBaseUri() {
170
-		$baseUri = 'http';
171
-		if ($this->secure) {
172
-			$baseUri .= 's';
173
-		}
174
-		$baseUri .= '://' . $this->host . $this->root;
175
-		return $baseUri;
176
-	}
177
-
178
-	/** {@inheritdoc} */
179
-	public function mkdir($path) {
180
-		$this->init();
181
-		$path = $this->cleanPath($path);
182
-		$result = $this->simpleResponse('MKCOL', $path, null, 201);
183
-		if ($result) {
184
-			$this->statCache->set($path, true);
185
-		}
186
-		return $result;
187
-	}
188
-
189
-	/** {@inheritdoc} */
190
-	public function rmdir($path) {
191
-		$this->init();
192
-		$path = $this->cleanPath($path);
193
-		// FIXME: some WebDAV impl return 403 when trying to DELETE
194
-		// a non-empty folder
195
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
196
-		$this->statCache->clear($path . '/');
197
-		$this->statCache->remove($path);
198
-		return $result;
199
-	}
200
-
201
-	/** {@inheritdoc} */
202
-	public function opendir($path) {
203
-		$this->init();
204
-		$path = $this->cleanPath($path);
205
-		try {
206
-			$response = $this->client->propFind(
207
-				$this->encodePath($path),
208
-				['{DAV:}href'],
209
-				1
210
-			);
211
-			if ($response === false) {
212
-				return false;
213
-			}
214
-			$content = [];
215
-			$files = array_keys($response);
216
-			array_shift($files); //the first entry is the current directory
217
-
218
-			if (!$this->statCache->hasKey($path)) {
219
-				$this->statCache->set($path, true);
220
-			}
221
-			foreach ($files as $file) {
222
-				$file = urldecode($file);
223
-				// do not store the real entry, we might not have all properties
224
-				if (!$this->statCache->hasKey($path)) {
225
-					$this->statCache->set($file, true);
226
-				}
227
-				$file = basename($file);
228
-				$content[] = $file;
229
-			}
230
-			return IteratorDirectory::wrap($content);
231
-		} catch (\Exception $e) {
232
-			$this->convertException($e, $path);
233
-		}
234
-		return false;
235
-	}
236
-
237
-	/**
238
-	 * Propfind call with cache handling.
239
-	 *
240
-	 * First checks if information is cached.
241
-	 * If not, request it from the server then store to cache.
242
-	 *
243
-	 * @param string $path path to propfind
244
-	 *
245
-	 * @return array|boolean propfind response or false if the entry was not found
246
-	 *
247
-	 * @throws ClientHttpException
248
-	 */
249
-	protected function propfind($path) {
250
-		$path = $this->cleanPath($path);
251
-		$cachedResponse = $this->statCache->get($path);
252
-		// we either don't know it, or we know it exists but need more details
253
-		if (is_null($cachedResponse) || $cachedResponse === true) {
254
-			$this->init();
255
-			try {
256
-				$response = $this->client->propFind(
257
-					$this->encodePath($path),
258
-					array(
259
-						'{DAV:}getlastmodified',
260
-						'{DAV:}getcontentlength',
261
-						'{DAV:}getcontenttype',
262
-						'{http://owncloud.org/ns}permissions',
263
-						'{http://open-collaboration-services.org/ns}share-permissions',
264
-						'{DAV:}resourcetype',
265
-						'{DAV:}getetag',
266
-					)
267
-				);
268
-				$this->statCache->set($path, $response);
269
-			} catch (ClientHttpException $e) {
270
-				if ($e->getHttpStatus() === 404) {
271
-					$this->statCache->clear($path . '/');
272
-					$this->statCache->set($path, false);
273
-					return false;
274
-				}
275
-				$this->convertException($e, $path);
276
-			} catch (\Exception $e) {
277
-				$this->convertException($e, $path);
278
-			}
279
-		} else {
280
-			$response = $cachedResponse;
281
-		}
282
-		return $response;
283
-	}
284
-
285
-	/** {@inheritdoc} */
286
-	public function filetype($path) {
287
-		try {
288
-			$response = $this->propfind($path);
289
-			if ($response === false) {
290
-				return false;
291
-			}
292
-			$responseType = [];
293
-			if (isset($response["{DAV:}resourcetype"])) {
294
-				/** @var ResourceType[] $response */
295
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
296
-			}
297
-			return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
298
-		} catch (\Exception $e) {
299
-			$this->convertException($e, $path);
300
-		}
301
-		return false;
302
-	}
303
-
304
-	/** {@inheritdoc} */
305
-	public function file_exists($path) {
306
-		try {
307
-			$path = $this->cleanPath($path);
308
-			$cachedState = $this->statCache->get($path);
309
-			if ($cachedState === false) {
310
-				// we know the file doesn't exist
311
-				return false;
312
-			} else if (!is_null($cachedState)) {
313
-				return true;
314
-			}
315
-			// need to get from server
316
-			return ($this->propfind($path) !== false);
317
-		} catch (\Exception $e) {
318
-			$this->convertException($e, $path);
319
-		}
320
-		return false;
321
-	}
322
-
323
-	/** {@inheritdoc} */
324
-	public function unlink($path) {
325
-		$this->init();
326
-		$path = $this->cleanPath($path);
327
-		$result = $this->simpleResponse('DELETE', $path, null, 204);
328
-		$this->statCache->clear($path . '/');
329
-		$this->statCache->remove($path);
330
-		return $result;
331
-	}
332
-
333
-	/** {@inheritdoc} */
334
-	public function fopen($path, $mode) {
335
-		$this->init();
336
-		$path = $this->cleanPath($path);
337
-		switch ($mode) {
338
-			case 'r':
339
-			case 'rb':
340
-				try {
341
-					$response = $this->httpClientService
342
-						->newClient()
343
-						->get($this->createBaseUri() . $this->encodePath($path), [
344
-							'auth' => [$this->user, $this->password],
345
-							'stream' => true
346
-						]);
347
-				} catch (\GuzzleHttp\Exception\ClientException $e) {
348
-					if ($e->getResponse() instanceof ResponseInterface
349
-						&& $e->getResponse()->getStatusCode() === 404) {
350
-						return false;
351
-					} else {
352
-						throw $e;
353
-					}
354
-				}
355
-
356
-				if ($response->getStatusCode() !== Http::STATUS_OK) {
357
-					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
358
-						throw new \OCP\Lock\LockedException($path);
359
-					} else {
360
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
361
-					}
362
-				}
363
-
364
-				return $response->getBody();
365
-			case 'w':
366
-			case 'wb':
367
-			case 'a':
368
-			case 'ab':
369
-			case 'r+':
370
-			case 'w+':
371
-			case 'wb+':
372
-			case 'a+':
373
-			case 'x':
374
-			case 'x+':
375
-			case 'c':
376
-			case 'c+':
377
-				//emulate these
378
-				$tempManager = \OC::$server->getTempManager();
379
-				if (strrpos($path, '.') !== false) {
380
-					$ext = substr($path, strrpos($path, '.'));
381
-				} else {
382
-					$ext = '';
383
-				}
384
-				if ($this->file_exists($path)) {
385
-					if (!$this->isUpdatable($path)) {
386
-						return false;
387
-					}
388
-					if ($mode === 'w' or $mode === 'w+') {
389
-						$tmpFile = $tempManager->getTemporaryFile($ext);
390
-					} else {
391
-						$tmpFile = $this->getCachedFile($path);
392
-					}
393
-				} else {
394
-					if (!$this->isCreatable(dirname($path))) {
395
-						return false;
396
-					}
397
-					$tmpFile = $tempManager->getTemporaryFile($ext);
398
-				}
399
-				$handle = fopen($tmpFile, $mode);
400
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
401
-					$this->writeBack($tmpFile, $path);
402
-				});
403
-		}
404
-	}
405
-
406
-	/**
407
-	 * @param string $tmpFile
408
-	 */
409
-	public function writeBack($tmpFile, $path) {
410
-		$this->uploadFile($tmpFile, $path);
411
-		unlink($tmpFile);
412
-	}
413
-
414
-	/** {@inheritdoc} */
415
-	public function free_space($path) {
416
-		$this->init();
417
-		$path = $this->cleanPath($path);
418
-		try {
419
-			// TODO: cacheable ?
420
-			$response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
421
-			if ($response === false) {
422
-				return FileInfo::SPACE_UNKNOWN;
423
-			}
424
-			if (isset($response['{DAV:}quota-available-bytes'])) {
425
-				return (int)$response['{DAV:}quota-available-bytes'];
426
-			} else {
427
-				return FileInfo::SPACE_UNKNOWN;
428
-			}
429
-		} catch (\Exception $e) {
430
-			return FileInfo::SPACE_UNKNOWN;
431
-		}
432
-	}
433
-
434
-	/** {@inheritdoc} */
435
-	public function touch($path, $mtime = null) {
436
-		$this->init();
437
-		if (is_null($mtime)) {
438
-			$mtime = time();
439
-		}
440
-		$path = $this->cleanPath($path);
441
-
442
-		// if file exists, update the mtime, else create a new empty file
443
-		if ($this->file_exists($path)) {
444
-			try {
445
-				$this->statCache->remove($path);
446
-				$this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
447
-				// non-owncloud clients might not have accepted the property, need to recheck it
448
-				$response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
449
-				if ($response === false) {
450
-					return false;
451
-				}
452
-				if (isset($response['{DAV:}getlastmodified'])) {
453
-					$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
454
-					if ($remoteMtime !== $mtime) {
455
-						// server has not accepted the mtime
456
-						return false;
457
-					}
458
-				}
459
-			} catch (ClientHttpException $e) {
460
-				if ($e->getHttpStatus() === 501) {
461
-					return false;
462
-				}
463
-				$this->convertException($e, $path);
464
-				return false;
465
-			} catch (\Exception $e) {
466
-				$this->convertException($e, $path);
467
-				return false;
468
-			}
469
-		} else {
470
-			$this->file_put_contents($path, '');
471
-		}
472
-		return true;
473
-	}
474
-
475
-	/**
476
-	 * @param string $path
477
-	 * @param string $data
478
-	 * @return int
479
-	 */
480
-	public function file_put_contents($path, $data) {
481
-		$path = $this->cleanPath($path);
482
-		$result = parent::file_put_contents($path, $data);
483
-		$this->statCache->remove($path);
484
-		return $result;
485
-	}
486
-
487
-	/**
488
-	 * @param string $path
489
-	 * @param string $target
490
-	 */
491
-	protected function uploadFile($path, $target) {
492
-		$this->init();
493
-
494
-		// invalidate
495
-		$target = $this->cleanPath($target);
496
-		$this->statCache->remove($target);
497
-		$source = fopen($path, 'r');
498
-
499
-		$this->httpClientService
500
-			->newClient()
501
-			->put($this->createBaseUri() . $this->encodePath($target), [
502
-				'body' => $source,
503
-				'auth' => [$this->user, $this->password]
504
-			]);
505
-
506
-		$this->removeCachedFile($target);
507
-	}
508
-
509
-	/** {@inheritdoc} */
510
-	public function rename($path1, $path2) {
511
-		$this->init();
512
-		$path1 = $this->cleanPath($path1);
513
-		$path2 = $this->cleanPath($path2);
514
-		try {
515
-			// overwrite directory ?
516
-			if ($this->is_dir($path2)) {
517
-				// needs trailing slash in destination
518
-				$path2 = rtrim($path2, '/') . '/';
519
-			}
520
-			$this->client->request(
521
-				'MOVE',
522
-				$this->encodePath($path1),
523
-				null,
524
-				[
525
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
526
-				]
527
-			);
528
-			$this->statCache->clear($path1 . '/');
529
-			$this->statCache->clear($path2 . '/');
530
-			$this->statCache->set($path1, false);
531
-			$this->statCache->set($path2, true);
532
-			$this->removeCachedFile($path1);
533
-			$this->removeCachedFile($path2);
534
-			return true;
535
-		} catch (\Exception $e) {
536
-			$this->convertException($e);
537
-		}
538
-		return false;
539
-	}
540
-
541
-	/** {@inheritdoc} */
542
-	public function copy($path1, $path2) {
543
-		$this->init();
544
-		$path1 = $this->cleanPath($path1);
545
-		$path2 = $this->cleanPath($path2);
546
-		try {
547
-			// overwrite directory ?
548
-			if ($this->is_dir($path2)) {
549
-				// needs trailing slash in destination
550
-				$path2 = rtrim($path2, '/') . '/';
551
-			}
552
-			$this->client->request(
553
-				'COPY',
554
-				$this->encodePath($path1),
555
-				null,
556
-				[
557
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
558
-				]
559
-			);
560
-			$this->statCache->clear($path2 . '/');
561
-			$this->statCache->set($path2, true);
562
-			$this->removeCachedFile($path2);
563
-			return true;
564
-		} catch (\Exception $e) {
565
-			$this->convertException($e);
566
-		}
567
-		return false;
568
-	}
569
-
570
-	/** {@inheritdoc} */
571
-	public function stat($path) {
572
-		try {
573
-			$response = $this->propfind($path);
574
-			if (!$response) {
575
-				return false;
576
-			}
577
-			return [
578
-				'mtime' => strtotime($response['{DAV:}getlastmodified']),
579
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
580
-			];
581
-		} catch (\Exception $e) {
582
-			$this->convertException($e, $path);
583
-		}
584
-		return array();
585
-	}
586
-
587
-	/** {@inheritdoc} */
588
-	public function getMimeType($path) {
589
-		$remoteMimetype = $this->getMimeTypeFromRemote($path);
590
-		if ($remoteMimetype === 'application/octet-stream') {
591
-			return \OC::$server->getMimeTypeDetector()->detectPath($path);
592
-		} else {
593
-			return $remoteMimetype;
594
-		}
595
-	}
596
-
597
-	public function getMimeTypeFromRemote($path) {
598
-		try {
599
-			$response = $this->propfind($path);
600
-			if ($response === false) {
601
-				return false;
602
-			}
603
-			$responseType = [];
604
-			if (isset($response["{DAV:}resourcetype"])) {
605
-				/** @var ResourceType[] $response */
606
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
607
-			}
608
-			$type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
609
-			if ($type == 'dir') {
610
-				return 'httpd/unix-directory';
611
-			} elseif (isset($response['{DAV:}getcontenttype'])) {
612
-				return $response['{DAV:}getcontenttype'];
613
-			} else {
614
-				return 'application/octet-stream';
615
-			}
616
-		} catch (\Exception $e) {
617
-			return false;
618
-		}
619
-	}
620
-
621
-	/**
622
-	 * @param string $path
623
-	 * @return string
624
-	 */
625
-	public function cleanPath($path) {
626
-		if ($path === '') {
627
-			return $path;
628
-		}
629
-		$path = Filesystem::normalizePath($path);
630
-		// remove leading slash
631
-		return substr($path, 1);
632
-	}
633
-
634
-	/**
635
-	 * URL encodes the given path but keeps the slashes
636
-	 *
637
-	 * @param string $path to encode
638
-	 * @return string encoded path
639
-	 */
640
-	protected function encodePath($path) {
641
-		// slashes need to stay
642
-		return str_replace('%2F', '/', rawurlencode($path));
643
-	}
644
-
645
-	/**
646
-	 * @param string $method
647
-	 * @param string $path
648
-	 * @param string|resource|null $body
649
-	 * @param int $expected
650
-	 * @return bool
651
-	 * @throws StorageInvalidException
652
-	 * @throws StorageNotAvailableException
653
-	 */
654
-	protected function simpleResponse($method, $path, $body, $expected) {
655
-		$path = $this->cleanPath($path);
656
-		try {
657
-			$response = $this->client->request($method, $this->encodePath($path), $body);
658
-			return $response['statusCode'] == $expected;
659
-		} catch (ClientHttpException $e) {
660
-			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
661
-				$this->statCache->clear($path . '/');
662
-				$this->statCache->set($path, false);
663
-				return false;
664
-			}
665
-
666
-			$this->convertException($e, $path);
667
-		} catch (\Exception $e) {
668
-			$this->convertException($e, $path);
669
-		}
670
-		return false;
671
-	}
672
-
673
-	/**
674
-	 * check if curl is installed
675
-	 */
676
-	public static function checkDependencies() {
677
-		return true;
678
-	}
679
-
680
-	/** {@inheritdoc} */
681
-	public function isUpdatable($path) {
682
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
683
-	}
684
-
685
-	/** {@inheritdoc} */
686
-	public function isCreatable($path) {
687
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
688
-	}
689
-
690
-	/** {@inheritdoc} */
691
-	public function isSharable($path) {
692
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
693
-	}
694
-
695
-	/** {@inheritdoc} */
696
-	public function isDeletable($path) {
697
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
698
-	}
699
-
700
-	/** {@inheritdoc} */
701
-	public function getPermissions($path) {
702
-		$this->init();
703
-		$path = $this->cleanPath($path);
704
-		$response = $this->propfind($path);
705
-		if ($response === false) {
706
-			return 0;
707
-		}
708
-		if (isset($response['{http://owncloud.org/ns}permissions'])) {
709
-			return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
710
-		} else if ($this->is_dir($path)) {
711
-			return Constants::PERMISSION_ALL;
712
-		} else if ($this->file_exists($path)) {
713
-			return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
714
-		} else {
715
-			return 0;
716
-		}
717
-	}
718
-
719
-	/** {@inheritdoc} */
720
-	public function getETag($path) {
721
-		$this->init();
722
-		$path = $this->cleanPath($path);
723
-		$response = $this->propfind($path);
724
-		if ($response === false) {
725
-			return null;
726
-		}
727
-		if (isset($response['{DAV:}getetag'])) {
728
-			return trim($response['{DAV:}getetag'], '"');
729
-		}
730
-		return parent::getEtag($path);
731
-	}
732
-
733
-	/**
734
-	 * @param string $permissionsString
735
-	 * @return int
736
-	 */
737
-	protected function parsePermissions($permissionsString) {
738
-		$permissions = Constants::PERMISSION_READ;
739
-		if (strpos($permissionsString, 'R') !== false) {
740
-			$permissions |= Constants::PERMISSION_SHARE;
741
-		}
742
-		if (strpos($permissionsString, 'D') !== false) {
743
-			$permissions |= Constants::PERMISSION_DELETE;
744
-		}
745
-		if (strpos($permissionsString, 'W') !== false) {
746
-			$permissions |= Constants::PERMISSION_UPDATE;
747
-		}
748
-		if (strpos($permissionsString, 'CK') !== false) {
749
-			$permissions |= Constants::PERMISSION_CREATE;
750
-			$permissions |= Constants::PERMISSION_UPDATE;
751
-		}
752
-		return $permissions;
753
-	}
754
-
755
-	/**
756
-	 * check if a file or folder has been updated since $time
757
-	 *
758
-	 * @param string $path
759
-	 * @param int $time
760
-	 * @throws \OCP\Files\StorageNotAvailableException
761
-	 * @return bool
762
-	 */
763
-	public function hasUpdated($path, $time) {
764
-		$this->init();
765
-		$path = $this->cleanPath($path);
766
-		try {
767
-			// force refresh for $path
768
-			$this->statCache->remove($path);
769
-			$response = $this->propfind($path);
770
-			if ($response === false) {
771
-				if ($path === '') {
772
-					// if root is gone it means the storage is not available
773
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
774
-				}
775
-				return false;
776
-			}
777
-			if (isset($response['{DAV:}getetag'])) {
778
-				$cachedData = $this->getCache()->get($path);
779
-				$etag = null;
780
-				if (isset($response['{DAV:}getetag'])) {
781
-					$etag = trim($response['{DAV:}getetag'], '"');
782
-				}
783
-				if (!empty($etag) && $cachedData['etag'] !== $etag) {
784
-					return true;
785
-				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
786
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
787
-					return $sharePermissions !== $cachedData['permissions'];
788
-				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
789
-					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
790
-					return $permissions !== $cachedData['permissions'];
791
-				} else {
792
-					return false;
793
-				}
794
-			} else {
795
-				$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
796
-				return $remoteMtime > $time;
797
-			}
798
-		} catch (ClientHttpException $e) {
799
-			if ($e->getHttpStatus() === 405) {
800
-				if ($path === '') {
801
-					// if root is gone it means the storage is not available
802
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
803
-				}
804
-				return false;
805
-			}
806
-			$this->convertException($e, $path);
807
-			return false;
808
-		} catch (\Exception $e) {
809
-			$this->convertException($e, $path);
810
-			return false;
811
-		}
812
-	}
813
-
814
-	/**
815
-	 * Interpret the given exception and decide whether it is due to an
816
-	 * unavailable storage, invalid storage or other.
817
-	 * This will either throw StorageInvalidException, StorageNotAvailableException
818
-	 * or do nothing.
819
-	 *
820
-	 * @param Exception $e sabre exception
821
-	 * @param string $path optional path from the operation
822
-	 *
823
-	 * @throws StorageInvalidException if the storage is invalid, for example
824
-	 * when the authentication expired or is invalid
825
-	 * @throws StorageNotAvailableException if the storage is not available,
826
-	 * which might be temporary
827
-	 */
828
-	protected function convertException(Exception $e, $path = '') {
829
-		\OC::$server->getLogger()->logException($e, ['app' => 'files_external']);
830
-		if ($e instanceof ClientHttpException) {
831
-			if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
832
-				throw new \OCP\Lock\LockedException($path);
833
-			}
834
-			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
835
-				// either password was changed or was invalid all along
836
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
837
-			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
838
-				// ignore exception for MethodNotAllowed, false will be returned
839
-				return;
840
-			}
841
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
842
-		} else if ($e instanceof ClientException) {
843
-			// connection timeout or refused, server could be temporarily down
844
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
845
-		} else if ($e instanceof \InvalidArgumentException) {
846
-			// parse error because the server returned HTML instead of XML,
847
-			// possibly temporarily down
848
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
849
-		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
850
-			// rethrow
851
-			throw $e;
852
-		}
853
-
854
-		// TODO: only log for now, but in the future need to wrap/rethrow exception
855
-	}
60
+    /** @var string */
61
+    protected $password;
62
+    /** @var string */
63
+    protected $user;
64
+    /** @var string */
65
+    protected $authType;
66
+    /** @var string */
67
+    protected $host;
68
+    /** @var bool */
69
+    protected $secure;
70
+    /** @var string */
71
+    protected $root;
72
+    /** @var string */
73
+    protected $certPath;
74
+    /** @var bool */
75
+    protected $ready;
76
+    /** @var Client */
77
+    protected $client;
78
+    /** @var ArrayCache */
79
+    protected $statCache;
80
+    /** @var \OCP\Http\Client\IClientService */
81
+    protected $httpClientService;
82
+
83
+    /**
84
+     * @param array $params
85
+     * @throws \Exception
86
+     */
87
+    public function __construct($params) {
88
+        $this->statCache = new ArrayCache();
89
+        $this->httpClientService = \OC::$server->getHTTPClientService();
90
+        if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
91
+            $host = $params['host'];
92
+            //remove leading http[s], will be generated in createBaseUri()
93
+            if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
94
+            else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
95
+            $this->host = $host;
96
+            $this->user = $params['user'];
97
+            $this->password = $params['password'];
98
+            if (isset($params['authType'])) {
99
+                $this->authType = $params['authType'];
100
+            }
101
+            if (isset($params['secure'])) {
102
+                if (is_string($params['secure'])) {
103
+                    $this->secure = ($params['secure'] === 'true');
104
+                } else {
105
+                    $this->secure = (bool)$params['secure'];
106
+                }
107
+            } else {
108
+                $this->secure = false;
109
+            }
110
+            if ($this->secure === true) {
111
+                // inject mock for testing
112
+                $certManager = \OC::$server->getCertificateManager();
113
+                if (is_null($certManager)) { //no user
114
+                    $certManager = \OC::$server->getCertificateManager(null);
115
+                }
116
+                $certPath = $certManager->getAbsoluteBundlePath();
117
+                if (file_exists($certPath)) {
118
+                    $this->certPath = $certPath;
119
+                }
120
+            }
121
+            $this->root = $params['root'] ?? '/';
122
+            $this->root = '/' . ltrim($this->root, '/');
123
+            $this->root = rtrim($this->root, '/') . '/';
124
+        } else {
125
+            throw new \Exception('Invalid webdav storage configuration');
126
+        }
127
+    }
128
+
129
+    protected function init() {
130
+        if ($this->ready) {
131
+            return;
132
+        }
133
+        $this->ready = true;
134
+
135
+        $settings = [
136
+            'baseUri' => $this->createBaseUri(),
137
+            'userName' => $this->user,
138
+            'password' => $this->password,
139
+        ];
140
+        if (isset($this->authType)) {
141
+            $settings['authType'] = $this->authType;
142
+        }
143
+
144
+        $proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
145
+        if ($proxy !== '') {
146
+            $settings['proxy'] = $proxy;
147
+        }
148
+
149
+        $this->client = new Client($settings);
150
+        $this->client->setThrowExceptions(true);
151
+        if ($this->secure === true && $this->certPath) {
152
+            $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
153
+        }
154
+    }
155
+
156
+    /**
157
+     * Clear the stat cache
158
+     */
159
+    public function clearStatCache() {
160
+        $this->statCache->clear();
161
+    }
162
+
163
+    /** {@inheritdoc} */
164
+    public function getId() {
165
+        return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
166
+    }
167
+
168
+    /** {@inheritdoc} */
169
+    public function createBaseUri() {
170
+        $baseUri = 'http';
171
+        if ($this->secure) {
172
+            $baseUri .= 's';
173
+        }
174
+        $baseUri .= '://' . $this->host . $this->root;
175
+        return $baseUri;
176
+    }
177
+
178
+    /** {@inheritdoc} */
179
+    public function mkdir($path) {
180
+        $this->init();
181
+        $path = $this->cleanPath($path);
182
+        $result = $this->simpleResponse('MKCOL', $path, null, 201);
183
+        if ($result) {
184
+            $this->statCache->set($path, true);
185
+        }
186
+        return $result;
187
+    }
188
+
189
+    /** {@inheritdoc} */
190
+    public function rmdir($path) {
191
+        $this->init();
192
+        $path = $this->cleanPath($path);
193
+        // FIXME: some WebDAV impl return 403 when trying to DELETE
194
+        // a non-empty folder
195
+        $result = $this->simpleResponse('DELETE', $path . '/', null, 204);
196
+        $this->statCache->clear($path . '/');
197
+        $this->statCache->remove($path);
198
+        return $result;
199
+    }
200
+
201
+    /** {@inheritdoc} */
202
+    public function opendir($path) {
203
+        $this->init();
204
+        $path = $this->cleanPath($path);
205
+        try {
206
+            $response = $this->client->propFind(
207
+                $this->encodePath($path),
208
+                ['{DAV:}href'],
209
+                1
210
+            );
211
+            if ($response === false) {
212
+                return false;
213
+            }
214
+            $content = [];
215
+            $files = array_keys($response);
216
+            array_shift($files); //the first entry is the current directory
217
+
218
+            if (!$this->statCache->hasKey($path)) {
219
+                $this->statCache->set($path, true);
220
+            }
221
+            foreach ($files as $file) {
222
+                $file = urldecode($file);
223
+                // do not store the real entry, we might not have all properties
224
+                if (!$this->statCache->hasKey($path)) {
225
+                    $this->statCache->set($file, true);
226
+                }
227
+                $file = basename($file);
228
+                $content[] = $file;
229
+            }
230
+            return IteratorDirectory::wrap($content);
231
+        } catch (\Exception $e) {
232
+            $this->convertException($e, $path);
233
+        }
234
+        return false;
235
+    }
236
+
237
+    /**
238
+     * Propfind call with cache handling.
239
+     *
240
+     * First checks if information is cached.
241
+     * If not, request it from the server then store to cache.
242
+     *
243
+     * @param string $path path to propfind
244
+     *
245
+     * @return array|boolean propfind response or false if the entry was not found
246
+     *
247
+     * @throws ClientHttpException
248
+     */
249
+    protected function propfind($path) {
250
+        $path = $this->cleanPath($path);
251
+        $cachedResponse = $this->statCache->get($path);
252
+        // we either don't know it, or we know it exists but need more details
253
+        if (is_null($cachedResponse) || $cachedResponse === true) {
254
+            $this->init();
255
+            try {
256
+                $response = $this->client->propFind(
257
+                    $this->encodePath($path),
258
+                    array(
259
+                        '{DAV:}getlastmodified',
260
+                        '{DAV:}getcontentlength',
261
+                        '{DAV:}getcontenttype',
262
+                        '{http://owncloud.org/ns}permissions',
263
+                        '{http://open-collaboration-services.org/ns}share-permissions',
264
+                        '{DAV:}resourcetype',
265
+                        '{DAV:}getetag',
266
+                    )
267
+                );
268
+                $this->statCache->set($path, $response);
269
+            } catch (ClientHttpException $e) {
270
+                if ($e->getHttpStatus() === 404) {
271
+                    $this->statCache->clear($path . '/');
272
+                    $this->statCache->set($path, false);
273
+                    return false;
274
+                }
275
+                $this->convertException($e, $path);
276
+            } catch (\Exception $e) {
277
+                $this->convertException($e, $path);
278
+            }
279
+        } else {
280
+            $response = $cachedResponse;
281
+        }
282
+        return $response;
283
+    }
284
+
285
+    /** {@inheritdoc} */
286
+    public function filetype($path) {
287
+        try {
288
+            $response = $this->propfind($path);
289
+            if ($response === false) {
290
+                return false;
291
+            }
292
+            $responseType = [];
293
+            if (isset($response["{DAV:}resourcetype"])) {
294
+                /** @var ResourceType[] $response */
295
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
296
+            }
297
+            return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
298
+        } catch (\Exception $e) {
299
+            $this->convertException($e, $path);
300
+        }
301
+        return false;
302
+    }
303
+
304
+    /** {@inheritdoc} */
305
+    public function file_exists($path) {
306
+        try {
307
+            $path = $this->cleanPath($path);
308
+            $cachedState = $this->statCache->get($path);
309
+            if ($cachedState === false) {
310
+                // we know the file doesn't exist
311
+                return false;
312
+            } else if (!is_null($cachedState)) {
313
+                return true;
314
+            }
315
+            // need to get from server
316
+            return ($this->propfind($path) !== false);
317
+        } catch (\Exception $e) {
318
+            $this->convertException($e, $path);
319
+        }
320
+        return false;
321
+    }
322
+
323
+    /** {@inheritdoc} */
324
+    public function unlink($path) {
325
+        $this->init();
326
+        $path = $this->cleanPath($path);
327
+        $result = $this->simpleResponse('DELETE', $path, null, 204);
328
+        $this->statCache->clear($path . '/');
329
+        $this->statCache->remove($path);
330
+        return $result;
331
+    }
332
+
333
+    /** {@inheritdoc} */
334
+    public function fopen($path, $mode) {
335
+        $this->init();
336
+        $path = $this->cleanPath($path);
337
+        switch ($mode) {
338
+            case 'r':
339
+            case 'rb':
340
+                try {
341
+                    $response = $this->httpClientService
342
+                        ->newClient()
343
+                        ->get($this->createBaseUri() . $this->encodePath($path), [
344
+                            'auth' => [$this->user, $this->password],
345
+                            'stream' => true
346
+                        ]);
347
+                } catch (\GuzzleHttp\Exception\ClientException $e) {
348
+                    if ($e->getResponse() instanceof ResponseInterface
349
+                        && $e->getResponse()->getStatusCode() === 404) {
350
+                        return false;
351
+                    } else {
352
+                        throw $e;
353
+                    }
354
+                }
355
+
356
+                if ($response->getStatusCode() !== Http::STATUS_OK) {
357
+                    if ($response->getStatusCode() === Http::STATUS_LOCKED) {
358
+                        throw new \OCP\Lock\LockedException($path);
359
+                    } else {
360
+                        Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
361
+                    }
362
+                }
363
+
364
+                return $response->getBody();
365
+            case 'w':
366
+            case 'wb':
367
+            case 'a':
368
+            case 'ab':
369
+            case 'r+':
370
+            case 'w+':
371
+            case 'wb+':
372
+            case 'a+':
373
+            case 'x':
374
+            case 'x+':
375
+            case 'c':
376
+            case 'c+':
377
+                //emulate these
378
+                $tempManager = \OC::$server->getTempManager();
379
+                if (strrpos($path, '.') !== false) {
380
+                    $ext = substr($path, strrpos($path, '.'));
381
+                } else {
382
+                    $ext = '';
383
+                }
384
+                if ($this->file_exists($path)) {
385
+                    if (!$this->isUpdatable($path)) {
386
+                        return false;
387
+                    }
388
+                    if ($mode === 'w' or $mode === 'w+') {
389
+                        $tmpFile = $tempManager->getTemporaryFile($ext);
390
+                    } else {
391
+                        $tmpFile = $this->getCachedFile($path);
392
+                    }
393
+                } else {
394
+                    if (!$this->isCreatable(dirname($path))) {
395
+                        return false;
396
+                    }
397
+                    $tmpFile = $tempManager->getTemporaryFile($ext);
398
+                }
399
+                $handle = fopen($tmpFile, $mode);
400
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
401
+                    $this->writeBack($tmpFile, $path);
402
+                });
403
+        }
404
+    }
405
+
406
+    /**
407
+     * @param string $tmpFile
408
+     */
409
+    public function writeBack($tmpFile, $path) {
410
+        $this->uploadFile($tmpFile, $path);
411
+        unlink($tmpFile);
412
+    }
413
+
414
+    /** {@inheritdoc} */
415
+    public function free_space($path) {
416
+        $this->init();
417
+        $path = $this->cleanPath($path);
418
+        try {
419
+            // TODO: cacheable ?
420
+            $response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
421
+            if ($response === false) {
422
+                return FileInfo::SPACE_UNKNOWN;
423
+            }
424
+            if (isset($response['{DAV:}quota-available-bytes'])) {
425
+                return (int)$response['{DAV:}quota-available-bytes'];
426
+            } else {
427
+                return FileInfo::SPACE_UNKNOWN;
428
+            }
429
+        } catch (\Exception $e) {
430
+            return FileInfo::SPACE_UNKNOWN;
431
+        }
432
+    }
433
+
434
+    /** {@inheritdoc} */
435
+    public function touch($path, $mtime = null) {
436
+        $this->init();
437
+        if (is_null($mtime)) {
438
+            $mtime = time();
439
+        }
440
+        $path = $this->cleanPath($path);
441
+
442
+        // if file exists, update the mtime, else create a new empty file
443
+        if ($this->file_exists($path)) {
444
+            try {
445
+                $this->statCache->remove($path);
446
+                $this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
447
+                // non-owncloud clients might not have accepted the property, need to recheck it
448
+                $response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
449
+                if ($response === false) {
450
+                    return false;
451
+                }
452
+                if (isset($response['{DAV:}getlastmodified'])) {
453
+                    $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
454
+                    if ($remoteMtime !== $mtime) {
455
+                        // server has not accepted the mtime
456
+                        return false;
457
+                    }
458
+                }
459
+            } catch (ClientHttpException $e) {
460
+                if ($e->getHttpStatus() === 501) {
461
+                    return false;
462
+                }
463
+                $this->convertException($e, $path);
464
+                return false;
465
+            } catch (\Exception $e) {
466
+                $this->convertException($e, $path);
467
+                return false;
468
+            }
469
+        } else {
470
+            $this->file_put_contents($path, '');
471
+        }
472
+        return true;
473
+    }
474
+
475
+    /**
476
+     * @param string $path
477
+     * @param string $data
478
+     * @return int
479
+     */
480
+    public function file_put_contents($path, $data) {
481
+        $path = $this->cleanPath($path);
482
+        $result = parent::file_put_contents($path, $data);
483
+        $this->statCache->remove($path);
484
+        return $result;
485
+    }
486
+
487
+    /**
488
+     * @param string $path
489
+     * @param string $target
490
+     */
491
+    protected function uploadFile($path, $target) {
492
+        $this->init();
493
+
494
+        // invalidate
495
+        $target = $this->cleanPath($target);
496
+        $this->statCache->remove($target);
497
+        $source = fopen($path, 'r');
498
+
499
+        $this->httpClientService
500
+            ->newClient()
501
+            ->put($this->createBaseUri() . $this->encodePath($target), [
502
+                'body' => $source,
503
+                'auth' => [$this->user, $this->password]
504
+            ]);
505
+
506
+        $this->removeCachedFile($target);
507
+    }
508
+
509
+    /** {@inheritdoc} */
510
+    public function rename($path1, $path2) {
511
+        $this->init();
512
+        $path1 = $this->cleanPath($path1);
513
+        $path2 = $this->cleanPath($path2);
514
+        try {
515
+            // overwrite directory ?
516
+            if ($this->is_dir($path2)) {
517
+                // needs trailing slash in destination
518
+                $path2 = rtrim($path2, '/') . '/';
519
+            }
520
+            $this->client->request(
521
+                'MOVE',
522
+                $this->encodePath($path1),
523
+                null,
524
+                [
525
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
526
+                ]
527
+            );
528
+            $this->statCache->clear($path1 . '/');
529
+            $this->statCache->clear($path2 . '/');
530
+            $this->statCache->set($path1, false);
531
+            $this->statCache->set($path2, true);
532
+            $this->removeCachedFile($path1);
533
+            $this->removeCachedFile($path2);
534
+            return true;
535
+        } catch (\Exception $e) {
536
+            $this->convertException($e);
537
+        }
538
+        return false;
539
+    }
540
+
541
+    /** {@inheritdoc} */
542
+    public function copy($path1, $path2) {
543
+        $this->init();
544
+        $path1 = $this->cleanPath($path1);
545
+        $path2 = $this->cleanPath($path2);
546
+        try {
547
+            // overwrite directory ?
548
+            if ($this->is_dir($path2)) {
549
+                // needs trailing slash in destination
550
+                $path2 = rtrim($path2, '/') . '/';
551
+            }
552
+            $this->client->request(
553
+                'COPY',
554
+                $this->encodePath($path1),
555
+                null,
556
+                [
557
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
558
+                ]
559
+            );
560
+            $this->statCache->clear($path2 . '/');
561
+            $this->statCache->set($path2, true);
562
+            $this->removeCachedFile($path2);
563
+            return true;
564
+        } catch (\Exception $e) {
565
+            $this->convertException($e);
566
+        }
567
+        return false;
568
+    }
569
+
570
+    /** {@inheritdoc} */
571
+    public function stat($path) {
572
+        try {
573
+            $response = $this->propfind($path);
574
+            if (!$response) {
575
+                return false;
576
+            }
577
+            return [
578
+                'mtime' => strtotime($response['{DAV:}getlastmodified']),
579
+                'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
580
+            ];
581
+        } catch (\Exception $e) {
582
+            $this->convertException($e, $path);
583
+        }
584
+        return array();
585
+    }
586
+
587
+    /** {@inheritdoc} */
588
+    public function getMimeType($path) {
589
+        $remoteMimetype = $this->getMimeTypeFromRemote($path);
590
+        if ($remoteMimetype === 'application/octet-stream') {
591
+            return \OC::$server->getMimeTypeDetector()->detectPath($path);
592
+        } else {
593
+            return $remoteMimetype;
594
+        }
595
+    }
596
+
597
+    public function getMimeTypeFromRemote($path) {
598
+        try {
599
+            $response = $this->propfind($path);
600
+            if ($response === false) {
601
+                return false;
602
+            }
603
+            $responseType = [];
604
+            if (isset($response["{DAV:}resourcetype"])) {
605
+                /** @var ResourceType[] $response */
606
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
607
+            }
608
+            $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
609
+            if ($type == 'dir') {
610
+                return 'httpd/unix-directory';
611
+            } elseif (isset($response['{DAV:}getcontenttype'])) {
612
+                return $response['{DAV:}getcontenttype'];
613
+            } else {
614
+                return 'application/octet-stream';
615
+            }
616
+        } catch (\Exception $e) {
617
+            return false;
618
+        }
619
+    }
620
+
621
+    /**
622
+     * @param string $path
623
+     * @return string
624
+     */
625
+    public function cleanPath($path) {
626
+        if ($path === '') {
627
+            return $path;
628
+        }
629
+        $path = Filesystem::normalizePath($path);
630
+        // remove leading slash
631
+        return substr($path, 1);
632
+    }
633
+
634
+    /**
635
+     * URL encodes the given path but keeps the slashes
636
+     *
637
+     * @param string $path to encode
638
+     * @return string encoded path
639
+     */
640
+    protected function encodePath($path) {
641
+        // slashes need to stay
642
+        return str_replace('%2F', '/', rawurlencode($path));
643
+    }
644
+
645
+    /**
646
+     * @param string $method
647
+     * @param string $path
648
+     * @param string|resource|null $body
649
+     * @param int $expected
650
+     * @return bool
651
+     * @throws StorageInvalidException
652
+     * @throws StorageNotAvailableException
653
+     */
654
+    protected function simpleResponse($method, $path, $body, $expected) {
655
+        $path = $this->cleanPath($path);
656
+        try {
657
+            $response = $this->client->request($method, $this->encodePath($path), $body);
658
+            return $response['statusCode'] == $expected;
659
+        } catch (ClientHttpException $e) {
660
+            if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
661
+                $this->statCache->clear($path . '/');
662
+                $this->statCache->set($path, false);
663
+                return false;
664
+            }
665
+
666
+            $this->convertException($e, $path);
667
+        } catch (\Exception $e) {
668
+            $this->convertException($e, $path);
669
+        }
670
+        return false;
671
+    }
672
+
673
+    /**
674
+     * check if curl is installed
675
+     */
676
+    public static function checkDependencies() {
677
+        return true;
678
+    }
679
+
680
+    /** {@inheritdoc} */
681
+    public function isUpdatable($path) {
682
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
683
+    }
684
+
685
+    /** {@inheritdoc} */
686
+    public function isCreatable($path) {
687
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
688
+    }
689
+
690
+    /** {@inheritdoc} */
691
+    public function isSharable($path) {
692
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
693
+    }
694
+
695
+    /** {@inheritdoc} */
696
+    public function isDeletable($path) {
697
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
698
+    }
699
+
700
+    /** {@inheritdoc} */
701
+    public function getPermissions($path) {
702
+        $this->init();
703
+        $path = $this->cleanPath($path);
704
+        $response = $this->propfind($path);
705
+        if ($response === false) {
706
+            return 0;
707
+        }
708
+        if (isset($response['{http://owncloud.org/ns}permissions'])) {
709
+            return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
710
+        } else if ($this->is_dir($path)) {
711
+            return Constants::PERMISSION_ALL;
712
+        } else if ($this->file_exists($path)) {
713
+            return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
714
+        } else {
715
+            return 0;
716
+        }
717
+    }
718
+
719
+    /** {@inheritdoc} */
720
+    public function getETag($path) {
721
+        $this->init();
722
+        $path = $this->cleanPath($path);
723
+        $response = $this->propfind($path);
724
+        if ($response === false) {
725
+            return null;
726
+        }
727
+        if (isset($response['{DAV:}getetag'])) {
728
+            return trim($response['{DAV:}getetag'], '"');
729
+        }
730
+        return parent::getEtag($path);
731
+    }
732
+
733
+    /**
734
+     * @param string $permissionsString
735
+     * @return int
736
+     */
737
+    protected function parsePermissions($permissionsString) {
738
+        $permissions = Constants::PERMISSION_READ;
739
+        if (strpos($permissionsString, 'R') !== false) {
740
+            $permissions |= Constants::PERMISSION_SHARE;
741
+        }
742
+        if (strpos($permissionsString, 'D') !== false) {
743
+            $permissions |= Constants::PERMISSION_DELETE;
744
+        }
745
+        if (strpos($permissionsString, 'W') !== false) {
746
+            $permissions |= Constants::PERMISSION_UPDATE;
747
+        }
748
+        if (strpos($permissionsString, 'CK') !== false) {
749
+            $permissions |= Constants::PERMISSION_CREATE;
750
+            $permissions |= Constants::PERMISSION_UPDATE;
751
+        }
752
+        return $permissions;
753
+    }
754
+
755
+    /**
756
+     * check if a file or folder has been updated since $time
757
+     *
758
+     * @param string $path
759
+     * @param int $time
760
+     * @throws \OCP\Files\StorageNotAvailableException
761
+     * @return bool
762
+     */
763
+    public function hasUpdated($path, $time) {
764
+        $this->init();
765
+        $path = $this->cleanPath($path);
766
+        try {
767
+            // force refresh for $path
768
+            $this->statCache->remove($path);
769
+            $response = $this->propfind($path);
770
+            if ($response === false) {
771
+                if ($path === '') {
772
+                    // if root is gone it means the storage is not available
773
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
774
+                }
775
+                return false;
776
+            }
777
+            if (isset($response['{DAV:}getetag'])) {
778
+                $cachedData = $this->getCache()->get($path);
779
+                $etag = null;
780
+                if (isset($response['{DAV:}getetag'])) {
781
+                    $etag = trim($response['{DAV:}getetag'], '"');
782
+                }
783
+                if (!empty($etag) && $cachedData['etag'] !== $etag) {
784
+                    return true;
785
+                } else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
786
+                    $sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
787
+                    return $sharePermissions !== $cachedData['permissions'];
788
+                } else if (isset($response['{http://owncloud.org/ns}permissions'])) {
789
+                    $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
790
+                    return $permissions !== $cachedData['permissions'];
791
+                } else {
792
+                    return false;
793
+                }
794
+            } else {
795
+                $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
796
+                return $remoteMtime > $time;
797
+            }
798
+        } catch (ClientHttpException $e) {
799
+            if ($e->getHttpStatus() === 405) {
800
+                if ($path === '') {
801
+                    // if root is gone it means the storage is not available
802
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
803
+                }
804
+                return false;
805
+            }
806
+            $this->convertException($e, $path);
807
+            return false;
808
+        } catch (\Exception $e) {
809
+            $this->convertException($e, $path);
810
+            return false;
811
+        }
812
+    }
813
+
814
+    /**
815
+     * Interpret the given exception and decide whether it is due to an
816
+     * unavailable storage, invalid storage or other.
817
+     * This will either throw StorageInvalidException, StorageNotAvailableException
818
+     * or do nothing.
819
+     *
820
+     * @param Exception $e sabre exception
821
+     * @param string $path optional path from the operation
822
+     *
823
+     * @throws StorageInvalidException if the storage is invalid, for example
824
+     * when the authentication expired or is invalid
825
+     * @throws StorageNotAvailableException if the storage is not available,
826
+     * which might be temporary
827
+     */
828
+    protected function convertException(Exception $e, $path = '') {
829
+        \OC::$server->getLogger()->logException($e, ['app' => 'files_external']);
830
+        if ($e instanceof ClientHttpException) {
831
+            if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
832
+                throw new \OCP\Lock\LockedException($path);
833
+            }
834
+            if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
835
+                // either password was changed or was invalid all along
836
+                throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
837
+            } else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
838
+                // ignore exception for MethodNotAllowed, false will be returned
839
+                return;
840
+            }
841
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
842
+        } else if ($e instanceof ClientException) {
843
+            // connection timeout or refused, server could be temporarily down
844
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
845
+        } else if ($e instanceof \InvalidArgumentException) {
846
+            // parse error because the server returned HTML instead of XML,
847
+            // possibly temporarily down
848
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
849
+        } else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
850
+            // rethrow
851
+            throw $e;
852
+        }
853
+
854
+        // TODO: only log for now, but in the future need to wrap/rethrow exception
855
+    }
856 856
 }
857 857
 
Please login to merge, or discard this patch.