Completed
Pull Request — master (#7387)
by Robin
14:47
created
lib/private/Files/ObjectStore/Swift.php 1 patch
Indentation   +244 added lines, -244 removed lines patch added patch discarded remove patch
@@ -39,249 +39,249 @@
 block discarded – undo
39 39
 
40 40
 class Swift implements IObjectStore {
41 41
 
42
-	/**
43
-	 * @var \OpenCloud\OpenStack
44
-	 */
45
-	private $client;
46
-
47
-	/**
48
-	 * @var array
49
-	 */
50
-	private $params;
51
-
52
-	/**
53
-	 * @var \OpenCloud\ObjectStore\Service
54
-	 */
55
-	private $objectStoreService;
56
-
57
-	/**
58
-	 * @var \OpenCloud\ObjectStore\Resource\Container
59
-	 */
60
-	private $container;
61
-
62
-	private $memcache;
63
-
64
-	public function __construct($params) {
65
-		if (isset($params['bucket'])) {
66
-			$params['container'] = $params['bucket'];
67
-		}
68
-		if (!isset($params['container'])) {
69
-			$params['container'] = 'owncloud';
70
-		}
71
-		if (!isset($params['autocreate'])) {
72
-			// should only be true for tests
73
-			$params['autocreate'] = false;
74
-		}
75
-
76
-		if (isset($params['apiKey'])) {
77
-			$this->client = new Rackspace($params['url'], $params);
78
-			$cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket'];
79
-		} else {
80
-			$this->client = new OpenStack($params['url'], $params);
81
-			$cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket'];
82
-		}
83
-
84
-		$cacheFactory = \OC::$server->getMemCacheFactory();
85
-		$this->memcache = $cacheFactory->create('swift::' . $cacheKey);
86
-
87
-		$this->params = $params;
88
-	}
89
-
90
-	protected function init() {
91
-		if ($this->container) {
92
-			return;
93
-		}
94
-
95
-		$this->importToken();
96
-
97
-		/** @var Token $token */
98
-		$token = $this->client->getTokenObject();
99
-
100
-		if (!$token || $token->hasExpired()) {
101
-			try {
102
-				$this->client->authenticate();
103
-				$this->exportToken();
104
-			} catch (ClientErrorResponseException $e) {
105
-				$statusCode = $e->getResponse()->getStatusCode();
106
-				if ($statusCode == 412) {
107
-					throw new StorageAuthException('Precondition failed, verify the keystone url', $e);
108
-				} else if ($statusCode === 401) {
109
-					throw new StorageAuthException('Authentication failed, verify the username, password and possibly tenant', $e);
110
-				} else {
111
-					throw new StorageAuthException('Unknown error', $e);
112
-				}
113
-			}
114
-		}
115
-
116
-
117
-		/** @var Catalog $catalog */
118
-		$catalog = $this->client->getCatalog();
119
-
120
-		if (isset($this->params['serviceName'])) {
121
-			$serviceName = $this->params['serviceName'];
122
-		} else {
123
-			$serviceName = Service::DEFAULT_NAME;
124
-		}
125
-
126
-		if (isset($this->params['urlType'])) {
127
-			$urlType = $this->params['urlType'];
128
-			if ($urlType !== 'internalURL' && $urlType !== 'publicURL') {
129
-				throw new StorageNotAvailableException('Invalid url type');
130
-			}
131
-		} else {
132
-			$urlType = Service::DEFAULT_URL_TYPE;
133
-		}
134
-
135
-		$catalogItem = $this->getCatalogForService($catalog, $serviceName);
136
-		if (!$catalogItem) {
137
-			$available = implode(', ', $this->getAvailableServiceNames($catalog));
138
-			throw new StorageNotAvailableException(
139
-				"Service $serviceName not found in service catalog, available services: $available"
140
-			);
141
-		} else if (isset($this->params['region'])) {
142
-			$this->validateRegion($catalogItem, $this->params['region']);
143
-		}
144
-
145
-		$this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType);
146
-
147
-		try {
148
-			$this->container = $this->objectStoreService->getContainer($this->params['container']);
149
-		} catch (ClientErrorResponseException $ex) {
150
-			// if the container does not exist and autocreate is true try to create the container on the fly
151
-			if (isset($this->params['autocreate']) && $this->params['autocreate'] === true) {
152
-				$this->container = $this->objectStoreService->createContainer($this->params['container']);
153
-			} else {
154
-				throw $ex;
155
-			}
156
-		}
157
-	}
158
-
159
-	private function exportToken() {
160
-		$export = $this->client->exportCredentials();
161
-		$export['catalog'] = array_map(function (CatalogItem $item) {
162
-			return [
163
-				'name' => $item->getName(),
164
-				'endpoints' => $item->getEndpoints(),
165
-				'type' => $item->getType()
166
-			];
167
-		}, $export['catalog']->getItems());
168
-		$this->memcache->set('token', json_encode($export));
169
-	}
170
-
171
-	private function importToken() {
172
-		$cachedTokenString = $this->memcache->get('token');
173
-		if ($cachedTokenString) {
174
-			$cachedToken = json_decode($cachedTokenString, true);
175
-			$cachedToken['catalog'] = array_map(function (array $item) {
176
-				$itemClass = new \stdClass();
177
-				$itemClass->name = $item['name'];
178
-				$itemClass->endpoints = array_map(function (array $endpoint) {
179
-					return (object) $endpoint;
180
-				}, $item['endpoints']);
181
-				$itemClass->type = $item['type'];
182
-
183
-				return $itemClass;
184
-			}, $cachedToken['catalog']);
185
-			try {
186
-				$this->client->importCredentials($cachedToken);
187
-			} catch (\Exception $e) {
188
-				$this->client->setTokenObject(new Token());
189
-			}
190
-		}
191
-	}
192
-
193
-	/**
194
-	 * @param Catalog $catalog
195
-	 * @param $name
196
-	 * @return null|CatalogItem
197
-	 */
198
-	private function getCatalogForService(Catalog $catalog, $name) {
199
-		foreach ($catalog->getItems() as $item) {
200
-			/** @var CatalogItem $item */
201
-			if ($item->hasType(Service::DEFAULT_TYPE) && $item->hasName($name)) {
202
-				return $item;
203
-			}
204
-		}
205
-
206
-		return null;
207
-	}
208
-
209
-	private function validateRegion(CatalogItem $item, $region) {
210
-		$endPoints = $item->getEndpoints();
211
-		foreach ($endPoints as $endPoint) {
212
-			if ($endPoint->region === $region) {
213
-				return;
214
-			}
215
-		}
216
-
217
-		$availableRegions = implode(', ', array_map(function ($endpoint) {
218
-			return $endpoint->region;
219
-		}, $endPoints));
220
-
221
-		throw new StorageNotAvailableException("Invalid region '$region', available regions: $availableRegions");
222
-	}
223
-
224
-	private function getAvailableServiceNames(Catalog $catalog) {
225
-		return array_map(function (CatalogItem $item) {
226
-			return $item->getName();
227
-		}, array_filter($catalog->getItems(), function (CatalogItem $item) {
228
-			return $item->hasType(Service::DEFAULT_TYPE);
229
-		}));
230
-	}
231
-
232
-	/**
233
-	 * @return string the container name where objects are stored
234
-	 */
235
-	public function getStorageId() {
236
-		return $this->params['container'];
237
-	}
238
-
239
-	/**
240
-	 * @param string $urn the unified resource name used to identify the object
241
-	 * @param resource $stream stream with the data to write
242
-	 * @throws Exception from openstack lib when something goes wrong
243
-	 */
244
-	public function writeObject($urn, $stream) {
245
-		$this->init();
246
-		$this->container->uploadObject($urn, $stream);
247
-	}
248
-
249
-	/**
250
-	 * @param string $urn the unified resource name used to identify the object
251
-	 * @return resource stream with the read data
252
-	 * @throws Exception from openstack lib when something goes wrong
253
-	 */
254
-	public function readObject($urn) {
255
-		$this->init();
256
-		$object = $this->container->getObject($urn);
257
-
258
-		// we need to keep a reference to objectContent or
259
-		// the stream will be closed before we can do anything with it
260
-		/** @var $objectContent \Guzzle\Http\EntityBody * */
261
-		$objectContent = $object->getContent();
262
-		$objectContent->rewind();
263
-
264
-		$stream = $objectContent->getStream();
265
-		// save the object content in the context of the stream to prevent it being gc'd until the stream is closed
266
-		stream_context_set_option($stream, 'swift', 'content', $objectContent);
267
-
268
-		RetryWrapper::wrap($stream);
269
-	}
270
-
271
-	/**
272
-	 * @param string $urn Unified Resource Name
273
-	 * @return void
274
-	 * @throws Exception from openstack lib when something goes wrong
275
-	 */
276
-	public function deleteObject($urn) {
277
-		$this->init();
278
-		// see https://github.com/rackspace/php-opencloud/issues/243#issuecomment-30032242
279
-		$this->container->dataObject()->setName($urn)->delete();
280
-	}
281
-
282
-	public function deleteContainer($recursive = false) {
283
-		$this->init();
284
-		$this->container->delete($recursive);
285
-	}
42
+    /**
43
+     * @var \OpenCloud\OpenStack
44
+     */
45
+    private $client;
46
+
47
+    /**
48
+     * @var array
49
+     */
50
+    private $params;
51
+
52
+    /**
53
+     * @var \OpenCloud\ObjectStore\Service
54
+     */
55
+    private $objectStoreService;
56
+
57
+    /**
58
+     * @var \OpenCloud\ObjectStore\Resource\Container
59
+     */
60
+    private $container;
61
+
62
+    private $memcache;
63
+
64
+    public function __construct($params) {
65
+        if (isset($params['bucket'])) {
66
+            $params['container'] = $params['bucket'];
67
+        }
68
+        if (!isset($params['container'])) {
69
+            $params['container'] = 'owncloud';
70
+        }
71
+        if (!isset($params['autocreate'])) {
72
+            // should only be true for tests
73
+            $params['autocreate'] = false;
74
+        }
75
+
76
+        if (isset($params['apiKey'])) {
77
+            $this->client = new Rackspace($params['url'], $params);
78
+            $cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket'];
79
+        } else {
80
+            $this->client = new OpenStack($params['url'], $params);
81
+            $cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket'];
82
+        }
83
+
84
+        $cacheFactory = \OC::$server->getMemCacheFactory();
85
+        $this->memcache = $cacheFactory->create('swift::' . $cacheKey);
86
+
87
+        $this->params = $params;
88
+    }
89
+
90
+    protected function init() {
91
+        if ($this->container) {
92
+            return;
93
+        }
94
+
95
+        $this->importToken();
96
+
97
+        /** @var Token $token */
98
+        $token = $this->client->getTokenObject();
99
+
100
+        if (!$token || $token->hasExpired()) {
101
+            try {
102
+                $this->client->authenticate();
103
+                $this->exportToken();
104
+            } catch (ClientErrorResponseException $e) {
105
+                $statusCode = $e->getResponse()->getStatusCode();
106
+                if ($statusCode == 412) {
107
+                    throw new StorageAuthException('Precondition failed, verify the keystone url', $e);
108
+                } else if ($statusCode === 401) {
109
+                    throw new StorageAuthException('Authentication failed, verify the username, password and possibly tenant', $e);
110
+                } else {
111
+                    throw new StorageAuthException('Unknown error', $e);
112
+                }
113
+            }
114
+        }
115
+
116
+
117
+        /** @var Catalog $catalog */
118
+        $catalog = $this->client->getCatalog();
119
+
120
+        if (isset($this->params['serviceName'])) {
121
+            $serviceName = $this->params['serviceName'];
122
+        } else {
123
+            $serviceName = Service::DEFAULT_NAME;
124
+        }
125
+
126
+        if (isset($this->params['urlType'])) {
127
+            $urlType = $this->params['urlType'];
128
+            if ($urlType !== 'internalURL' && $urlType !== 'publicURL') {
129
+                throw new StorageNotAvailableException('Invalid url type');
130
+            }
131
+        } else {
132
+            $urlType = Service::DEFAULT_URL_TYPE;
133
+        }
134
+
135
+        $catalogItem = $this->getCatalogForService($catalog, $serviceName);
136
+        if (!$catalogItem) {
137
+            $available = implode(', ', $this->getAvailableServiceNames($catalog));
138
+            throw new StorageNotAvailableException(
139
+                "Service $serviceName not found in service catalog, available services: $available"
140
+            );
141
+        } else if (isset($this->params['region'])) {
142
+            $this->validateRegion($catalogItem, $this->params['region']);
143
+        }
144
+
145
+        $this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType);
146
+
147
+        try {
148
+            $this->container = $this->objectStoreService->getContainer($this->params['container']);
149
+        } catch (ClientErrorResponseException $ex) {
150
+            // if the container does not exist and autocreate is true try to create the container on the fly
151
+            if (isset($this->params['autocreate']) && $this->params['autocreate'] === true) {
152
+                $this->container = $this->objectStoreService->createContainer($this->params['container']);
153
+            } else {
154
+                throw $ex;
155
+            }
156
+        }
157
+    }
158
+
159
+    private function exportToken() {
160
+        $export = $this->client->exportCredentials();
161
+        $export['catalog'] = array_map(function (CatalogItem $item) {
162
+            return [
163
+                'name' => $item->getName(),
164
+                'endpoints' => $item->getEndpoints(),
165
+                'type' => $item->getType()
166
+            ];
167
+        }, $export['catalog']->getItems());
168
+        $this->memcache->set('token', json_encode($export));
169
+    }
170
+
171
+    private function importToken() {
172
+        $cachedTokenString = $this->memcache->get('token');
173
+        if ($cachedTokenString) {
174
+            $cachedToken = json_decode($cachedTokenString, true);
175
+            $cachedToken['catalog'] = array_map(function (array $item) {
176
+                $itemClass = new \stdClass();
177
+                $itemClass->name = $item['name'];
178
+                $itemClass->endpoints = array_map(function (array $endpoint) {
179
+                    return (object) $endpoint;
180
+                }, $item['endpoints']);
181
+                $itemClass->type = $item['type'];
182
+
183
+                return $itemClass;
184
+            }, $cachedToken['catalog']);
185
+            try {
186
+                $this->client->importCredentials($cachedToken);
187
+            } catch (\Exception $e) {
188
+                $this->client->setTokenObject(new Token());
189
+            }
190
+        }
191
+    }
192
+
193
+    /**
194
+     * @param Catalog $catalog
195
+     * @param $name
196
+     * @return null|CatalogItem
197
+     */
198
+    private function getCatalogForService(Catalog $catalog, $name) {
199
+        foreach ($catalog->getItems() as $item) {
200
+            /** @var CatalogItem $item */
201
+            if ($item->hasType(Service::DEFAULT_TYPE) && $item->hasName($name)) {
202
+                return $item;
203
+            }
204
+        }
205
+
206
+        return null;
207
+    }
208
+
209
+    private function validateRegion(CatalogItem $item, $region) {
210
+        $endPoints = $item->getEndpoints();
211
+        foreach ($endPoints as $endPoint) {
212
+            if ($endPoint->region === $region) {
213
+                return;
214
+            }
215
+        }
216
+
217
+        $availableRegions = implode(', ', array_map(function ($endpoint) {
218
+            return $endpoint->region;
219
+        }, $endPoints));
220
+
221
+        throw new StorageNotAvailableException("Invalid region '$region', available regions: $availableRegions");
222
+    }
223
+
224
+    private function getAvailableServiceNames(Catalog $catalog) {
225
+        return array_map(function (CatalogItem $item) {
226
+            return $item->getName();
227
+        }, array_filter($catalog->getItems(), function (CatalogItem $item) {
228
+            return $item->hasType(Service::DEFAULT_TYPE);
229
+        }));
230
+    }
231
+
232
+    /**
233
+     * @return string the container name where objects are stored
234
+     */
235
+    public function getStorageId() {
236
+        return $this->params['container'];
237
+    }
238
+
239
+    /**
240
+     * @param string $urn the unified resource name used to identify the object
241
+     * @param resource $stream stream with the data to write
242
+     * @throws Exception from openstack lib when something goes wrong
243
+     */
244
+    public function writeObject($urn, $stream) {
245
+        $this->init();
246
+        $this->container->uploadObject($urn, $stream);
247
+    }
248
+
249
+    /**
250
+     * @param string $urn the unified resource name used to identify the object
251
+     * @return resource stream with the read data
252
+     * @throws Exception from openstack lib when something goes wrong
253
+     */
254
+    public function readObject($urn) {
255
+        $this->init();
256
+        $object = $this->container->getObject($urn);
257
+
258
+        // we need to keep a reference to objectContent or
259
+        // the stream will be closed before we can do anything with it
260
+        /** @var $objectContent \Guzzle\Http\EntityBody * */
261
+        $objectContent = $object->getContent();
262
+        $objectContent->rewind();
263
+
264
+        $stream = $objectContent->getStream();
265
+        // save the object content in the context of the stream to prevent it being gc'd until the stream is closed
266
+        stream_context_set_option($stream, 'swift', 'content', $objectContent);
267
+
268
+        RetryWrapper::wrap($stream);
269
+    }
270
+
271
+    /**
272
+     * @param string $urn Unified Resource Name
273
+     * @return void
274
+     * @throws Exception from openstack lib when something goes wrong
275
+     */
276
+    public function deleteObject($urn) {
277
+        $this->init();
278
+        // see https://github.com/rackspace/php-opencloud/issues/243#issuecomment-30032242
279
+        $this->container->dataObject()->setName($urn)->delete();
280
+    }
281
+
282
+    public function deleteContainer($recursive = false) {
283
+        $this->init();
284
+        $this->container->delete($recursive);
285
+    }
286 286
 
287 287
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/Swift.php 2 patches
Indentation   +596 added lines, -596 removed lines patch added patch discarded remove patch
@@ -53,601 +53,601 @@
 block discarded – undo
53 53
 
54 54
 class Swift extends \OC\Files\Storage\Common {
55 55
 
56
-	/**
57
-	 * @var \OpenCloud\ObjectStore\Service
58
-	 */
59
-	private $connection;
60
-	/**
61
-	 * @var \OpenCloud\ObjectStore\Resource\Container
62
-	 */
63
-	private $container;
64
-	/**
65
-	 * @var \OpenCloud\OpenStack
66
-	 */
67
-	private $anchor;
68
-	/**
69
-	 * @var string
70
-	 */
71
-	private $bucket;
72
-	/**
73
-	 * Connection parameters
74
-	 *
75
-	 * @var array
76
-	 */
77
-	private $params;
78
-
79
-	/** @var string  */
80
-	private $id;
81
-
82
-	/**
83
-	 * Key value cache mapping path to data object. Maps path to
84
-	 * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing
85
-	 * paths and path to false for not existing paths.
86
-	 * @var \OCP\ICache
87
-	 */
88
-	private $objectCache;
89
-
90
-	/**
91
-	 * @param string $path
92
-	 */
93
-	private function normalizePath($path) {
94
-		$path = trim($path, '/');
95
-
96
-		if (!$path) {
97
-			$path = '.';
98
-		}
99
-
100
-		$path = str_replace('#', '%23', $path);
101
-
102
-		return $path;
103
-	}
104
-
105
-	const SUBCONTAINER_FILE = '.subcontainers';
106
-
107
-	/**
108
-	 * translate directory path to container name
109
-	 *
110
-	 * @param string $path
111
-	 * @return string
112
-	 */
113
-
114
-	/**
115
-	 * Fetches an object from the API.
116
-	 * If the object is cached already or a
117
-	 * failed "doesn't exist" response was cached,
118
-	 * that one will be returned.
119
-	 *
120
-	 * @param string $path
121
-	 * @return \OpenCloud\ObjectStore\Resource\DataObject|bool object
122
-	 * or false if the object did not exist
123
-	 */
124
-	private function fetchObject($path) {
125
-		if ($this->objectCache->hasKey($path)) {
126
-			// might be "false" if object did not exist from last check
127
-			return $this->objectCache->get($path);
128
-		}
129
-		try {
130
-			$object = $this->getContainer()->getPartialObject($path);
131
-			$this->objectCache->set($path, $object);
132
-			return $object;
133
-		} catch (ClientErrorResponseException $e) {
134
-			// this exception happens when the object does not exist, which
135
-			// is expected in most cases
136
-			$this->objectCache->set($path, false);
137
-			return false;
138
-		} catch (ClientErrorResponseException $e) {
139
-			// Expected response is "404 Not Found", so only log if it isn't
140
-			if ($e->getResponse()->getStatusCode() !== 404) {
141
-				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
142
-			}
143
-			return false;
144
-		}
145
-	}
146
-
147
-	/**
148
-	 * Returns whether the given path exists.
149
-	 *
150
-	 * @param string $path
151
-	 *
152
-	 * @return bool true if the object exist, false otherwise
153
-	 */
154
-	private function doesObjectExist($path) {
155
-		return $this->fetchObject($path) !== false;
156
-	}
157
-
158
-	public function __construct($params) {
159
-		if ((empty($params['key']) and empty($params['password']))
160
-			or empty($params['user']) or empty($params['bucket'])
161
-			or empty($params['region'])
162
-		) {
163
-			throw new \Exception("API Key or password, Username, Bucket and Region have to be configured.");
164
-		}
165
-
166
-		$this->id = 'swift::' . $params['user'] . md5($params['bucket']);
167
-
168
-		$bucketUrl = Url::factory($params['bucket']);
169
-		if ($bucketUrl->isAbsolute()) {
170
-			$this->bucket = end(($bucketUrl->getPathSegments()));
171
-			$params['endpoint_url'] = $bucketUrl->addPath('..')->normalizePath();
172
-		} else {
173
-			$this->bucket = $params['bucket'];
174
-		}
175
-
176
-		if (empty($params['url'])) {
177
-			$params['url'] = 'https://identity.api.rackspacecloud.com/v2.0/';
178
-		}
179
-
180
-		if (empty($params['service_name'])) {
181
-			$params['service_name'] = 'cloudFiles';
182
-		}
183
-
184
-		$this->params = $params;
185
-		// FIXME: private class...
186
-		$this->objectCache = new \OC\Cache\CappedMemoryCache();
187
-	}
188
-
189
-	public function mkdir($path) {
190
-		$path = $this->normalizePath($path);
191
-
192
-		if ($this->is_dir($path)) {
193
-			return false;
194
-		}
195
-
196
-		if ($path !== '.') {
197
-			$path .= '/';
198
-		}
199
-
200
-		try {
201
-			$customHeaders = array('content-type' => 'httpd/unix-directory');
202
-			$metadataHeaders = DataObject::stockHeaders(array());
203
-			$allHeaders = $customHeaders + $metadataHeaders;
204
-			$this->getContainer()->uploadObject($path, '', $allHeaders);
205
-			// invalidate so that the next access gets the real object
206
-			// with all properties
207
-			$this->objectCache->remove($path);
208
-		} catch (Exceptions\CreateUpdateError $e) {
209
-			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
210
-			return false;
211
-		}
212
-
213
-		return true;
214
-	}
215
-
216
-	public function file_exists($path) {
217
-		$path = $this->normalizePath($path);
218
-
219
-		if ($path !== '.' && $this->is_dir($path)) {
220
-			$path .= '/';
221
-		}
222
-
223
-		return $this->doesObjectExist($path);
224
-	}
225
-
226
-	public function rmdir($path) {
227
-		$path = $this->normalizePath($path);
228
-
229
-		if (!$this->is_dir($path) || !$this->isDeletable($path)) {
230
-			return false;
231
-		}
232
-
233
-		$dh = $this->opendir($path);
234
-		while ($file = readdir($dh)) {
235
-			if (\OC\Files\Filesystem::isIgnoredDir($file)) {
236
-				continue;
237
-			}
238
-
239
-			if ($this->is_dir($path . '/' . $file)) {
240
-				$this->rmdir($path . '/' . $file);
241
-			} else {
242
-				$this->unlink($path . '/' . $file);
243
-			}
244
-		}
245
-
246
-		try {
247
-			$this->getContainer()->dataObject()->setName($path . '/')->delete();
248
-			$this->objectCache->remove($path . '/');
249
-		} catch (Exceptions\DeleteError $e) {
250
-			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
251
-			return false;
252
-		}
253
-
254
-		return true;
255
-	}
256
-
257
-	public function opendir($path) {
258
-		$path = $this->normalizePath($path);
259
-
260
-		if ($path === '.') {
261
-			$path = '';
262
-		} else {
263
-			$path .= '/';
264
-		}
265
-
266
-		$path = str_replace('%23', '#', $path); // the prefix is sent as a query param, so revert the encoding of #
267
-
268
-		try {
269
-			$files = array();
270
-			/** @var OpenCloud\Common\Collection $objects */
271
-			$objects = $this->getContainer()->objectList(array(
272
-				'prefix' => $path,
273
-				'delimiter' => '/'
274
-			));
275
-
276
-			/** @var OpenCloud\ObjectStore\Resource\DataObject $object */
277
-			foreach ($objects as $object) {
278
-				$file = basename($object->getName());
279
-				if ($file !== basename($path) && $file !== '.') {
280
-					$files[] = $file;
281
-				}
282
-			}
283
-
284
-			return IteratorDirectory::wrap($files);
285
-		} catch (\Exception $e) {
286
-			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
287
-			return false;
288
-		}
289
-
290
-	}
291
-
292
-	public function stat($path) {
293
-		$path = $this->normalizePath($path);
294
-
295
-		if ($path === '.') {
296
-			$path = '';
297
-		} else if ($this->is_dir($path)) {
298
-			$path .= '/';
299
-		}
300
-
301
-		try {
302
-			/** @var DataObject $object */
303
-			$object = $this->fetchObject($path);
304
-			if (!$object) {
305
-				return false;
306
-			}
307
-		} catch (ClientErrorResponseException $e) {
308
-			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
309
-			return false;
310
-		}
311
-
312
-		$dateTime = \DateTime::createFromFormat(\DateTime::RFC1123, $object->getLastModified());
313
-		if ($dateTime !== false) {
314
-			$mtime = $dateTime->getTimestamp();
315
-		} else {
316
-			$mtime = null;
317
-		}
318
-		$objectMetadata = $object->getMetadata();
319
-		$metaTimestamp = $objectMetadata->getProperty('timestamp');
320
-		if (isset($metaTimestamp)) {
321
-			$mtime = $metaTimestamp;
322
-		}
323
-
324
-		if (!empty($mtime)) {
325
-			$mtime = floor($mtime);
326
-		}
327
-
328
-		$stat = array();
329
-		$stat['size'] = (int)$object->getContentLength();
330
-		$stat['mtime'] = $mtime;
331
-		$stat['atime'] = time();
332
-		return $stat;
333
-	}
334
-
335
-	public function filetype($path) {
336
-		$path = $this->normalizePath($path);
337
-
338
-		if ($path !== '.' && $this->doesObjectExist($path)) {
339
-			return 'file';
340
-		}
341
-
342
-		if ($path !== '.') {
343
-			$path .= '/';
344
-		}
345
-
346
-		if ($this->doesObjectExist($path)) {
347
-			return 'dir';
348
-		}
349
-	}
350
-
351
-	public function unlink($path) {
352
-		$path = $this->normalizePath($path);
353
-
354
-		if ($this->is_dir($path)) {
355
-			return $this->rmdir($path);
356
-		}
357
-
358
-		try {
359
-			$this->getContainer()->dataObject()->setName($path)->delete();
360
-			$this->objectCache->remove($path);
361
-			$this->objectCache->remove($path . '/');
362
-		} catch (ClientErrorResponseException $e) {
363
-			if ($e->getResponse()->getStatusCode() !== 404) {
364
-				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
365
-			}
366
-			return false;
367
-		}
368
-
369
-		return true;
370
-	}
371
-
372
-	public function fopen($path, $mode) {
373
-		$path = $this->normalizePath($path);
374
-
375
-		switch ($mode) {
376
-			case 'a':
377
-			case 'ab':
378
-			case 'a+':
379
-				return false;
380
-			case 'r':
381
-			case 'rb':
382
-				try {
383
-					$c = $this->getContainer();
384
-					$streamFactory = new \Guzzle\Stream\PhpStreamRequestFactory();
385
-					/** @var \OpenCloud\Common\Http\Client $client */
386
-					$client = $c->getClient();
387
-					$streamInterface = $streamFactory->fromRequest($client->get($c->getUrl($path)));
388
-					$streamInterface->rewind();
389
-					$stream = $streamInterface->getStream();
390
-					stream_context_set_option($stream, 'swift','content', $streamInterface);
391
-					if(!strrpos($streamInterface
392
-						->getMetaData('wrapper_data')[0], '404 Not Found')) {
393
-						return RetryWrapper::wrap($stream);
394
-					}
395
-					return false;
396
-				} catch (\Guzzle\Http\Exception\BadResponseException $e) {
397
-					\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
398
-					return false;
399
-				}
400
-			case 'w':
401
-			case 'wb':
402
-			case 'r+':
403
-			case 'w+':
404
-			case 'wb+':
405
-			case 'x':
406
-			case 'x+':
407
-			case 'c':
408
-			case 'c+':
409
-				if (strrpos($path, '.') !== false) {
410
-					$ext = substr($path, strrpos($path, '.'));
411
-				} else {
412
-					$ext = '';
413
-				}
414
-				$tmpFile = \OCP\Files::tmpFile($ext);
415
-				// Fetch existing file if required
416
-				if ($mode[0] !== 'w' && $this->file_exists($path)) {
417
-					if ($mode[0] === 'x') {
418
-						// File cannot already exist
419
-						return false;
420
-					}
421
-					$source = $this->fopen($path, 'r');
422
-					file_put_contents($tmpFile, $source);
423
-				}
424
-				$handle = fopen($tmpFile, $mode);
425
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
426
-					$this->writeBack($tmpFile, $path);
427
-				});
428
-		}
429
-	}
430
-
431
-	public function touch($path, $mtime = null) {
432
-		$path = $this->normalizePath($path);
433
-		if (is_null($mtime)) {
434
-			$mtime = time();
435
-		}
436
-		$metadata = array('timestamp' => $mtime);
437
-		if ($this->file_exists($path)) {
438
-			if ($this->is_dir($path) && $path !== '.') {
439
-				$path .= '/';
440
-			}
441
-
442
-			$object = $this->fetchObject($path);
443
-			if ($object->saveMetadata($metadata)) {
444
-				// invalidate target object to force repopulation on fetch
445
-				$this->objectCache->remove($path);
446
-			}
447
-			return true;
448
-		} else {
449
-			$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
450
-			$customHeaders = array('content-type' => $mimeType);
451
-			$metadataHeaders = DataObject::stockHeaders($metadata);
452
-			$allHeaders = $customHeaders + $metadataHeaders;
453
-			$this->getContainer()->uploadObject($path, '', $allHeaders);
454
-			// invalidate target object to force repopulation on fetch
455
-			$this->objectCache->remove($path);
456
-			return true;
457
-		}
458
-	}
459
-
460
-	public function copy($path1, $path2) {
461
-		$path1 = $this->normalizePath($path1);
462
-		$path2 = $this->normalizePath($path2);
463
-
464
-		$fileType = $this->filetype($path1);
465
-		if ($fileType === 'file') {
466
-
467
-			// make way
468
-			$this->unlink($path2);
469
-
470
-			try {
471
-				$source = $this->fetchObject($path1);
472
-				$source->copy($this->bucket . '/' . $path2);
473
-				// invalidate target object to force repopulation on fetch
474
-				$this->objectCache->remove($path2);
475
-				$this->objectCache->remove($path2 . '/');
476
-			} catch (ClientErrorResponseException $e) {
477
-				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
478
-				return false;
479
-			}
480
-
481
-		} else if ($fileType === 'dir') {
482
-
483
-			// make way
484
-			$this->unlink($path2);
485
-
486
-			try {
487
-				$source = $this->fetchObject($path1 . '/');
488
-				$source->copy($this->bucket . '/' . $path2 . '/');
489
-				// invalidate target object to force repopulation on fetch
490
-				$this->objectCache->remove($path2);
491
-				$this->objectCache->remove($path2 . '/');
492
-			} catch (ClientErrorResponseException $e) {
493
-				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
494
-				return false;
495
-			}
496
-
497
-			$dh = $this->opendir($path1);
498
-			while ($file = readdir($dh)) {
499
-				if (\OC\Files\Filesystem::isIgnoredDir($file)) {
500
-					continue;
501
-				}
502
-
503
-				$source = $path1 . '/' . $file;
504
-				$target = $path2 . '/' . $file;
505
-				$this->copy($source, $target);
506
-			}
507
-
508
-		} else {
509
-			//file does not exist
510
-			return false;
511
-		}
512
-
513
-		return true;
514
-	}
515
-
516
-	public function rename($path1, $path2) {
517
-		$path1 = $this->normalizePath($path1);
518
-		$path2 = $this->normalizePath($path2);
519
-
520
-		$fileType = $this->filetype($path1);
521
-
522
-		if ($fileType === 'dir' || $fileType === 'file') {
523
-			// copy
524
-			if ($this->copy($path1, $path2) === false) {
525
-				return false;
526
-			}
527
-
528
-			// cleanup
529
-			if ($this->unlink($path1) === false) {
530
-				$this->unlink($path2);
531
-				return false;
532
-			}
533
-
534
-			return true;
535
-		}
536
-
537
-		return false;
538
-	}
539
-
540
-	public function getId() {
541
-		return $this->id;
542
-	}
543
-
544
-	/**
545
-	 * Returns the connection
546
-	 *
547
-	 * @return OpenCloud\ObjectStore\Service connected client
548
-	 * @throws \Exception if connection could not be made
549
-	 */
550
-	public function getConnection() {
551
-		if (!is_null($this->connection)) {
552
-			return $this->connection;
553
-		}
554
-
555
-		$settings = array(
556
-			'username' => $this->params['user'],
557
-		);
558
-
559
-		if (!empty($this->params['password'])) {
560
-			$settings['password'] = $this->params['password'];
561
-		} else if (!empty($this->params['key'])) {
562
-			$settings['apiKey'] = $this->params['key'];
563
-		}
564
-
565
-		if (!empty($this->params['tenant'])) {
566
-			$settings['tenantName'] = $this->params['tenant'];
567
-		}
568
-
569
-		if (!empty($this->params['timeout'])) {
570
-			$settings['timeout'] = $this->params['timeout'];
571
-		}
572
-
573
-		if (isset($settings['apiKey'])) {
574
-			$this->anchor = new Rackspace($this->params['url'], $settings);
575
-		} else {
576
-			$this->anchor = new OpenStack($this->params['url'], $settings);
577
-		}
578
-
579
-		$connection = $this->anchor->objectStoreService($this->params['service_name'], $this->params['region']);
580
-
581
-		if (!empty($this->params['endpoint_url'])) {
582
-			$endpoint = $connection->getEndpoint();
583
-			$endpoint->setPublicUrl($this->params['endpoint_url']);
584
-			$endpoint->setPrivateUrl($this->params['endpoint_url']);
585
-			$connection->setEndpoint($endpoint);
586
-		}
587
-
588
-		$this->connection = $connection;
589
-
590
-		return $this->connection;
591
-	}
592
-
593
-	/**
594
-	 * Returns the initialized object store container.
595
-	 *
596
-	 * @return OpenCloud\ObjectStore\Resource\Container
597
-	 */
598
-	public function getContainer() {
599
-		if (!is_null($this->container)) {
600
-			return $this->container;
601
-		}
602
-
603
-		try {
604
-			$this->container = $this->getConnection()->getContainer($this->bucket);
605
-		} catch (ClientErrorResponseException $e) {
606
-			$this->container = $this->getConnection()->createContainer($this->bucket);
607
-		}
608
-
609
-		if (!$this->file_exists('.')) {
610
-			$this->mkdir('.');
611
-		}
612
-
613
-		return $this->container;
614
-	}
615
-
616
-	public function writeBack($tmpFile, $path) {
617
-		$fileData = fopen($tmpFile, 'r');
618
-		$this->getContainer()->uploadObject($path, $fileData);
619
-		// invalidate target object to force repopulation on fetch
620
-		$this->objectCache->remove($path);
621
-		unlink($tmpFile);
622
-	}
623
-
624
-	public function hasUpdated($path, $time) {
625
-		if ($this->is_file($path)) {
626
-			return parent::hasUpdated($path, $time);
627
-		}
628
-		$path = $this->normalizePath($path);
629
-		$dh = $this->opendir($path);
630
-		$content = array();
631
-		while (($file = readdir($dh)) !== false) {
632
-			$content[] = $file;
633
-		}
634
-		if ($path === '.') {
635
-			$path = '';
636
-		}
637
-		$cachedContent = $this->getCache()->getFolderContents($path);
638
-		$cachedNames = array_map(function ($content) {
639
-			return $content['name'];
640
-		}, $cachedContent);
641
-		sort($cachedNames);
642
-		sort($content);
643
-		return $cachedNames !== $content;
644
-	}
645
-
646
-	/**
647
-	 * check if curl is installed
648
-	 */
649
-	public static function checkDependencies() {
650
-		return true;
651
-	}
56
+    /**
57
+     * @var \OpenCloud\ObjectStore\Service
58
+     */
59
+    private $connection;
60
+    /**
61
+     * @var \OpenCloud\ObjectStore\Resource\Container
62
+     */
63
+    private $container;
64
+    /**
65
+     * @var \OpenCloud\OpenStack
66
+     */
67
+    private $anchor;
68
+    /**
69
+     * @var string
70
+     */
71
+    private $bucket;
72
+    /**
73
+     * Connection parameters
74
+     *
75
+     * @var array
76
+     */
77
+    private $params;
78
+
79
+    /** @var string  */
80
+    private $id;
81
+
82
+    /**
83
+     * Key value cache mapping path to data object. Maps path to
84
+     * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing
85
+     * paths and path to false for not existing paths.
86
+     * @var \OCP\ICache
87
+     */
88
+    private $objectCache;
89
+
90
+    /**
91
+     * @param string $path
92
+     */
93
+    private function normalizePath($path) {
94
+        $path = trim($path, '/');
95
+
96
+        if (!$path) {
97
+            $path = '.';
98
+        }
99
+
100
+        $path = str_replace('#', '%23', $path);
101
+
102
+        return $path;
103
+    }
104
+
105
+    const SUBCONTAINER_FILE = '.subcontainers';
106
+
107
+    /**
108
+     * translate directory path to container name
109
+     *
110
+     * @param string $path
111
+     * @return string
112
+     */
113
+
114
+    /**
115
+     * Fetches an object from the API.
116
+     * If the object is cached already or a
117
+     * failed "doesn't exist" response was cached,
118
+     * that one will be returned.
119
+     *
120
+     * @param string $path
121
+     * @return \OpenCloud\ObjectStore\Resource\DataObject|bool object
122
+     * or false if the object did not exist
123
+     */
124
+    private function fetchObject($path) {
125
+        if ($this->objectCache->hasKey($path)) {
126
+            // might be "false" if object did not exist from last check
127
+            return $this->objectCache->get($path);
128
+        }
129
+        try {
130
+            $object = $this->getContainer()->getPartialObject($path);
131
+            $this->objectCache->set($path, $object);
132
+            return $object;
133
+        } catch (ClientErrorResponseException $e) {
134
+            // this exception happens when the object does not exist, which
135
+            // is expected in most cases
136
+            $this->objectCache->set($path, false);
137
+            return false;
138
+        } catch (ClientErrorResponseException $e) {
139
+            // Expected response is "404 Not Found", so only log if it isn't
140
+            if ($e->getResponse()->getStatusCode() !== 404) {
141
+                \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
142
+            }
143
+            return false;
144
+        }
145
+    }
146
+
147
+    /**
148
+     * Returns whether the given path exists.
149
+     *
150
+     * @param string $path
151
+     *
152
+     * @return bool true if the object exist, false otherwise
153
+     */
154
+    private function doesObjectExist($path) {
155
+        return $this->fetchObject($path) !== false;
156
+    }
157
+
158
+    public function __construct($params) {
159
+        if ((empty($params['key']) and empty($params['password']))
160
+            or empty($params['user']) or empty($params['bucket'])
161
+            or empty($params['region'])
162
+        ) {
163
+            throw new \Exception("API Key or password, Username, Bucket and Region have to be configured.");
164
+        }
165
+
166
+        $this->id = 'swift::' . $params['user'] . md5($params['bucket']);
167
+
168
+        $bucketUrl = Url::factory($params['bucket']);
169
+        if ($bucketUrl->isAbsolute()) {
170
+            $this->bucket = end(($bucketUrl->getPathSegments()));
171
+            $params['endpoint_url'] = $bucketUrl->addPath('..')->normalizePath();
172
+        } else {
173
+            $this->bucket = $params['bucket'];
174
+        }
175
+
176
+        if (empty($params['url'])) {
177
+            $params['url'] = 'https://identity.api.rackspacecloud.com/v2.0/';
178
+        }
179
+
180
+        if (empty($params['service_name'])) {
181
+            $params['service_name'] = 'cloudFiles';
182
+        }
183
+
184
+        $this->params = $params;
185
+        // FIXME: private class...
186
+        $this->objectCache = new \OC\Cache\CappedMemoryCache();
187
+    }
188
+
189
+    public function mkdir($path) {
190
+        $path = $this->normalizePath($path);
191
+
192
+        if ($this->is_dir($path)) {
193
+            return false;
194
+        }
195
+
196
+        if ($path !== '.') {
197
+            $path .= '/';
198
+        }
199
+
200
+        try {
201
+            $customHeaders = array('content-type' => 'httpd/unix-directory');
202
+            $metadataHeaders = DataObject::stockHeaders(array());
203
+            $allHeaders = $customHeaders + $metadataHeaders;
204
+            $this->getContainer()->uploadObject($path, '', $allHeaders);
205
+            // invalidate so that the next access gets the real object
206
+            // with all properties
207
+            $this->objectCache->remove($path);
208
+        } catch (Exceptions\CreateUpdateError $e) {
209
+            \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
210
+            return false;
211
+        }
212
+
213
+        return true;
214
+    }
215
+
216
+    public function file_exists($path) {
217
+        $path = $this->normalizePath($path);
218
+
219
+        if ($path !== '.' && $this->is_dir($path)) {
220
+            $path .= '/';
221
+        }
222
+
223
+        return $this->doesObjectExist($path);
224
+    }
225
+
226
+    public function rmdir($path) {
227
+        $path = $this->normalizePath($path);
228
+
229
+        if (!$this->is_dir($path) || !$this->isDeletable($path)) {
230
+            return false;
231
+        }
232
+
233
+        $dh = $this->opendir($path);
234
+        while ($file = readdir($dh)) {
235
+            if (\OC\Files\Filesystem::isIgnoredDir($file)) {
236
+                continue;
237
+            }
238
+
239
+            if ($this->is_dir($path . '/' . $file)) {
240
+                $this->rmdir($path . '/' . $file);
241
+            } else {
242
+                $this->unlink($path . '/' . $file);
243
+            }
244
+        }
245
+
246
+        try {
247
+            $this->getContainer()->dataObject()->setName($path . '/')->delete();
248
+            $this->objectCache->remove($path . '/');
249
+        } catch (Exceptions\DeleteError $e) {
250
+            \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
251
+            return false;
252
+        }
253
+
254
+        return true;
255
+    }
256
+
257
+    public function opendir($path) {
258
+        $path = $this->normalizePath($path);
259
+
260
+        if ($path === '.') {
261
+            $path = '';
262
+        } else {
263
+            $path .= '/';
264
+        }
265
+
266
+        $path = str_replace('%23', '#', $path); // the prefix is sent as a query param, so revert the encoding of #
267
+
268
+        try {
269
+            $files = array();
270
+            /** @var OpenCloud\Common\Collection $objects */
271
+            $objects = $this->getContainer()->objectList(array(
272
+                'prefix' => $path,
273
+                'delimiter' => '/'
274
+            ));
275
+
276
+            /** @var OpenCloud\ObjectStore\Resource\DataObject $object */
277
+            foreach ($objects as $object) {
278
+                $file = basename($object->getName());
279
+                if ($file !== basename($path) && $file !== '.') {
280
+                    $files[] = $file;
281
+                }
282
+            }
283
+
284
+            return IteratorDirectory::wrap($files);
285
+        } catch (\Exception $e) {
286
+            \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
287
+            return false;
288
+        }
289
+
290
+    }
291
+
292
+    public function stat($path) {
293
+        $path = $this->normalizePath($path);
294
+
295
+        if ($path === '.') {
296
+            $path = '';
297
+        } else if ($this->is_dir($path)) {
298
+            $path .= '/';
299
+        }
300
+
301
+        try {
302
+            /** @var DataObject $object */
303
+            $object = $this->fetchObject($path);
304
+            if (!$object) {
305
+                return false;
306
+            }
307
+        } catch (ClientErrorResponseException $e) {
308
+            \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
309
+            return false;
310
+        }
311
+
312
+        $dateTime = \DateTime::createFromFormat(\DateTime::RFC1123, $object->getLastModified());
313
+        if ($dateTime !== false) {
314
+            $mtime = $dateTime->getTimestamp();
315
+        } else {
316
+            $mtime = null;
317
+        }
318
+        $objectMetadata = $object->getMetadata();
319
+        $metaTimestamp = $objectMetadata->getProperty('timestamp');
320
+        if (isset($metaTimestamp)) {
321
+            $mtime = $metaTimestamp;
322
+        }
323
+
324
+        if (!empty($mtime)) {
325
+            $mtime = floor($mtime);
326
+        }
327
+
328
+        $stat = array();
329
+        $stat['size'] = (int)$object->getContentLength();
330
+        $stat['mtime'] = $mtime;
331
+        $stat['atime'] = time();
332
+        return $stat;
333
+    }
334
+
335
+    public function filetype($path) {
336
+        $path = $this->normalizePath($path);
337
+
338
+        if ($path !== '.' && $this->doesObjectExist($path)) {
339
+            return 'file';
340
+        }
341
+
342
+        if ($path !== '.') {
343
+            $path .= '/';
344
+        }
345
+
346
+        if ($this->doesObjectExist($path)) {
347
+            return 'dir';
348
+        }
349
+    }
350
+
351
+    public function unlink($path) {
352
+        $path = $this->normalizePath($path);
353
+
354
+        if ($this->is_dir($path)) {
355
+            return $this->rmdir($path);
356
+        }
357
+
358
+        try {
359
+            $this->getContainer()->dataObject()->setName($path)->delete();
360
+            $this->objectCache->remove($path);
361
+            $this->objectCache->remove($path . '/');
362
+        } catch (ClientErrorResponseException $e) {
363
+            if ($e->getResponse()->getStatusCode() !== 404) {
364
+                \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
365
+            }
366
+            return false;
367
+        }
368
+
369
+        return true;
370
+    }
371
+
372
+    public function fopen($path, $mode) {
373
+        $path = $this->normalizePath($path);
374
+
375
+        switch ($mode) {
376
+            case 'a':
377
+            case 'ab':
378
+            case 'a+':
379
+                return false;
380
+            case 'r':
381
+            case 'rb':
382
+                try {
383
+                    $c = $this->getContainer();
384
+                    $streamFactory = new \Guzzle\Stream\PhpStreamRequestFactory();
385
+                    /** @var \OpenCloud\Common\Http\Client $client */
386
+                    $client = $c->getClient();
387
+                    $streamInterface = $streamFactory->fromRequest($client->get($c->getUrl($path)));
388
+                    $streamInterface->rewind();
389
+                    $stream = $streamInterface->getStream();
390
+                    stream_context_set_option($stream, 'swift','content', $streamInterface);
391
+                    if(!strrpos($streamInterface
392
+                        ->getMetaData('wrapper_data')[0], '404 Not Found')) {
393
+                        return RetryWrapper::wrap($stream);
394
+                    }
395
+                    return false;
396
+                } catch (\Guzzle\Http\Exception\BadResponseException $e) {
397
+                    \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
398
+                    return false;
399
+                }
400
+            case 'w':
401
+            case 'wb':
402
+            case 'r+':
403
+            case 'w+':
404
+            case 'wb+':
405
+            case 'x':
406
+            case 'x+':
407
+            case 'c':
408
+            case 'c+':
409
+                if (strrpos($path, '.') !== false) {
410
+                    $ext = substr($path, strrpos($path, '.'));
411
+                } else {
412
+                    $ext = '';
413
+                }
414
+                $tmpFile = \OCP\Files::tmpFile($ext);
415
+                // Fetch existing file if required
416
+                if ($mode[0] !== 'w' && $this->file_exists($path)) {
417
+                    if ($mode[0] === 'x') {
418
+                        // File cannot already exist
419
+                        return false;
420
+                    }
421
+                    $source = $this->fopen($path, 'r');
422
+                    file_put_contents($tmpFile, $source);
423
+                }
424
+                $handle = fopen($tmpFile, $mode);
425
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
426
+                    $this->writeBack($tmpFile, $path);
427
+                });
428
+        }
429
+    }
430
+
431
+    public function touch($path, $mtime = null) {
432
+        $path = $this->normalizePath($path);
433
+        if (is_null($mtime)) {
434
+            $mtime = time();
435
+        }
436
+        $metadata = array('timestamp' => $mtime);
437
+        if ($this->file_exists($path)) {
438
+            if ($this->is_dir($path) && $path !== '.') {
439
+                $path .= '/';
440
+            }
441
+
442
+            $object = $this->fetchObject($path);
443
+            if ($object->saveMetadata($metadata)) {
444
+                // invalidate target object to force repopulation on fetch
445
+                $this->objectCache->remove($path);
446
+            }
447
+            return true;
448
+        } else {
449
+            $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
450
+            $customHeaders = array('content-type' => $mimeType);
451
+            $metadataHeaders = DataObject::stockHeaders($metadata);
452
+            $allHeaders = $customHeaders + $metadataHeaders;
453
+            $this->getContainer()->uploadObject($path, '', $allHeaders);
454
+            // invalidate target object to force repopulation on fetch
455
+            $this->objectCache->remove($path);
456
+            return true;
457
+        }
458
+    }
459
+
460
+    public function copy($path1, $path2) {
461
+        $path1 = $this->normalizePath($path1);
462
+        $path2 = $this->normalizePath($path2);
463
+
464
+        $fileType = $this->filetype($path1);
465
+        if ($fileType === 'file') {
466
+
467
+            // make way
468
+            $this->unlink($path2);
469
+
470
+            try {
471
+                $source = $this->fetchObject($path1);
472
+                $source->copy($this->bucket . '/' . $path2);
473
+                // invalidate target object to force repopulation on fetch
474
+                $this->objectCache->remove($path2);
475
+                $this->objectCache->remove($path2 . '/');
476
+            } catch (ClientErrorResponseException $e) {
477
+                \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
478
+                return false;
479
+            }
480
+
481
+        } else if ($fileType === 'dir') {
482
+
483
+            // make way
484
+            $this->unlink($path2);
485
+
486
+            try {
487
+                $source = $this->fetchObject($path1 . '/');
488
+                $source->copy($this->bucket . '/' . $path2 . '/');
489
+                // invalidate target object to force repopulation on fetch
490
+                $this->objectCache->remove($path2);
491
+                $this->objectCache->remove($path2 . '/');
492
+            } catch (ClientErrorResponseException $e) {
493
+                \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
494
+                return false;
495
+            }
496
+
497
+            $dh = $this->opendir($path1);
498
+            while ($file = readdir($dh)) {
499
+                if (\OC\Files\Filesystem::isIgnoredDir($file)) {
500
+                    continue;
501
+                }
502
+
503
+                $source = $path1 . '/' . $file;
504
+                $target = $path2 . '/' . $file;
505
+                $this->copy($source, $target);
506
+            }
507
+
508
+        } else {
509
+            //file does not exist
510
+            return false;
511
+        }
512
+
513
+        return true;
514
+    }
515
+
516
+    public function rename($path1, $path2) {
517
+        $path1 = $this->normalizePath($path1);
518
+        $path2 = $this->normalizePath($path2);
519
+
520
+        $fileType = $this->filetype($path1);
521
+
522
+        if ($fileType === 'dir' || $fileType === 'file') {
523
+            // copy
524
+            if ($this->copy($path1, $path2) === false) {
525
+                return false;
526
+            }
527
+
528
+            // cleanup
529
+            if ($this->unlink($path1) === false) {
530
+                $this->unlink($path2);
531
+                return false;
532
+            }
533
+
534
+            return true;
535
+        }
536
+
537
+        return false;
538
+    }
539
+
540
+    public function getId() {
541
+        return $this->id;
542
+    }
543
+
544
+    /**
545
+     * Returns the connection
546
+     *
547
+     * @return OpenCloud\ObjectStore\Service connected client
548
+     * @throws \Exception if connection could not be made
549
+     */
550
+    public function getConnection() {
551
+        if (!is_null($this->connection)) {
552
+            return $this->connection;
553
+        }
554
+
555
+        $settings = array(
556
+            'username' => $this->params['user'],
557
+        );
558
+
559
+        if (!empty($this->params['password'])) {
560
+            $settings['password'] = $this->params['password'];
561
+        } else if (!empty($this->params['key'])) {
562
+            $settings['apiKey'] = $this->params['key'];
563
+        }
564
+
565
+        if (!empty($this->params['tenant'])) {
566
+            $settings['tenantName'] = $this->params['tenant'];
567
+        }
568
+
569
+        if (!empty($this->params['timeout'])) {
570
+            $settings['timeout'] = $this->params['timeout'];
571
+        }
572
+
573
+        if (isset($settings['apiKey'])) {
574
+            $this->anchor = new Rackspace($this->params['url'], $settings);
575
+        } else {
576
+            $this->anchor = new OpenStack($this->params['url'], $settings);
577
+        }
578
+
579
+        $connection = $this->anchor->objectStoreService($this->params['service_name'], $this->params['region']);
580
+
581
+        if (!empty($this->params['endpoint_url'])) {
582
+            $endpoint = $connection->getEndpoint();
583
+            $endpoint->setPublicUrl($this->params['endpoint_url']);
584
+            $endpoint->setPrivateUrl($this->params['endpoint_url']);
585
+            $connection->setEndpoint($endpoint);
586
+        }
587
+
588
+        $this->connection = $connection;
589
+
590
+        return $this->connection;
591
+    }
592
+
593
+    /**
594
+     * Returns the initialized object store container.
595
+     *
596
+     * @return OpenCloud\ObjectStore\Resource\Container
597
+     */
598
+    public function getContainer() {
599
+        if (!is_null($this->container)) {
600
+            return $this->container;
601
+        }
602
+
603
+        try {
604
+            $this->container = $this->getConnection()->getContainer($this->bucket);
605
+        } catch (ClientErrorResponseException $e) {
606
+            $this->container = $this->getConnection()->createContainer($this->bucket);
607
+        }
608
+
609
+        if (!$this->file_exists('.')) {
610
+            $this->mkdir('.');
611
+        }
612
+
613
+        return $this->container;
614
+    }
615
+
616
+    public function writeBack($tmpFile, $path) {
617
+        $fileData = fopen($tmpFile, 'r');
618
+        $this->getContainer()->uploadObject($path, $fileData);
619
+        // invalidate target object to force repopulation on fetch
620
+        $this->objectCache->remove($path);
621
+        unlink($tmpFile);
622
+    }
623
+
624
+    public function hasUpdated($path, $time) {
625
+        if ($this->is_file($path)) {
626
+            return parent::hasUpdated($path, $time);
627
+        }
628
+        $path = $this->normalizePath($path);
629
+        $dh = $this->opendir($path);
630
+        $content = array();
631
+        while (($file = readdir($dh)) !== false) {
632
+            $content[] = $file;
633
+        }
634
+        if ($path === '.') {
635
+            $path = '';
636
+        }
637
+        $cachedContent = $this->getCache()->getFolderContents($path);
638
+        $cachedNames = array_map(function ($content) {
639
+            return $content['name'];
640
+        }, $cachedContent);
641
+        sort($cachedNames);
642
+        sort($content);
643
+        return $cachedNames !== $content;
644
+    }
645
+
646
+    /**
647
+     * check if curl is installed
648
+     */
649
+    public static function checkDependencies() {
650
+        return true;
651
+    }
652 652
 
653 653
 }
Please login to merge, or discard this patch.
Spacing   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -163,7 +163,7 @@  discard block
 block discarded – undo
163 163
 			throw new \Exception("API Key or password, Username, Bucket and Region have to be configured.");
164 164
 		}
165 165
 
166
-		$this->id = 'swift::' . $params['user'] . md5($params['bucket']);
166
+		$this->id = 'swift::'.$params['user'].md5($params['bucket']);
167 167
 
168 168
 		$bucketUrl = Url::factory($params['bucket']);
169 169
 		if ($bucketUrl->isAbsolute()) {
@@ -236,16 +236,16 @@  discard block
 block discarded – undo
236 236
 				continue;
237 237
 			}
238 238
 
239
-			if ($this->is_dir($path . '/' . $file)) {
240
-				$this->rmdir($path . '/' . $file);
239
+			if ($this->is_dir($path.'/'.$file)) {
240
+				$this->rmdir($path.'/'.$file);
241 241
 			} else {
242
-				$this->unlink($path . '/' . $file);
242
+				$this->unlink($path.'/'.$file);
243 243
 			}
244 244
 		}
245 245
 
246 246
 		try {
247
-			$this->getContainer()->dataObject()->setName($path . '/')->delete();
248
-			$this->objectCache->remove($path . '/');
247
+			$this->getContainer()->dataObject()->setName($path.'/')->delete();
248
+			$this->objectCache->remove($path.'/');
249 249
 		} catch (Exceptions\DeleteError $e) {
250 250
 			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
251 251
 			return false;
@@ -326,7 +326,7 @@  discard block
 block discarded – undo
326 326
 		}
327 327
 
328 328
 		$stat = array();
329
-		$stat['size'] = (int)$object->getContentLength();
329
+		$stat['size'] = (int) $object->getContentLength();
330 330
 		$stat['mtime'] = $mtime;
331 331
 		$stat['atime'] = time();
332 332
 		return $stat;
@@ -358,7 +358,7 @@  discard block
 block discarded – undo
358 358
 		try {
359 359
 			$this->getContainer()->dataObject()->setName($path)->delete();
360 360
 			$this->objectCache->remove($path);
361
-			$this->objectCache->remove($path . '/');
361
+			$this->objectCache->remove($path.'/');
362 362
 		} catch (ClientErrorResponseException $e) {
363 363
 			if ($e->getResponse()->getStatusCode() !== 404) {
364 364
 				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
@@ -387,8 +387,8 @@  discard block
 block discarded – undo
387 387
 					$streamInterface = $streamFactory->fromRequest($client->get($c->getUrl($path)));
388 388
 					$streamInterface->rewind();
389 389
 					$stream = $streamInterface->getStream();
390
-					stream_context_set_option($stream, 'swift','content', $streamInterface);
391
-					if(!strrpos($streamInterface
390
+					stream_context_set_option($stream, 'swift', 'content', $streamInterface);
391
+					if (!strrpos($streamInterface
392 392
 						->getMetaData('wrapper_data')[0], '404 Not Found')) {
393 393
 						return RetryWrapper::wrap($stream);
394 394
 					}
@@ -422,7 +422,7 @@  discard block
 block discarded – undo
422 422
 					file_put_contents($tmpFile, $source);
423 423
 				}
424 424
 				$handle = fopen($tmpFile, $mode);
425
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
425
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
426 426
 					$this->writeBack($tmpFile, $path);
427 427
 				});
428 428
 		}
@@ -469,10 +469,10 @@  discard block
 block discarded – undo
469 469
 
470 470
 			try {
471 471
 				$source = $this->fetchObject($path1);
472
-				$source->copy($this->bucket . '/' . $path2);
472
+				$source->copy($this->bucket.'/'.$path2);
473 473
 				// invalidate target object to force repopulation on fetch
474 474
 				$this->objectCache->remove($path2);
475
-				$this->objectCache->remove($path2 . '/');
475
+				$this->objectCache->remove($path2.'/');
476 476
 			} catch (ClientErrorResponseException $e) {
477 477
 				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
478 478
 				return false;
@@ -484,11 +484,11 @@  discard block
 block discarded – undo
484 484
 			$this->unlink($path2);
485 485
 
486 486
 			try {
487
-				$source = $this->fetchObject($path1 . '/');
488
-				$source->copy($this->bucket . '/' . $path2 . '/');
487
+				$source = $this->fetchObject($path1.'/');
488
+				$source->copy($this->bucket.'/'.$path2.'/');
489 489
 				// invalidate target object to force repopulation on fetch
490 490
 				$this->objectCache->remove($path2);
491
-				$this->objectCache->remove($path2 . '/');
491
+				$this->objectCache->remove($path2.'/');
492 492
 			} catch (ClientErrorResponseException $e) {
493 493
 				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
494 494
 				return false;
@@ -500,8 +500,8 @@  discard block
 block discarded – undo
500 500
 					continue;
501 501
 				}
502 502
 
503
-				$source = $path1 . '/' . $file;
504
-				$target = $path2 . '/' . $file;
503
+				$source = $path1.'/'.$file;
504
+				$target = $path2.'/'.$file;
505 505
 				$this->copy($source, $target);
506 506
 			}
507 507
 
@@ -635,7 +635,7 @@  discard block
 block discarded – undo
635 635
 			$path = '';
636 636
 		}
637 637
 		$cachedContent = $this->getCache()->getFolderContents($path);
638
-		$cachedNames = array_map(function ($content) {
638
+		$cachedNames = array_map(function($content) {
639 639
 			return $content['name'];
640 640
 		}, $cachedContent);
641 641
 		sort($cachedNames);
Please login to merge, or discard this patch.