Completed
Pull Request — master (#3676)
by Individual IT
12:49
created
apps/files_external/lib/Lib/Storage/Google.php 3 patches
Doc Comments   +4 added lines, -1 removed lines patch added patch discarded remove patch
@@ -216,7 +216,7 @@  discard block
 block discarded – undo
216 216
 	 *
217 217
 	 * @param \Google_Service_Drive_DriveFile
218 218
 	 *
219
-	 * @return true if the file is a Google Doc file, false otherwise
219
+	 * @return boolean if the file is a Google Doc file, false otherwise
220 220
 	 */
221 221
 	private function isGoogleDocFile($file) {
222 222
 		return $this->getGoogleDocExtension($file->getMimeType()) !== '';
@@ -505,6 +505,9 @@  discard block
 block discarded – undo
505 505
 		}
506 506
 	}
507 507
 
508
+	/**
509
+	 * @param string $path
510
+	 */
508 511
 	public function writeBack($tmpFile, $path) {
509 512
 		$parentFolder = $this->getDriveFile(dirname($path));
510 513
 		if ($parentFolder) {
Please login to merge, or discard this patch.
Indentation   +675 added lines, -675 removed lines patch added patch discarded remove patch
@@ -41,684 +41,684 @@
 block discarded – undo
41 41
 use Icewind\Streams\RetryWrapper;
42 42
 
43 43
 set_include_path(get_include_path().PATH_SEPARATOR.
44
-	\OC_App::getAppPath('files_external').'/3rdparty/google-api-php-client/src');
44
+    \OC_App::getAppPath('files_external').'/3rdparty/google-api-php-client/src');
45 45
 require_once 'Google/autoload.php';
46 46
 
47 47
 class Google extends \OC\Files\Storage\Common {
48 48
 
49
-	private $client;
50
-	private $id;
51
-	private $service;
52
-	private $driveFiles;
53
-
54
-	// Google Doc mimetypes
55
-	const FOLDER = 'application/vnd.google-apps.folder';
56
-	const DOCUMENT = 'application/vnd.google-apps.document';
57
-	const SPREADSHEET = 'application/vnd.google-apps.spreadsheet';
58
-	const DRAWING = 'application/vnd.google-apps.drawing';
59
-	const PRESENTATION = 'application/vnd.google-apps.presentation';
60
-	const MAP = 'application/vnd.google-apps.map';
61
-
62
-	public function __construct($params) {
63
-		if (isset($params['configured']) && $params['configured'] === 'true'
64
-			&& isset($params['client_id']) && isset($params['client_secret'])
65
-			&& isset($params['token'])
66
-		) {
67
-			$this->client = new \Google_Client();
68
-			$this->client->setClientId($params['client_id']);
69
-			$this->client->setClientSecret($params['client_secret']);
70
-			$this->client->setScopes(array('https://www.googleapis.com/auth/drive'));
71
-			$this->client->setAccessToken($params['token']);
72
-			// if curl isn't available we're likely to run into
73
-			// https://github.com/google/google-api-php-client/issues/59
74
-			// - disable gzip to avoid it.
75
-			if (!function_exists('curl_version') || !function_exists('curl_exec')) {
76
-				$this->client->setClassConfig("Google_Http_Request", "disable_gzip", true);
77
-			}
78
-			// note: API connection is lazy
79
-			$this->service = new \Google_Service_Drive($this->client);
80
-			$token = json_decode($params['token'], true);
81
-			$this->id = 'google::'.substr($params['client_id'], 0, 30).$token['created'];
82
-		} else {
83
-			throw new \Exception('Creating Google storage failed');
84
-		}
85
-	}
86
-
87
-	public function getId() {
88
-		return $this->id;
89
-	}
90
-
91
-	/**
92
-	 * Get the Google_Service_Drive_DriveFile object for the specified path.
93
-	 * Returns false on failure.
94
-	 * @param string $path
95
-	 * @return \Google_Service_Drive_DriveFile|false
96
-	 */
97
-	private function getDriveFile($path) {
98
-		// Remove leading and trailing slashes
99
-		$path = trim($path, '/');
100
-		if ($path === '.') {
101
-			$path = '';
102
-		}
103
-		if (isset($this->driveFiles[$path])) {
104
-			return $this->driveFiles[$path];
105
-		} else if ($path === '') {
106
-			$root = $this->service->files->get('root');
107
-			$this->driveFiles[$path] = $root;
108
-			return $root;
109
-		} else {
110
-			// Google Drive SDK does not have methods for retrieving files by path
111
-			// Instead we must find the id of the parent folder of the file
112
-			$parentId = $this->getDriveFile('')->getId();
113
-			$folderNames = explode('/', $path);
114
-			$path = '';
115
-			// Loop through each folder of this path to get to the file
116
-			foreach ($folderNames as $name) {
117
-				// Reconstruct path from beginning
118
-				if ($path === '') {
119
-					$path .= $name;
120
-				} else {
121
-					$path .= '/'.$name;
122
-				}
123
-				if (isset($this->driveFiles[$path])) {
124
-					$parentId = $this->driveFiles[$path]->getId();
125
-				} else {
126
-					$q = "title='" . str_replace("'","\\'", $name) . "' and '" . str_replace("'","\\'", $parentId) . "' in parents and trashed = false";
127
-					$result = $this->service->files->listFiles(array('q' => $q))->getItems();
128
-					if (!empty($result)) {
129
-						// Google Drive allows files with the same name, ownCloud doesn't
130
-						if (count($result) > 1) {
131
-							$this->onDuplicateFileDetected($path);
132
-							return false;
133
-						} else {
134
-							$file = current($result);
135
-							$this->driveFiles[$path] = $file;
136
-							$parentId = $file->getId();
137
-						}
138
-					} else {
139
-						// Google Docs have no extension in their title, so try without extension
140
-						$pos = strrpos($path, '.');
141
-						if ($pos !== false) {
142
-							$pathWithoutExt = substr($path, 0, $pos);
143
-							$file = $this->getDriveFile($pathWithoutExt);
144
-							if ($file && $this->isGoogleDocFile($file)) {
145
-								// Switch cached Google_Service_Drive_DriveFile to the correct index
146
-								unset($this->driveFiles[$pathWithoutExt]);
147
-								$this->driveFiles[$path] = $file;
148
-								$parentId = $file->getId();
149
-							} else {
150
-								return false;
151
-							}
152
-						} else {
153
-							return false;
154
-						}
155
-					}
156
-				}
157
-			}
158
-			return $this->driveFiles[$path];
159
-		}
160
-	}
161
-
162
-	/**
163
-	 * Set the Google_Service_Drive_DriveFile object in the cache
164
-	 * @param string $path
165
-	 * @param \Google_Service_Drive_DriveFile|false $file
166
-	 */
167
-	private function setDriveFile($path, $file) {
168
-		$path = trim($path, '/');
169
-		$this->driveFiles[$path] = $file;
170
-		if ($file === false) {
171
-			// Remove all children
172
-			$len = strlen($path);
173
-			foreach ($this->driveFiles as $key => $file) {
174
-				if (substr($key, 0, $len) === $path) {
175
-					unset($this->driveFiles[$key]);
176
-				}
177
-			}
178
-		}
179
-	}
180
-
181
-	/**
182
-	 * Write a log message to inform about duplicate file names
183
-	 * @param string $path
184
-	 */
185
-	private function onDuplicateFileDetected($path) {
186
-		$about = $this->service->about->get();
187
-		$user = $about->getName();
188
-		\OCP\Util::writeLog('files_external',
189
-			'Ignoring duplicate file name: '.$path.' on Google Drive for Google user: '.$user,
190
-			\OCP\Util::INFO
191
-		);
192
-	}
193
-
194
-	/**
195
-	 * Generate file extension for a Google Doc, choosing Open Document formats for download
196
-	 * @param string $mimetype
197
-	 * @return string
198
-	 */
199
-	private function getGoogleDocExtension($mimetype) {
200
-		if ($mimetype === self::DOCUMENT) {
201
-			return 'odt';
202
-		} else if ($mimetype === self::SPREADSHEET) {
203
-			return 'ods';
204
-		} else if ($mimetype === self::DRAWING) {
205
-			return 'jpg';
206
-		} else if ($mimetype === self::PRESENTATION) {
207
-			// Download as .odp is not available
208
-			return 'pdf';
209
-		} else {
210
-			return '';
211
-		}
212
-	}
213
-
214
-	/**
215
-	 * Returns whether the given drive file is a Google Doc file
216
-	 *
217
-	 * @param \Google_Service_Drive_DriveFile
218
-	 *
219
-	 * @return true if the file is a Google Doc file, false otherwise
220
-	 */
221
-	private function isGoogleDocFile($file) {
222
-		return $this->getGoogleDocExtension($file->getMimeType()) !== '';
223
-	}
224
-
225
-	public function mkdir($path) {
226
-		if (!$this->is_dir($path)) {
227
-			$parentFolder = $this->getDriveFile(dirname($path));
228
-			if ($parentFolder) {
229
-				$folder = new \Google_Service_Drive_DriveFile();
230
-				$folder->setTitle(basename($path));
231
-				$folder->setMimeType(self::FOLDER);
232
-				$parent = new \Google_Service_Drive_ParentReference();
233
-				$parent->setId($parentFolder->getId());
234
-				$folder->setParents(array($parent));
235
-				$result = $this->service->files->insert($folder);
236
-				if ($result) {
237
-					$this->setDriveFile($path, $result);
238
-				}
239
-				return (bool)$result;
240
-			}
241
-		}
242
-		return false;
243
-	}
244
-
245
-	public function rmdir($path) {
246
-		if (!$this->isDeletable($path)) {
247
-			return false;
248
-		}
249
-		if (trim($path, '/') === '') {
250
-			$dir = $this->opendir($path);
251
-			if(is_resource($dir)) {
252
-				while (($file = readdir($dir)) !== false) {
253
-					if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
254
-						if (!$this->unlink($path.'/'.$file)) {
255
-							return false;
256
-						}
257
-					}
258
-				}
259
-				closedir($dir);
260
-			}
261
-			$this->driveFiles = array();
262
-			return true;
263
-		} else {
264
-			return $this->unlink($path);
265
-		}
266
-	}
267
-
268
-	public function opendir($path) {
269
-		$folder = $this->getDriveFile($path);
270
-		if ($folder) {
271
-			$files = array();
272
-			$duplicates = array();
273
-			$pageToken = true;
274
-			while ($pageToken) {
275
-				$params = array();
276
-				if ($pageToken !== true) {
277
-					$params['pageToken'] = $pageToken;
278
-				}
279
-				$params['q'] = "'" . str_replace("'","\\'", $folder->getId()) . "' in parents and trashed = false";
280
-				$children = $this->service->files->listFiles($params);
281
-				foreach ($children->getItems() as $child) {
282
-					$name = $child->getTitle();
283
-					// Check if this is a Google Doc i.e. no extension in name
284
-					$extension = $child->getFileExtension();
285
-					if (empty($extension)) {
286
-						if ($child->getMimeType() === self::MAP) {
287
-							continue; // No method known to transfer map files, ignore it
288
-						} else if ($child->getMimeType() !== self::FOLDER) {
289
-							$name .= '.'.$this->getGoogleDocExtension($child->getMimeType());
290
-						}
291
-					}
292
-					if ($path === '') {
293
-						$filepath = $name;
294
-					} else {
295
-						$filepath = $path.'/'.$name;
296
-					}
297
-					// Google Drive allows files with the same name, ownCloud doesn't
298
-					// Prevent opendir() from returning any duplicate files
299
-					$key = array_search($name, $files);
300
-					if ($key !== false || isset($duplicates[$filepath])) {
301
-						if (!isset($duplicates[$filepath])) {
302
-							$duplicates[$filepath] = true;
303
-							$this->setDriveFile($filepath, false);
304
-							unset($files[$key]);
305
-							$this->onDuplicateFileDetected($filepath);
306
-						}
307
-					} else {
308
-						// Cache the Google_Service_Drive_DriveFile for future use
309
-						$this->setDriveFile($filepath, $child);
310
-						$files[] = $name;
311
-					}
312
-				}
313
-				$pageToken = $children->getNextPageToken();
314
-			}
315
-			return IteratorDirectory::wrap($files);
316
-		} else {
317
-			return false;
318
-		}
319
-	}
320
-
321
-	public function stat($path) {
322
-		$file = $this->getDriveFile($path);
323
-		if ($file) {
324
-			$stat = array();
325
-			if ($this->filetype($path) === 'dir') {
326
-				$stat['size'] = 0;
327
-			} else {
328
-				// Check if this is a Google Doc
329
-				if ($this->isGoogleDocFile($file)) {
330
-					// Return unknown file size
331
-					$stat['size'] = \OCP\Files\FileInfo::SPACE_UNKNOWN;
332
-				} else {
333
-					$stat['size'] = $file->getFileSize();
334
-				}
335
-			}
336
-			$stat['atime'] = strtotime($file->getLastViewedByMeDate());
337
-			$stat['mtime'] = strtotime($file->getModifiedDate());
338
-			$stat['ctime'] = strtotime($file->getCreatedDate());
339
-			return $stat;
340
-		} else {
341
-			return false;
342
-		}
343
-	}
344
-
345
-	public function filetype($path) {
346
-		if ($path === '') {
347
-			return 'dir';
348
-		} else {
349
-			$file = $this->getDriveFile($path);
350
-			if ($file) {
351
-				if ($file->getMimeType() === self::FOLDER) {
352
-					return 'dir';
353
-				} else {
354
-					return 'file';
355
-				}
356
-			} else {
357
-				return false;
358
-			}
359
-		}
360
-	}
361
-
362
-	public function isUpdatable($path) {
363
-		$file = $this->getDriveFile($path);
364
-		if ($file) {
365
-			return $file->getEditable();
366
-		} else {
367
-			return false;
368
-		}
369
-	}
370
-
371
-	public function file_exists($path) {
372
-		return (bool)$this->getDriveFile($path);
373
-	}
374
-
375
-	public function unlink($path) {
376
-		$file = $this->getDriveFile($path);
377
-		if ($file) {
378
-			$result = $this->service->files->trash($file->getId());
379
-			if ($result) {
380
-				$this->setDriveFile($path, false);
381
-			}
382
-			return (bool)$result;
383
-		} else {
384
-			return false;
385
-		}
386
-	}
387
-
388
-	public function rename($path1, $path2) {
389
-		$file = $this->getDriveFile($path1);
390
-		if ($file) {
391
-			$newFile = $this->getDriveFile($path2);
392
-			if (dirname($path1) === dirname($path2)) {
393
-				if ($newFile) {
394
-					// rename to the name of the target file, could be an office file without extension
395
-					$file->setTitle($newFile->getTitle());
396
-				} else {
397
-					$file->setTitle(basename(($path2)));
398
-				}
399
-			} else {
400
-				// Change file parent
401
-				$parentFolder2 = $this->getDriveFile(dirname($path2));
402
-				if ($parentFolder2) {
403
-					$parent = new \Google_Service_Drive_ParentReference();
404
-					$parent->setId($parentFolder2->getId());
405
-					$file->setParents(array($parent));
406
-				} else {
407
-					return false;
408
-				}
409
-			}
410
-			// We need to get the object for the existing file with the same
411
-			// name (if there is one) before we do the patch. If oldfile
412
-			// exists and is a directory we have to delete it before we
413
-			// do the rename too.
414
-			$oldfile = $this->getDriveFile($path2);
415
-			if ($oldfile && $this->is_dir($path2)) {
416
-				$this->rmdir($path2);
417
-				$oldfile = false;
418
-			}
419
-			$result = $this->service->files->patch($file->getId(), $file);
420
-			if ($result) {
421
-				$this->setDriveFile($path1, false);
422
-				$this->setDriveFile($path2, $result);
423
-				if ($oldfile && $newFile) {
424
-					// only delete if they have a different id (same id can happen for part files)
425
-					if ($newFile->getId() !== $oldfile->getId()) {
426
-						$this->service->files->delete($oldfile->getId());
427
-					}
428
-				}
429
-			}
430
-			return (bool)$result;
431
-		} else {
432
-			return false;
433
-		}
434
-	}
435
-
436
-	public function fopen($path, $mode) {
437
-		$pos = strrpos($path, '.');
438
-		if ($pos !== false) {
439
-			$ext = substr($path, $pos);
440
-		} else {
441
-			$ext = '';
442
-		}
443
-		switch ($mode) {
444
-			case 'r':
445
-			case 'rb':
446
-				$file = $this->getDriveFile($path);
447
-				if ($file) {
448
-					$exportLinks = $file->getExportLinks();
449
-					$mimetype = $this->getMimeType($path);
450
-					$downloadUrl = null;
451
-					if ($exportLinks && isset($exportLinks[$mimetype])) {
452
-						$downloadUrl = $exportLinks[$mimetype];
453
-					} else {
454
-						$downloadUrl = $file->getDownloadUrl();
455
-					}
456
-					if (isset($downloadUrl)) {
457
-						$request = new \Google_Http_Request($downloadUrl, 'GET', null, null);
458
-						$httpRequest = $this->client->getAuth()->sign($request);
459
-						// the library's service doesn't support streaming, so we use Guzzle instead
460
-						$client = \OC::$server->getHTTPClientService()->newClient();
461
-						try {
462
-							$response = $client->get($downloadUrl, [
463
-								'headers' => $httpRequest->getRequestHeaders(),
464
-								'stream' => true,
465
-								'verify' => realpath(__DIR__ . '/../../../3rdparty/google-api-php-client/src/Google/IO/cacerts.pem'),
466
-							]);
467
-						} catch (RequestException $e) {
468
-							if(!is_null($e->getResponse())) {
469
-								if ($e->getResponse()->getStatusCode() === 404) {
470
-									return false;
471
-								} else {
472
-									throw $e;
473
-								}
474
-							} else {
475
-								throw $e;
476
-							}
477
-						}
478
-
479
-						$handle = $response->getBody();
480
-						return RetryWrapper::wrap($handle);
481
-					}
482
-				}
483
-				return false;
484
-			case 'w':
485
-			case 'wb':
486
-			case 'a':
487
-			case 'ab':
488
-			case 'r+':
489
-			case 'w+':
490
-			case 'wb+':
491
-			case 'a+':
492
-			case 'x':
493
-			case 'x+':
494
-			case 'c':
495
-			case 'c+':
496
-				$tmpFile = \OCP\Files::tmpFile($ext);
497
-				if ($this->file_exists($path)) {
498
-					$source = $this->fopen($path, 'rb');
499
-					file_put_contents($tmpFile, $source);
500
-				}
501
-				$handle = fopen($tmpFile, $mode);
502
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
503
-					$this->writeBack($tmpFile, $path);
504
-				});
505
-		}
506
-	}
507
-
508
-	public function writeBack($tmpFile, $path) {
509
-		$parentFolder = $this->getDriveFile(dirname($path));
510
-		if ($parentFolder) {
511
-			$mimetype = \OC::$server->getMimeTypeDetector()->detect($tmpFile);
512
-			$params = array(
513
-				'mimeType' => $mimetype,
514
-				'uploadType' => 'media'
515
-			);
516
-			$result = false;
517
-
518
-			$chunkSizeBytes = 10 * 1024 * 1024;
519
-
520
-			$useChunking = false;
521
-			$size = filesize($tmpFile);
522
-			if ($size > $chunkSizeBytes) {
523
-				$useChunking = true;
524
-			} else {
525
-				$params['data'] = file_get_contents($tmpFile);
526
-			}
527
-
528
-			if ($this->file_exists($path)) {
529
-				$file = $this->getDriveFile($path);
530
-				$this->client->setDefer($useChunking);
531
-				$request = $this->service->files->update($file->getId(), $file, $params);
532
-			} else {
533
-				$file = new \Google_Service_Drive_DriveFile();
534
-				$file->setTitle(basename($path));
535
-				$file->setMimeType($mimetype);
536
-				$parent = new \Google_Service_Drive_ParentReference();
537
-				$parent->setId($parentFolder->getId());
538
-				$file->setParents(array($parent));
539
-				$this->client->setDefer($useChunking);
540
-				$request = $this->service->files->insert($file, $params);
541
-			}
542
-
543
-			if ($useChunking) {
544
-				// Create a media file upload to represent our upload process.
545
-				$media = new \Google_Http_MediaFileUpload(
546
-					$this->client,
547
-					$request,
548
-					'text/plain',
549
-					null,
550
-					true,
551
-					$chunkSizeBytes
552
-				);
553
-				$media->setFileSize($size);
554
-
555
-				// Upload the various chunks. $status will be false until the process is
556
-				// complete.
557
-				$status = false;
558
-				$handle = fopen($tmpFile, 'rb');
559
-				while (!$status && !feof($handle)) {
560
-					$chunk = fread($handle, $chunkSizeBytes);
561
-					$status = $media->nextChunk($chunk);
562
-				}
563
-
564
-				// The final value of $status will be the data from the API for the object
565
-				// that has been uploaded.
566
-				$result = false;
567
-				if ($status !== false) {
568
-					$result = $status;
569
-				}
570
-
571
-				fclose($handle);
572
-			} else {
573
-				$result = $request;
574
-			}
575
-
576
-			// Reset to the client to execute requests immediately in the future.
577
-			$this->client->setDefer(false);
578
-
579
-			if ($result) {
580
-				$this->setDriveFile($path, $result);
581
-			}
582
-		}
583
-	}
584
-
585
-	public function getMimeType($path) {
586
-		$file = $this->getDriveFile($path);
587
-		if ($file) {
588
-			$mimetype = $file->getMimeType();
589
-			// Convert Google Doc mimetypes, choosing Open Document formats for download
590
-			if ($mimetype === self::FOLDER) {
591
-				return 'httpd/unix-directory';
592
-			} else if ($mimetype === self::DOCUMENT) {
593
-				return 'application/vnd.oasis.opendocument.text';
594
-			} else if ($mimetype === self::SPREADSHEET) {
595
-				return 'application/x-vnd.oasis.opendocument.spreadsheet';
596
-			} else if ($mimetype === self::DRAWING) {
597
-				return 'image/jpeg';
598
-			} else if ($mimetype === self::PRESENTATION) {
599
-				// Download as .odp is not available
600
-				return 'application/pdf';
601
-			} else {
602
-				// use extension-based detection, could be an encrypted file
603
-				return parent::getMimeType($path);
604
-			}
605
-		} else {
606
-			return false;
607
-		}
608
-	}
609
-
610
-	public function free_space($path) {
611
-		$about = $this->service->about->get();
612
-		return $about->getQuotaBytesTotal() - $about->getQuotaBytesUsed();
613
-	}
614
-
615
-	public function touch($path, $mtime = null) {
616
-		$file = $this->getDriveFile($path);
617
-		$result = false;
618
-		if ($file) {
619
-			if (isset($mtime)) {
620
-				// This is just RFC3339, but frustratingly, GDrive's API *requires*
621
-				// the fractions portion be present, while no handy PHP constant
622
-				// for RFC3339 or ISO8601 includes it. So we do it ourselves.
623
-				$file->setModifiedDate(date('Y-m-d\TH:i:s.uP', $mtime));
624
-				$result = $this->service->files->patch($file->getId(), $file, array(
625
-					'setModifiedDate' => true,
626
-				));
627
-			} else {
628
-				$result = $this->service->files->touch($file->getId());
629
-			}
630
-		} else {
631
-			$parentFolder = $this->getDriveFile(dirname($path));
632
-			if ($parentFolder) {
633
-				$file = new \Google_Service_Drive_DriveFile();
634
-				$file->setTitle(basename($path));
635
-				$parent = new \Google_Service_Drive_ParentReference();
636
-				$parent->setId($parentFolder->getId());
637
-				$file->setParents(array($parent));
638
-				$result = $this->service->files->insert($file);
639
-			}
640
-		}
641
-		if ($result) {
642
-			$this->setDriveFile($path, $result);
643
-		}
644
-		return (bool)$result;
645
-	}
646
-
647
-	public function test() {
648
-		if ($this->free_space('')) {
649
-			return true;
650
-		}
651
-		return false;
652
-	}
653
-
654
-	public function hasUpdated($path, $time) {
655
-		$appConfig = \OC::$server->getAppConfig();
656
-		if ($this->is_file($path)) {
657
-			return parent::hasUpdated($path, $time);
658
-		} else {
659
-			// Google Drive doesn't change modified times of folders when files inside are updated
660
-			// Instead we use the Changes API to see if folders have been updated, and it's a pain
661
-			$folder = $this->getDriveFile($path);
662
-			if ($folder) {
663
-				$result = false;
664
-				$folderId = $folder->getId();
665
-				$startChangeId = $appConfig->getValue('files_external', $this->getId().'cId');
666
-				$params = array(
667
-					'includeDeleted' => true,
668
-					'includeSubscribed' => true,
669
-				);
670
-				if (isset($startChangeId)) {
671
-					$startChangeId = (int)$startChangeId;
672
-					$largestChangeId = $startChangeId;
673
-					$params['startChangeId'] = $startChangeId + 1;
674
-				} else {
675
-					$largestChangeId = 0;
676
-				}
677
-				$pageToken = true;
678
-				while ($pageToken) {
679
-					if ($pageToken !== true) {
680
-						$params['pageToken'] = $pageToken;
681
-					}
682
-					$changes = $this->service->changes->listChanges($params);
683
-					if ($largestChangeId === 0 || $largestChangeId === $startChangeId) {
684
-						$largestChangeId = $changes->getLargestChangeId();
685
-					}
686
-					if (isset($startChangeId)) {
687
-						// Check if a file in this folder has been updated
688
-						// There is no way to filter by folder at the API level...
689
-						foreach ($changes->getItems() as $change) {
690
-							$file = $change->getFile();
691
-							if ($file) {
692
-								foreach ($file->getParents() as $parent) {
693
-									if ($parent->getId() === $folderId) {
694
-										$result = true;
695
-									// Check if there are changes in different folders
696
-									} else if ($change->getId() <= $largestChangeId) {
697
-										// Decrement id so this change is fetched when called again
698
-										$largestChangeId = $change->getId();
699
-										$largestChangeId--;
700
-									}
701
-								}
702
-							}
703
-						}
704
-						$pageToken = $changes->getNextPageToken();
705
-					} else {
706
-						// Assuming the initial scan just occurred and changes are negligible
707
-						break;
708
-					}
709
-				}
710
-				$appConfig->setValue('files_external', $this->getId().'cId', $largestChangeId);
711
-				return $result;
712
-			}
713
-		}
714
-		return false;
715
-	}
716
-
717
-	/**
718
-	 * check if curl is installed
719
-	 */
720
-	public static function checkDependencies() {
721
-		return true;
722
-	}
49
+    private $client;
50
+    private $id;
51
+    private $service;
52
+    private $driveFiles;
53
+
54
+    // Google Doc mimetypes
55
+    const FOLDER = 'application/vnd.google-apps.folder';
56
+    const DOCUMENT = 'application/vnd.google-apps.document';
57
+    const SPREADSHEET = 'application/vnd.google-apps.spreadsheet';
58
+    const DRAWING = 'application/vnd.google-apps.drawing';
59
+    const PRESENTATION = 'application/vnd.google-apps.presentation';
60
+    const MAP = 'application/vnd.google-apps.map';
61
+
62
+    public function __construct($params) {
63
+        if (isset($params['configured']) && $params['configured'] === 'true'
64
+            && isset($params['client_id']) && isset($params['client_secret'])
65
+            && isset($params['token'])
66
+        ) {
67
+            $this->client = new \Google_Client();
68
+            $this->client->setClientId($params['client_id']);
69
+            $this->client->setClientSecret($params['client_secret']);
70
+            $this->client->setScopes(array('https://www.googleapis.com/auth/drive'));
71
+            $this->client->setAccessToken($params['token']);
72
+            // if curl isn't available we're likely to run into
73
+            // https://github.com/google/google-api-php-client/issues/59
74
+            // - disable gzip to avoid it.
75
+            if (!function_exists('curl_version') || !function_exists('curl_exec')) {
76
+                $this->client->setClassConfig("Google_Http_Request", "disable_gzip", true);
77
+            }
78
+            // note: API connection is lazy
79
+            $this->service = new \Google_Service_Drive($this->client);
80
+            $token = json_decode($params['token'], true);
81
+            $this->id = 'google::'.substr($params['client_id'], 0, 30).$token['created'];
82
+        } else {
83
+            throw new \Exception('Creating Google storage failed');
84
+        }
85
+    }
86
+
87
+    public function getId() {
88
+        return $this->id;
89
+    }
90
+
91
+    /**
92
+     * Get the Google_Service_Drive_DriveFile object for the specified path.
93
+     * Returns false on failure.
94
+     * @param string $path
95
+     * @return \Google_Service_Drive_DriveFile|false
96
+     */
97
+    private function getDriveFile($path) {
98
+        // Remove leading and trailing slashes
99
+        $path = trim($path, '/');
100
+        if ($path === '.') {
101
+            $path = '';
102
+        }
103
+        if (isset($this->driveFiles[$path])) {
104
+            return $this->driveFiles[$path];
105
+        } else if ($path === '') {
106
+            $root = $this->service->files->get('root');
107
+            $this->driveFiles[$path] = $root;
108
+            return $root;
109
+        } else {
110
+            // Google Drive SDK does not have methods for retrieving files by path
111
+            // Instead we must find the id of the parent folder of the file
112
+            $parentId = $this->getDriveFile('')->getId();
113
+            $folderNames = explode('/', $path);
114
+            $path = '';
115
+            // Loop through each folder of this path to get to the file
116
+            foreach ($folderNames as $name) {
117
+                // Reconstruct path from beginning
118
+                if ($path === '') {
119
+                    $path .= $name;
120
+                } else {
121
+                    $path .= '/'.$name;
122
+                }
123
+                if (isset($this->driveFiles[$path])) {
124
+                    $parentId = $this->driveFiles[$path]->getId();
125
+                } else {
126
+                    $q = "title='" . str_replace("'","\\'", $name) . "' and '" . str_replace("'","\\'", $parentId) . "' in parents and trashed = false";
127
+                    $result = $this->service->files->listFiles(array('q' => $q))->getItems();
128
+                    if (!empty($result)) {
129
+                        // Google Drive allows files with the same name, ownCloud doesn't
130
+                        if (count($result) > 1) {
131
+                            $this->onDuplicateFileDetected($path);
132
+                            return false;
133
+                        } else {
134
+                            $file = current($result);
135
+                            $this->driveFiles[$path] = $file;
136
+                            $parentId = $file->getId();
137
+                        }
138
+                    } else {
139
+                        // Google Docs have no extension in their title, so try without extension
140
+                        $pos = strrpos($path, '.');
141
+                        if ($pos !== false) {
142
+                            $pathWithoutExt = substr($path, 0, $pos);
143
+                            $file = $this->getDriveFile($pathWithoutExt);
144
+                            if ($file && $this->isGoogleDocFile($file)) {
145
+                                // Switch cached Google_Service_Drive_DriveFile to the correct index
146
+                                unset($this->driveFiles[$pathWithoutExt]);
147
+                                $this->driveFiles[$path] = $file;
148
+                                $parentId = $file->getId();
149
+                            } else {
150
+                                return false;
151
+                            }
152
+                        } else {
153
+                            return false;
154
+                        }
155
+                    }
156
+                }
157
+            }
158
+            return $this->driveFiles[$path];
159
+        }
160
+    }
161
+
162
+    /**
163
+     * Set the Google_Service_Drive_DriveFile object in the cache
164
+     * @param string $path
165
+     * @param \Google_Service_Drive_DriveFile|false $file
166
+     */
167
+    private function setDriveFile($path, $file) {
168
+        $path = trim($path, '/');
169
+        $this->driveFiles[$path] = $file;
170
+        if ($file === false) {
171
+            // Remove all children
172
+            $len = strlen($path);
173
+            foreach ($this->driveFiles as $key => $file) {
174
+                if (substr($key, 0, $len) === $path) {
175
+                    unset($this->driveFiles[$key]);
176
+                }
177
+            }
178
+        }
179
+    }
180
+
181
+    /**
182
+     * Write a log message to inform about duplicate file names
183
+     * @param string $path
184
+     */
185
+    private function onDuplicateFileDetected($path) {
186
+        $about = $this->service->about->get();
187
+        $user = $about->getName();
188
+        \OCP\Util::writeLog('files_external',
189
+            'Ignoring duplicate file name: '.$path.' on Google Drive for Google user: '.$user,
190
+            \OCP\Util::INFO
191
+        );
192
+    }
193
+
194
+    /**
195
+     * Generate file extension for a Google Doc, choosing Open Document formats for download
196
+     * @param string $mimetype
197
+     * @return string
198
+     */
199
+    private function getGoogleDocExtension($mimetype) {
200
+        if ($mimetype === self::DOCUMENT) {
201
+            return 'odt';
202
+        } else if ($mimetype === self::SPREADSHEET) {
203
+            return 'ods';
204
+        } else if ($mimetype === self::DRAWING) {
205
+            return 'jpg';
206
+        } else if ($mimetype === self::PRESENTATION) {
207
+            // Download as .odp is not available
208
+            return 'pdf';
209
+        } else {
210
+            return '';
211
+        }
212
+    }
213
+
214
+    /**
215
+     * Returns whether the given drive file is a Google Doc file
216
+     *
217
+     * @param \Google_Service_Drive_DriveFile
218
+     *
219
+     * @return true if the file is a Google Doc file, false otherwise
220
+     */
221
+    private function isGoogleDocFile($file) {
222
+        return $this->getGoogleDocExtension($file->getMimeType()) !== '';
223
+    }
224
+
225
+    public function mkdir($path) {
226
+        if (!$this->is_dir($path)) {
227
+            $parentFolder = $this->getDriveFile(dirname($path));
228
+            if ($parentFolder) {
229
+                $folder = new \Google_Service_Drive_DriveFile();
230
+                $folder->setTitle(basename($path));
231
+                $folder->setMimeType(self::FOLDER);
232
+                $parent = new \Google_Service_Drive_ParentReference();
233
+                $parent->setId($parentFolder->getId());
234
+                $folder->setParents(array($parent));
235
+                $result = $this->service->files->insert($folder);
236
+                if ($result) {
237
+                    $this->setDriveFile($path, $result);
238
+                }
239
+                return (bool)$result;
240
+            }
241
+        }
242
+        return false;
243
+    }
244
+
245
+    public function rmdir($path) {
246
+        if (!$this->isDeletable($path)) {
247
+            return false;
248
+        }
249
+        if (trim($path, '/') === '') {
250
+            $dir = $this->opendir($path);
251
+            if(is_resource($dir)) {
252
+                while (($file = readdir($dir)) !== false) {
253
+                    if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
254
+                        if (!$this->unlink($path.'/'.$file)) {
255
+                            return false;
256
+                        }
257
+                    }
258
+                }
259
+                closedir($dir);
260
+            }
261
+            $this->driveFiles = array();
262
+            return true;
263
+        } else {
264
+            return $this->unlink($path);
265
+        }
266
+    }
267
+
268
+    public function opendir($path) {
269
+        $folder = $this->getDriveFile($path);
270
+        if ($folder) {
271
+            $files = array();
272
+            $duplicates = array();
273
+            $pageToken = true;
274
+            while ($pageToken) {
275
+                $params = array();
276
+                if ($pageToken !== true) {
277
+                    $params['pageToken'] = $pageToken;
278
+                }
279
+                $params['q'] = "'" . str_replace("'","\\'", $folder->getId()) . "' in parents and trashed = false";
280
+                $children = $this->service->files->listFiles($params);
281
+                foreach ($children->getItems() as $child) {
282
+                    $name = $child->getTitle();
283
+                    // Check if this is a Google Doc i.e. no extension in name
284
+                    $extension = $child->getFileExtension();
285
+                    if (empty($extension)) {
286
+                        if ($child->getMimeType() === self::MAP) {
287
+                            continue; // No method known to transfer map files, ignore it
288
+                        } else if ($child->getMimeType() !== self::FOLDER) {
289
+                            $name .= '.'.$this->getGoogleDocExtension($child->getMimeType());
290
+                        }
291
+                    }
292
+                    if ($path === '') {
293
+                        $filepath = $name;
294
+                    } else {
295
+                        $filepath = $path.'/'.$name;
296
+                    }
297
+                    // Google Drive allows files with the same name, ownCloud doesn't
298
+                    // Prevent opendir() from returning any duplicate files
299
+                    $key = array_search($name, $files);
300
+                    if ($key !== false || isset($duplicates[$filepath])) {
301
+                        if (!isset($duplicates[$filepath])) {
302
+                            $duplicates[$filepath] = true;
303
+                            $this->setDriveFile($filepath, false);
304
+                            unset($files[$key]);
305
+                            $this->onDuplicateFileDetected($filepath);
306
+                        }
307
+                    } else {
308
+                        // Cache the Google_Service_Drive_DriveFile for future use
309
+                        $this->setDriveFile($filepath, $child);
310
+                        $files[] = $name;
311
+                    }
312
+                }
313
+                $pageToken = $children->getNextPageToken();
314
+            }
315
+            return IteratorDirectory::wrap($files);
316
+        } else {
317
+            return false;
318
+        }
319
+    }
320
+
321
+    public function stat($path) {
322
+        $file = $this->getDriveFile($path);
323
+        if ($file) {
324
+            $stat = array();
325
+            if ($this->filetype($path) === 'dir') {
326
+                $stat['size'] = 0;
327
+            } else {
328
+                // Check if this is a Google Doc
329
+                if ($this->isGoogleDocFile($file)) {
330
+                    // Return unknown file size
331
+                    $stat['size'] = \OCP\Files\FileInfo::SPACE_UNKNOWN;
332
+                } else {
333
+                    $stat['size'] = $file->getFileSize();
334
+                }
335
+            }
336
+            $stat['atime'] = strtotime($file->getLastViewedByMeDate());
337
+            $stat['mtime'] = strtotime($file->getModifiedDate());
338
+            $stat['ctime'] = strtotime($file->getCreatedDate());
339
+            return $stat;
340
+        } else {
341
+            return false;
342
+        }
343
+    }
344
+
345
+    public function filetype($path) {
346
+        if ($path === '') {
347
+            return 'dir';
348
+        } else {
349
+            $file = $this->getDriveFile($path);
350
+            if ($file) {
351
+                if ($file->getMimeType() === self::FOLDER) {
352
+                    return 'dir';
353
+                } else {
354
+                    return 'file';
355
+                }
356
+            } else {
357
+                return false;
358
+            }
359
+        }
360
+    }
361
+
362
+    public function isUpdatable($path) {
363
+        $file = $this->getDriveFile($path);
364
+        if ($file) {
365
+            return $file->getEditable();
366
+        } else {
367
+            return false;
368
+        }
369
+    }
370
+
371
+    public function file_exists($path) {
372
+        return (bool)$this->getDriveFile($path);
373
+    }
374
+
375
+    public function unlink($path) {
376
+        $file = $this->getDriveFile($path);
377
+        if ($file) {
378
+            $result = $this->service->files->trash($file->getId());
379
+            if ($result) {
380
+                $this->setDriveFile($path, false);
381
+            }
382
+            return (bool)$result;
383
+        } else {
384
+            return false;
385
+        }
386
+    }
387
+
388
+    public function rename($path1, $path2) {
389
+        $file = $this->getDriveFile($path1);
390
+        if ($file) {
391
+            $newFile = $this->getDriveFile($path2);
392
+            if (dirname($path1) === dirname($path2)) {
393
+                if ($newFile) {
394
+                    // rename to the name of the target file, could be an office file without extension
395
+                    $file->setTitle($newFile->getTitle());
396
+                } else {
397
+                    $file->setTitle(basename(($path2)));
398
+                }
399
+            } else {
400
+                // Change file parent
401
+                $parentFolder2 = $this->getDriveFile(dirname($path2));
402
+                if ($parentFolder2) {
403
+                    $parent = new \Google_Service_Drive_ParentReference();
404
+                    $parent->setId($parentFolder2->getId());
405
+                    $file->setParents(array($parent));
406
+                } else {
407
+                    return false;
408
+                }
409
+            }
410
+            // We need to get the object for the existing file with the same
411
+            // name (if there is one) before we do the patch. If oldfile
412
+            // exists and is a directory we have to delete it before we
413
+            // do the rename too.
414
+            $oldfile = $this->getDriveFile($path2);
415
+            if ($oldfile && $this->is_dir($path2)) {
416
+                $this->rmdir($path2);
417
+                $oldfile = false;
418
+            }
419
+            $result = $this->service->files->patch($file->getId(), $file);
420
+            if ($result) {
421
+                $this->setDriveFile($path1, false);
422
+                $this->setDriveFile($path2, $result);
423
+                if ($oldfile && $newFile) {
424
+                    // only delete if they have a different id (same id can happen for part files)
425
+                    if ($newFile->getId() !== $oldfile->getId()) {
426
+                        $this->service->files->delete($oldfile->getId());
427
+                    }
428
+                }
429
+            }
430
+            return (bool)$result;
431
+        } else {
432
+            return false;
433
+        }
434
+    }
435
+
436
+    public function fopen($path, $mode) {
437
+        $pos = strrpos($path, '.');
438
+        if ($pos !== false) {
439
+            $ext = substr($path, $pos);
440
+        } else {
441
+            $ext = '';
442
+        }
443
+        switch ($mode) {
444
+            case 'r':
445
+            case 'rb':
446
+                $file = $this->getDriveFile($path);
447
+                if ($file) {
448
+                    $exportLinks = $file->getExportLinks();
449
+                    $mimetype = $this->getMimeType($path);
450
+                    $downloadUrl = null;
451
+                    if ($exportLinks && isset($exportLinks[$mimetype])) {
452
+                        $downloadUrl = $exportLinks[$mimetype];
453
+                    } else {
454
+                        $downloadUrl = $file->getDownloadUrl();
455
+                    }
456
+                    if (isset($downloadUrl)) {
457
+                        $request = new \Google_Http_Request($downloadUrl, 'GET', null, null);
458
+                        $httpRequest = $this->client->getAuth()->sign($request);
459
+                        // the library's service doesn't support streaming, so we use Guzzle instead
460
+                        $client = \OC::$server->getHTTPClientService()->newClient();
461
+                        try {
462
+                            $response = $client->get($downloadUrl, [
463
+                                'headers' => $httpRequest->getRequestHeaders(),
464
+                                'stream' => true,
465
+                                'verify' => realpath(__DIR__ . '/../../../3rdparty/google-api-php-client/src/Google/IO/cacerts.pem'),
466
+                            ]);
467
+                        } catch (RequestException $e) {
468
+                            if(!is_null($e->getResponse())) {
469
+                                if ($e->getResponse()->getStatusCode() === 404) {
470
+                                    return false;
471
+                                } else {
472
+                                    throw $e;
473
+                                }
474
+                            } else {
475
+                                throw $e;
476
+                            }
477
+                        }
478
+
479
+                        $handle = $response->getBody();
480
+                        return RetryWrapper::wrap($handle);
481
+                    }
482
+                }
483
+                return false;
484
+            case 'w':
485
+            case 'wb':
486
+            case 'a':
487
+            case 'ab':
488
+            case 'r+':
489
+            case 'w+':
490
+            case 'wb+':
491
+            case 'a+':
492
+            case 'x':
493
+            case 'x+':
494
+            case 'c':
495
+            case 'c+':
496
+                $tmpFile = \OCP\Files::tmpFile($ext);
497
+                if ($this->file_exists($path)) {
498
+                    $source = $this->fopen($path, 'rb');
499
+                    file_put_contents($tmpFile, $source);
500
+                }
501
+                $handle = fopen($tmpFile, $mode);
502
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
503
+                    $this->writeBack($tmpFile, $path);
504
+                });
505
+        }
506
+    }
507
+
508
+    public function writeBack($tmpFile, $path) {
509
+        $parentFolder = $this->getDriveFile(dirname($path));
510
+        if ($parentFolder) {
511
+            $mimetype = \OC::$server->getMimeTypeDetector()->detect($tmpFile);
512
+            $params = array(
513
+                'mimeType' => $mimetype,
514
+                'uploadType' => 'media'
515
+            );
516
+            $result = false;
517
+
518
+            $chunkSizeBytes = 10 * 1024 * 1024;
519
+
520
+            $useChunking = false;
521
+            $size = filesize($tmpFile);
522
+            if ($size > $chunkSizeBytes) {
523
+                $useChunking = true;
524
+            } else {
525
+                $params['data'] = file_get_contents($tmpFile);
526
+            }
527
+
528
+            if ($this->file_exists($path)) {
529
+                $file = $this->getDriveFile($path);
530
+                $this->client->setDefer($useChunking);
531
+                $request = $this->service->files->update($file->getId(), $file, $params);
532
+            } else {
533
+                $file = new \Google_Service_Drive_DriveFile();
534
+                $file->setTitle(basename($path));
535
+                $file->setMimeType($mimetype);
536
+                $parent = new \Google_Service_Drive_ParentReference();
537
+                $parent->setId($parentFolder->getId());
538
+                $file->setParents(array($parent));
539
+                $this->client->setDefer($useChunking);
540
+                $request = $this->service->files->insert($file, $params);
541
+            }
542
+
543
+            if ($useChunking) {
544
+                // Create a media file upload to represent our upload process.
545
+                $media = new \Google_Http_MediaFileUpload(
546
+                    $this->client,
547
+                    $request,
548
+                    'text/plain',
549
+                    null,
550
+                    true,
551
+                    $chunkSizeBytes
552
+                );
553
+                $media->setFileSize($size);
554
+
555
+                // Upload the various chunks. $status will be false until the process is
556
+                // complete.
557
+                $status = false;
558
+                $handle = fopen($tmpFile, 'rb');
559
+                while (!$status && !feof($handle)) {
560
+                    $chunk = fread($handle, $chunkSizeBytes);
561
+                    $status = $media->nextChunk($chunk);
562
+                }
563
+
564
+                // The final value of $status will be the data from the API for the object
565
+                // that has been uploaded.
566
+                $result = false;
567
+                if ($status !== false) {
568
+                    $result = $status;
569
+                }
570
+
571
+                fclose($handle);
572
+            } else {
573
+                $result = $request;
574
+            }
575
+
576
+            // Reset to the client to execute requests immediately in the future.
577
+            $this->client->setDefer(false);
578
+
579
+            if ($result) {
580
+                $this->setDriveFile($path, $result);
581
+            }
582
+        }
583
+    }
584
+
585
+    public function getMimeType($path) {
586
+        $file = $this->getDriveFile($path);
587
+        if ($file) {
588
+            $mimetype = $file->getMimeType();
589
+            // Convert Google Doc mimetypes, choosing Open Document formats for download
590
+            if ($mimetype === self::FOLDER) {
591
+                return 'httpd/unix-directory';
592
+            } else if ($mimetype === self::DOCUMENT) {
593
+                return 'application/vnd.oasis.opendocument.text';
594
+            } else if ($mimetype === self::SPREADSHEET) {
595
+                return 'application/x-vnd.oasis.opendocument.spreadsheet';
596
+            } else if ($mimetype === self::DRAWING) {
597
+                return 'image/jpeg';
598
+            } else if ($mimetype === self::PRESENTATION) {
599
+                // Download as .odp is not available
600
+                return 'application/pdf';
601
+            } else {
602
+                // use extension-based detection, could be an encrypted file
603
+                return parent::getMimeType($path);
604
+            }
605
+        } else {
606
+            return false;
607
+        }
608
+    }
609
+
610
+    public function free_space($path) {
611
+        $about = $this->service->about->get();
612
+        return $about->getQuotaBytesTotal() - $about->getQuotaBytesUsed();
613
+    }
614
+
615
+    public function touch($path, $mtime = null) {
616
+        $file = $this->getDriveFile($path);
617
+        $result = false;
618
+        if ($file) {
619
+            if (isset($mtime)) {
620
+                // This is just RFC3339, but frustratingly, GDrive's API *requires*
621
+                // the fractions portion be present, while no handy PHP constant
622
+                // for RFC3339 or ISO8601 includes it. So we do it ourselves.
623
+                $file->setModifiedDate(date('Y-m-d\TH:i:s.uP', $mtime));
624
+                $result = $this->service->files->patch($file->getId(), $file, array(
625
+                    'setModifiedDate' => true,
626
+                ));
627
+            } else {
628
+                $result = $this->service->files->touch($file->getId());
629
+            }
630
+        } else {
631
+            $parentFolder = $this->getDriveFile(dirname($path));
632
+            if ($parentFolder) {
633
+                $file = new \Google_Service_Drive_DriveFile();
634
+                $file->setTitle(basename($path));
635
+                $parent = new \Google_Service_Drive_ParentReference();
636
+                $parent->setId($parentFolder->getId());
637
+                $file->setParents(array($parent));
638
+                $result = $this->service->files->insert($file);
639
+            }
640
+        }
641
+        if ($result) {
642
+            $this->setDriveFile($path, $result);
643
+        }
644
+        return (bool)$result;
645
+    }
646
+
647
+    public function test() {
648
+        if ($this->free_space('')) {
649
+            return true;
650
+        }
651
+        return false;
652
+    }
653
+
654
+    public function hasUpdated($path, $time) {
655
+        $appConfig = \OC::$server->getAppConfig();
656
+        if ($this->is_file($path)) {
657
+            return parent::hasUpdated($path, $time);
658
+        } else {
659
+            // Google Drive doesn't change modified times of folders when files inside are updated
660
+            // Instead we use the Changes API to see if folders have been updated, and it's a pain
661
+            $folder = $this->getDriveFile($path);
662
+            if ($folder) {
663
+                $result = false;
664
+                $folderId = $folder->getId();
665
+                $startChangeId = $appConfig->getValue('files_external', $this->getId().'cId');
666
+                $params = array(
667
+                    'includeDeleted' => true,
668
+                    'includeSubscribed' => true,
669
+                );
670
+                if (isset($startChangeId)) {
671
+                    $startChangeId = (int)$startChangeId;
672
+                    $largestChangeId = $startChangeId;
673
+                    $params['startChangeId'] = $startChangeId + 1;
674
+                } else {
675
+                    $largestChangeId = 0;
676
+                }
677
+                $pageToken = true;
678
+                while ($pageToken) {
679
+                    if ($pageToken !== true) {
680
+                        $params['pageToken'] = $pageToken;
681
+                    }
682
+                    $changes = $this->service->changes->listChanges($params);
683
+                    if ($largestChangeId === 0 || $largestChangeId === $startChangeId) {
684
+                        $largestChangeId = $changes->getLargestChangeId();
685
+                    }
686
+                    if (isset($startChangeId)) {
687
+                        // Check if a file in this folder has been updated
688
+                        // There is no way to filter by folder at the API level...
689
+                        foreach ($changes->getItems() as $change) {
690
+                            $file = $change->getFile();
691
+                            if ($file) {
692
+                                foreach ($file->getParents() as $parent) {
693
+                                    if ($parent->getId() === $folderId) {
694
+                                        $result = true;
695
+                                    // Check if there are changes in different folders
696
+                                    } else if ($change->getId() <= $largestChangeId) {
697
+                                        // Decrement id so this change is fetched when called again
698
+                                        $largestChangeId = $change->getId();
699
+                                        $largestChangeId--;
700
+                                    }
701
+                                }
702
+                            }
703
+                        }
704
+                        $pageToken = $changes->getNextPageToken();
705
+                    } else {
706
+                        // Assuming the initial scan just occurred and changes are negligible
707
+                        break;
708
+                    }
709
+                }
710
+                $appConfig->setValue('files_external', $this->getId().'cId', $largestChangeId);
711
+                return $result;
712
+            }
713
+        }
714
+        return false;
715
+    }
716
+
717
+    /**
718
+     * check if curl is installed
719
+     */
720
+    public static function checkDependencies() {
721
+        return true;
722
+    }
723 723
 
724 724
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -123,7 +123,7 @@  discard block
 block discarded – undo
123 123
 				if (isset($this->driveFiles[$path])) {
124 124
 					$parentId = $this->driveFiles[$path]->getId();
125 125
 				} else {
126
-					$q = "title='" . str_replace("'","\\'", $name) . "' and '" . str_replace("'","\\'", $parentId) . "' in parents and trashed = false";
126
+					$q = "title='".str_replace("'", "\\'", $name)."' and '".str_replace("'", "\\'", $parentId)."' in parents and trashed = false";
127 127
 					$result = $this->service->files->listFiles(array('q' => $q))->getItems();
128 128
 					if (!empty($result)) {
129 129
 						// Google Drive allows files with the same name, ownCloud doesn't
@@ -236,7 +236,7 @@  discard block
 block discarded – undo
236 236
 				if ($result) {
237 237
 					$this->setDriveFile($path, $result);
238 238
 				}
239
-				return (bool)$result;
239
+				return (bool) $result;
240 240
 			}
241 241
 		}
242 242
 		return false;
@@ -248,7 +248,7 @@  discard block
 block discarded – undo
248 248
 		}
249 249
 		if (trim($path, '/') === '') {
250 250
 			$dir = $this->opendir($path);
251
-			if(is_resource($dir)) {
251
+			if (is_resource($dir)) {
252 252
 				while (($file = readdir($dir)) !== false) {
253 253
 					if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
254 254
 						if (!$this->unlink($path.'/'.$file)) {
@@ -276,7 +276,7 @@  discard block
 block discarded – undo
276 276
 				if ($pageToken !== true) {
277 277
 					$params['pageToken'] = $pageToken;
278 278
 				}
279
-				$params['q'] = "'" . str_replace("'","\\'", $folder->getId()) . "' in parents and trashed = false";
279
+				$params['q'] = "'".str_replace("'", "\\'", $folder->getId())."' in parents and trashed = false";
280 280
 				$children = $this->service->files->listFiles($params);
281 281
 				foreach ($children->getItems() as $child) {
282 282
 					$name = $child->getTitle();
@@ -369,7 +369,7 @@  discard block
 block discarded – undo
369 369
 	}
370 370
 
371 371
 	public function file_exists($path) {
372
-		return (bool)$this->getDriveFile($path);
372
+		return (bool) $this->getDriveFile($path);
373 373
 	}
374 374
 
375 375
 	public function unlink($path) {
@@ -379,7 +379,7 @@  discard block
 block discarded – undo
379 379
 			if ($result) {
380 380
 				$this->setDriveFile($path, false);
381 381
 			}
382
-			return (bool)$result;
382
+			return (bool) $result;
383 383
 		} else {
384 384
 			return false;
385 385
 		}
@@ -427,7 +427,7 @@  discard block
 block discarded – undo
427 427
 					}
428 428
 				}
429 429
 			}
430
-			return (bool)$result;
430
+			return (bool) $result;
431 431
 		} else {
432 432
 			return false;
433 433
 		}
@@ -462,10 +462,10 @@  discard block
 block discarded – undo
462 462
 							$response = $client->get($downloadUrl, [
463 463
 								'headers' => $httpRequest->getRequestHeaders(),
464 464
 								'stream' => true,
465
-								'verify' => realpath(__DIR__ . '/../../../3rdparty/google-api-php-client/src/Google/IO/cacerts.pem'),
465
+								'verify' => realpath(__DIR__.'/../../../3rdparty/google-api-php-client/src/Google/IO/cacerts.pem'),
466 466
 							]);
467 467
 						} catch (RequestException $e) {
468
-							if(!is_null($e->getResponse())) {
468
+							if (!is_null($e->getResponse())) {
469 469
 								if ($e->getResponse()->getStatusCode() === 404) {
470 470
 									return false;
471 471
 								} else {
@@ -499,7 +499,7 @@  discard block
 block discarded – undo
499 499
 					file_put_contents($tmpFile, $source);
500 500
 				}
501 501
 				$handle = fopen($tmpFile, $mode);
502
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
502
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
503 503
 					$this->writeBack($tmpFile, $path);
504 504
 				});
505 505
 		}
@@ -641,7 +641,7 @@  discard block
 block discarded – undo
641 641
 		if ($result) {
642 642
 			$this->setDriveFile($path, $result);
643 643
 		}
644
-		return (bool)$result;
644
+		return (bool) $result;
645 645
 	}
646 646
 
647 647
 	public function test() {
@@ -668,7 +668,7 @@  discard block
 block discarded – undo
668 668
 					'includeSubscribed' => true,
669 669
 				);
670 670
 				if (isset($startChangeId)) {
671
-					$startChangeId = (int)$startChangeId;
671
+					$startChangeId = (int) $startChangeId;
672 672
 					$largestChangeId = $startChangeId;
673 673
 					$params['startChangeId'] = $startChangeId + 1;
674 674
 				} else {
Please login to merge, or discard this patch.
lib/private/Archive/TAR.php 3 patches
Doc Comments   +1 added lines patch added patch discarded remove patch
@@ -370,6 +370,7 @@
 block discarded – undo
370 370
 
371 371
 	/**
372 372
 	 * write back temporary files
373
+	 * @param string $path
373 374
 	 */
374 375
 	function writeBack($tmpFile, $path) {
375 376
 		$this->addFile($path, $tmpFile);
Please login to merge, or discard this patch.
Indentation   +331 added lines, -331 removed lines patch added patch discarded remove patch
@@ -36,355 +36,355 @@
 block discarded – undo
36 36
 use Icewind\Streams\CallbackWrapper;
37 37
 
38 38
 class TAR extends Archive {
39
-	const PLAIN = 0;
40
-	const GZIP = 1;
41
-	const BZIP = 2;
39
+    const PLAIN = 0;
40
+    const GZIP = 1;
41
+    const BZIP = 2;
42 42
 
43
-	private $fileList;
44
-	private $cachedHeaders;
43
+    private $fileList;
44
+    private $cachedHeaders;
45 45
 
46
-	/**
47
-	 * @var \Archive_Tar tar
48
-	 */
49
-	private $tar = null;
50
-	private $path;
46
+    /**
47
+     * @var \Archive_Tar tar
48
+     */
49
+    private $tar = null;
50
+    private $path;
51 51
 
52
-	/**
53
-	 * @param string $source
54
-	 */
55
-	function __construct($source) {
56
-		$types = array(null, 'gz', 'bz2');
57
-		$this->path = $source;
58
-		$this->tar = new \Archive_Tar($source, $types[self::getTarType($source)]);
59
-	}
52
+    /**
53
+     * @param string $source
54
+     */
55
+    function __construct($source) {
56
+        $types = array(null, 'gz', 'bz2');
57
+        $this->path = $source;
58
+        $this->tar = new \Archive_Tar($source, $types[self::getTarType($source)]);
59
+    }
60 60
 
61
-	/**
62
-	 * try to detect the type of tar compression
63
-	 *
64
-	 * @param string $file
65
-	 * @return integer
66
-	 */
67
-	static public function getTarType($file) {
68
-		if (strpos($file, '.')) {
69
-			$extension = substr($file, strrpos($file, '.'));
70
-			switch ($extension) {
71
-				case '.gz':
72
-				case '.tgz':
73
-					return self::GZIP;
74
-				case '.bz':
75
-				case '.bz2':
76
-					return self::BZIP;
77
-				case '.tar':
78
-					return self::PLAIN;
79
-				default:
80
-					return self::PLAIN;
81
-			}
82
-		} else {
83
-			return self::PLAIN;
84
-		}
85
-	}
61
+    /**
62
+     * try to detect the type of tar compression
63
+     *
64
+     * @param string $file
65
+     * @return integer
66
+     */
67
+    static public function getTarType($file) {
68
+        if (strpos($file, '.')) {
69
+            $extension = substr($file, strrpos($file, '.'));
70
+            switch ($extension) {
71
+                case '.gz':
72
+                case '.tgz':
73
+                    return self::GZIP;
74
+                case '.bz':
75
+                case '.bz2':
76
+                    return self::BZIP;
77
+                case '.tar':
78
+                    return self::PLAIN;
79
+                default:
80
+                    return self::PLAIN;
81
+            }
82
+        } else {
83
+            return self::PLAIN;
84
+        }
85
+    }
86 86
 
87
-	/**
88
-	 * add an empty folder to the archive
89
-	 *
90
-	 * @param string $path
91
-	 * @return bool
92
-	 */
93
-	function addFolder($path) {
94
-		$tmpBase = \OC::$server->getTempManager()->getTemporaryFolder();
95
-		if (substr($path, -1, 1) != '/') {
96
-			$path .= '/';
97
-		}
98
-		if ($this->fileExists($path)) {
99
-			return false;
100
-		}
101
-		$parts = explode('/', $path);
102
-		$folder = $tmpBase;
103
-		foreach ($parts as $part) {
104
-			$folder .= '/' . $part;
105
-			if (!is_dir($folder)) {
106
-				mkdir($folder);
107
-			}
108
-		}
109
-		$result = $this->tar->addModify(array($tmpBase . $path), '', $tmpBase);
110
-		rmdir($tmpBase . $path);
111
-		$this->fileList = false;
112
-		$this->cachedHeaders = false;
113
-		return $result;
114
-	}
87
+    /**
88
+     * add an empty folder to the archive
89
+     *
90
+     * @param string $path
91
+     * @return bool
92
+     */
93
+    function addFolder($path) {
94
+        $tmpBase = \OC::$server->getTempManager()->getTemporaryFolder();
95
+        if (substr($path, -1, 1) != '/') {
96
+            $path .= '/';
97
+        }
98
+        if ($this->fileExists($path)) {
99
+            return false;
100
+        }
101
+        $parts = explode('/', $path);
102
+        $folder = $tmpBase;
103
+        foreach ($parts as $part) {
104
+            $folder .= '/' . $part;
105
+            if (!is_dir($folder)) {
106
+                mkdir($folder);
107
+            }
108
+        }
109
+        $result = $this->tar->addModify(array($tmpBase . $path), '', $tmpBase);
110
+        rmdir($tmpBase . $path);
111
+        $this->fileList = false;
112
+        $this->cachedHeaders = false;
113
+        return $result;
114
+    }
115 115
 
116
-	/**
117
-	 * add a file to the archive
118
-	 *
119
-	 * @param string $path
120
-	 * @param string $source either a local file or string data
121
-	 * @return bool
122
-	 */
123
-	function addFile($path, $source = '') {
124
-		if ($this->fileExists($path)) {
125
-			$this->remove($path);
126
-		}
127
-		if ($source and $source[0] == '/' and file_exists($source)) {
128
-			$source = file_get_contents($source);
129
-		}
130
-		$result = $this->tar->addString($path, $source);
131
-		$this->fileList = false;
132
-		$this->cachedHeaders = false;
133
-		return $result;
134
-	}
116
+    /**
117
+     * add a file to the archive
118
+     *
119
+     * @param string $path
120
+     * @param string $source either a local file or string data
121
+     * @return bool
122
+     */
123
+    function addFile($path, $source = '') {
124
+        if ($this->fileExists($path)) {
125
+            $this->remove($path);
126
+        }
127
+        if ($source and $source[0] == '/' and file_exists($source)) {
128
+            $source = file_get_contents($source);
129
+        }
130
+        $result = $this->tar->addString($path, $source);
131
+        $this->fileList = false;
132
+        $this->cachedHeaders = false;
133
+        return $result;
134
+    }
135 135
 
136
-	/**
137
-	 * rename a file or folder in the archive
138
-	 *
139
-	 * @param string $source
140
-	 * @param string $dest
141
-	 * @return bool
142
-	 */
143
-	function rename($source, $dest) {
144
-		//no proper way to delete, rename entire archive, rename file and remake archive
145
-		$tmp = \OCP\Files::tmpFolder();
146
-		$this->tar->extract($tmp);
147
-		rename($tmp . $source, $tmp . $dest);
148
-		$this->tar = null;
149
-		unlink($this->path);
150
-		$types = array(null, 'gz', 'bz');
151
-		$this->tar = new \Archive_Tar($this->path, $types[self::getTarType($this->path)]);
152
-		$this->tar->createModify(array($tmp), '', $tmp . '/');
153
-		$this->fileList = false;
154
-		$this->cachedHeaders = false;
155
-		return true;
156
-	}
136
+    /**
137
+     * rename a file or folder in the archive
138
+     *
139
+     * @param string $source
140
+     * @param string $dest
141
+     * @return bool
142
+     */
143
+    function rename($source, $dest) {
144
+        //no proper way to delete, rename entire archive, rename file and remake archive
145
+        $tmp = \OCP\Files::tmpFolder();
146
+        $this->tar->extract($tmp);
147
+        rename($tmp . $source, $tmp . $dest);
148
+        $this->tar = null;
149
+        unlink($this->path);
150
+        $types = array(null, 'gz', 'bz');
151
+        $this->tar = new \Archive_Tar($this->path, $types[self::getTarType($this->path)]);
152
+        $this->tar->createModify(array($tmp), '', $tmp . '/');
153
+        $this->fileList = false;
154
+        $this->cachedHeaders = false;
155
+        return true;
156
+    }
157 157
 
158
-	/**
159
-	 * @param string $file
160
-	 */
161
-	private function getHeader($file) {
162
-		if (!$this->cachedHeaders) {
163
-			$this->cachedHeaders = $this->tar->listContent();
164
-		}
165
-		foreach ($this->cachedHeaders as $header) {
166
-			if ($file == $header['filename']
167
-				or $file . '/' == $header['filename']
168
-				or '/' . $file . '/' == $header['filename']
169
-				or '/' . $file == $header['filename']
170
-			) {
171
-				return $header;
172
-			}
173
-		}
174
-		return null;
175
-	}
158
+    /**
159
+     * @param string $file
160
+     */
161
+    private function getHeader($file) {
162
+        if (!$this->cachedHeaders) {
163
+            $this->cachedHeaders = $this->tar->listContent();
164
+        }
165
+        foreach ($this->cachedHeaders as $header) {
166
+            if ($file == $header['filename']
167
+                or $file . '/' == $header['filename']
168
+                or '/' . $file . '/' == $header['filename']
169
+                or '/' . $file == $header['filename']
170
+            ) {
171
+                return $header;
172
+            }
173
+        }
174
+        return null;
175
+    }
176 176
 
177
-	/**
178
-	 * get the uncompressed size of a file in the archive
179
-	 *
180
-	 * @param string $path
181
-	 * @return int
182
-	 */
183
-	function filesize($path) {
184
-		$stat = $this->getHeader($path);
185
-		return $stat['size'];
186
-	}
177
+    /**
178
+     * get the uncompressed size of a file in the archive
179
+     *
180
+     * @param string $path
181
+     * @return int
182
+     */
183
+    function filesize($path) {
184
+        $stat = $this->getHeader($path);
185
+        return $stat['size'];
186
+    }
187 187
 
188
-	/**
189
-	 * get the last modified time of a file in the archive
190
-	 *
191
-	 * @param string $path
192
-	 * @return int
193
-	 */
194
-	function mtime($path) {
195
-		$stat = $this->getHeader($path);
196
-		return $stat['mtime'];
197
-	}
188
+    /**
189
+     * get the last modified time of a file in the archive
190
+     *
191
+     * @param string $path
192
+     * @return int
193
+     */
194
+    function mtime($path) {
195
+        $stat = $this->getHeader($path);
196
+        return $stat['mtime'];
197
+    }
198 198
 
199
-	/**
200
-	 * get the files in a folder
201
-	 *
202
-	 * @param string $path
203
-	 * @return array
204
-	 */
205
-	function getFolder($path) {
206
-		$files = $this->getFiles();
207
-		$folderContent = array();
208
-		$pathLength = strlen($path);
209
-		foreach ($files as $file) {
210
-			if ($file[0] == '/') {
211
-				$file = substr($file, 1);
212
-			}
213
-			if (substr($file, 0, $pathLength) == $path and $file != $path) {
214
-				$result = substr($file, $pathLength);
215
-				if ($pos = strpos($result, '/')) {
216
-					$result = substr($result, 0, $pos + 1);
217
-				}
218
-				if (array_search($result, $folderContent) === false) {
219
-					$folderContent[] = $result;
220
-				}
221
-			}
222
-		}
223
-		return $folderContent;
224
-	}
199
+    /**
200
+     * get the files in a folder
201
+     *
202
+     * @param string $path
203
+     * @return array
204
+     */
205
+    function getFolder($path) {
206
+        $files = $this->getFiles();
207
+        $folderContent = array();
208
+        $pathLength = strlen($path);
209
+        foreach ($files as $file) {
210
+            if ($file[0] == '/') {
211
+                $file = substr($file, 1);
212
+            }
213
+            if (substr($file, 0, $pathLength) == $path and $file != $path) {
214
+                $result = substr($file, $pathLength);
215
+                if ($pos = strpos($result, '/')) {
216
+                    $result = substr($result, 0, $pos + 1);
217
+                }
218
+                if (array_search($result, $folderContent) === false) {
219
+                    $folderContent[] = $result;
220
+                }
221
+            }
222
+        }
223
+        return $folderContent;
224
+    }
225 225
 
226
-	/**
227
-	 * get all files in the archive
228
-	 *
229
-	 * @return array
230
-	 */
231
-	function getFiles() {
232
-		if ($this->fileList) {
233
-			return $this->fileList;
234
-		}
235
-		if (!$this->cachedHeaders) {
236
-			$this->cachedHeaders = $this->tar->listContent();
237
-		}
238
-		$files = array();
239
-		foreach ($this->cachedHeaders as $header) {
240
-			$files[] = $header['filename'];
241
-		}
242
-		$this->fileList = $files;
243
-		return $files;
244
-	}
226
+    /**
227
+     * get all files in the archive
228
+     *
229
+     * @return array
230
+     */
231
+    function getFiles() {
232
+        if ($this->fileList) {
233
+            return $this->fileList;
234
+        }
235
+        if (!$this->cachedHeaders) {
236
+            $this->cachedHeaders = $this->tar->listContent();
237
+        }
238
+        $files = array();
239
+        foreach ($this->cachedHeaders as $header) {
240
+            $files[] = $header['filename'];
241
+        }
242
+        $this->fileList = $files;
243
+        return $files;
244
+    }
245 245
 
246
-	/**
247
-	 * get the content of a file
248
-	 *
249
-	 * @param string $path
250
-	 * @return string
251
-	 */
252
-	function getFile($path) {
253
-		return $this->tar->extractInString($path);
254
-	}
246
+    /**
247
+     * get the content of a file
248
+     *
249
+     * @param string $path
250
+     * @return string
251
+     */
252
+    function getFile($path) {
253
+        return $this->tar->extractInString($path);
254
+    }
255 255
 
256
-	/**
257
-	 * extract a single file from the archive
258
-	 *
259
-	 * @param string $path
260
-	 * @param string $dest
261
-	 * @return bool
262
-	 */
263
-	function extractFile($path, $dest) {
264
-		$tmp = \OCP\Files::tmpFolder();
265
-		if (!$this->fileExists($path)) {
266
-			return false;
267
-		}
268
-		if ($this->fileExists('/' . $path)) {
269
-			$success = $this->tar->extractList(array('/' . $path), $tmp);
270
-		} else {
271
-			$success = $this->tar->extractList(array($path), $tmp);
272
-		}
273
-		if ($success) {
274
-			rename($tmp . $path, $dest);
275
-		}
276
-		\OCP\Files::rmdirr($tmp);
277
-		return $success;
278
-	}
256
+    /**
257
+     * extract a single file from the archive
258
+     *
259
+     * @param string $path
260
+     * @param string $dest
261
+     * @return bool
262
+     */
263
+    function extractFile($path, $dest) {
264
+        $tmp = \OCP\Files::tmpFolder();
265
+        if (!$this->fileExists($path)) {
266
+            return false;
267
+        }
268
+        if ($this->fileExists('/' . $path)) {
269
+            $success = $this->tar->extractList(array('/' . $path), $tmp);
270
+        } else {
271
+            $success = $this->tar->extractList(array($path), $tmp);
272
+        }
273
+        if ($success) {
274
+            rename($tmp . $path, $dest);
275
+        }
276
+        \OCP\Files::rmdirr($tmp);
277
+        return $success;
278
+    }
279 279
 
280
-	/**
281
-	 * extract the archive
282
-	 *
283
-	 * @param string $dest
284
-	 * @return bool
285
-	 */
286
-	function extract($dest) {
287
-		return $this->tar->extract($dest);
288
-	}
280
+    /**
281
+     * extract the archive
282
+     *
283
+     * @param string $dest
284
+     * @return bool
285
+     */
286
+    function extract($dest) {
287
+        return $this->tar->extract($dest);
288
+    }
289 289
 
290
-	/**
291
-	 * check if a file or folder exists in the archive
292
-	 *
293
-	 * @param string $path
294
-	 * @return bool
295
-	 */
296
-	function fileExists($path) {
297
-		$files = $this->getFiles();
298
-		if ((array_search($path, $files) !== false) or (array_search($path . '/', $files) !== false)) {
299
-			return true;
300
-		} else {
301
-			$folderPath = $path;
302
-			if (substr($folderPath, -1, 1) != '/') {
303
-				$folderPath .= '/';
304
-			}
305
-			$pathLength = strlen($folderPath);
306
-			foreach ($files as $file) {
307
-				if (strlen($file) > $pathLength and substr($file, 0, $pathLength) == $folderPath) {
308
-					return true;
309
-				}
310
-			}
311
-		}
312
-		if ($path[0] != '/') { //not all programs agree on the use of a leading /
313
-			return $this->fileExists('/' . $path);
314
-		} else {
315
-			return false;
316
-		}
317
-	}
290
+    /**
291
+     * check if a file or folder exists in the archive
292
+     *
293
+     * @param string $path
294
+     * @return bool
295
+     */
296
+    function fileExists($path) {
297
+        $files = $this->getFiles();
298
+        if ((array_search($path, $files) !== false) or (array_search($path . '/', $files) !== false)) {
299
+            return true;
300
+        } else {
301
+            $folderPath = $path;
302
+            if (substr($folderPath, -1, 1) != '/') {
303
+                $folderPath .= '/';
304
+            }
305
+            $pathLength = strlen($folderPath);
306
+            foreach ($files as $file) {
307
+                if (strlen($file) > $pathLength and substr($file, 0, $pathLength) == $folderPath) {
308
+                    return true;
309
+                }
310
+            }
311
+        }
312
+        if ($path[0] != '/') { //not all programs agree on the use of a leading /
313
+            return $this->fileExists('/' . $path);
314
+        } else {
315
+            return false;
316
+        }
317
+    }
318 318
 
319
-	/**
320
-	 * remove a file or folder from the archive
321
-	 *
322
-	 * @param string $path
323
-	 * @return bool
324
-	 */
325
-	function remove($path) {
326
-		if (!$this->fileExists($path)) {
327
-			return false;
328
-		}
329
-		$this->fileList = false;
330
-		$this->cachedHeaders = false;
331
-		//no proper way to delete, extract entire archive, delete file and remake archive
332
-		$tmp = \OCP\Files::tmpFolder();
333
-		$this->tar->extract($tmp);
334
-		\OCP\Files::rmdirr($tmp . $path);
335
-		$this->tar = null;
336
-		unlink($this->path);
337
-		$this->reopen();
338
-		$this->tar->createModify(array($tmp), '', $tmp);
339
-		return true;
340
-	}
319
+    /**
320
+     * remove a file or folder from the archive
321
+     *
322
+     * @param string $path
323
+     * @return bool
324
+     */
325
+    function remove($path) {
326
+        if (!$this->fileExists($path)) {
327
+            return false;
328
+        }
329
+        $this->fileList = false;
330
+        $this->cachedHeaders = false;
331
+        //no proper way to delete, extract entire archive, delete file and remake archive
332
+        $tmp = \OCP\Files::tmpFolder();
333
+        $this->tar->extract($tmp);
334
+        \OCP\Files::rmdirr($tmp . $path);
335
+        $this->tar = null;
336
+        unlink($this->path);
337
+        $this->reopen();
338
+        $this->tar->createModify(array($tmp), '', $tmp);
339
+        return true;
340
+    }
341 341
 
342
-	/**
343
-	 * get a file handler
344
-	 *
345
-	 * @param string $path
346
-	 * @param string $mode
347
-	 * @return resource
348
-	 */
349
-	function getStream($path, $mode) {
350
-		if (strrpos($path, '.') !== false) {
351
-			$ext = substr($path, strrpos($path, '.'));
352
-		} else {
353
-			$ext = '';
354
-		}
355
-		$tmpFile = \OCP\Files::tmpFile($ext);
356
-		if ($this->fileExists($path)) {
357
-			$this->extractFile($path, $tmpFile);
358
-		} elseif ($mode == 'r' or $mode == 'rb') {
359
-			return false;
360
-		}
361
-		if ($mode == 'r' or $mode == 'rb') {
362
-			return fopen($tmpFile, $mode);
363
-		} else {
364
-			$handle = fopen($tmpFile, $mode);
365
-			return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
366
-				$this->writeBack($tmpFile, $path);
367
-			});
368
-		}
369
-	}
342
+    /**
343
+     * get a file handler
344
+     *
345
+     * @param string $path
346
+     * @param string $mode
347
+     * @return resource
348
+     */
349
+    function getStream($path, $mode) {
350
+        if (strrpos($path, '.') !== false) {
351
+            $ext = substr($path, strrpos($path, '.'));
352
+        } else {
353
+            $ext = '';
354
+        }
355
+        $tmpFile = \OCP\Files::tmpFile($ext);
356
+        if ($this->fileExists($path)) {
357
+            $this->extractFile($path, $tmpFile);
358
+        } elseif ($mode == 'r' or $mode == 'rb') {
359
+            return false;
360
+        }
361
+        if ($mode == 'r' or $mode == 'rb') {
362
+            return fopen($tmpFile, $mode);
363
+        } else {
364
+            $handle = fopen($tmpFile, $mode);
365
+            return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
366
+                $this->writeBack($tmpFile, $path);
367
+            });
368
+        }
369
+    }
370 370
 
371
-	/**
372
-	 * write back temporary files
373
-	 */
374
-	function writeBack($tmpFile, $path) {
375
-		$this->addFile($path, $tmpFile);
376
-		unlink($tmpFile);
377
-	}
371
+    /**
372
+     * write back temporary files
373
+     */
374
+    function writeBack($tmpFile, $path) {
375
+        $this->addFile($path, $tmpFile);
376
+        unlink($tmpFile);
377
+    }
378 378
 
379
-	/**
380
-	 * reopen the archive to ensure everything is written
381
-	 */
382
-	private function reopen() {
383
-		if ($this->tar) {
384
-			$this->tar->_close();
385
-			$this->tar = null;
386
-		}
387
-		$types = array(null, 'gz', 'bz');
388
-		$this->tar = new \Archive_Tar($this->path, $types[self::getTarType($this->path)]);
389
-	}
379
+    /**
380
+     * reopen the archive to ensure everything is written
381
+     */
382
+    private function reopen() {
383
+        if ($this->tar) {
384
+            $this->tar->_close();
385
+            $this->tar = null;
386
+        }
387
+        $types = array(null, 'gz', 'bz');
388
+        $this->tar = new \Archive_Tar($this->path, $types[self::getTarType($this->path)]);
389
+    }
390 390
 }
Please login to merge, or discard this patch.
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -101,13 +101,13 @@  discard block
 block discarded – undo
101 101
 		$parts = explode('/', $path);
102 102
 		$folder = $tmpBase;
103 103
 		foreach ($parts as $part) {
104
-			$folder .= '/' . $part;
104
+			$folder .= '/'.$part;
105 105
 			if (!is_dir($folder)) {
106 106
 				mkdir($folder);
107 107
 			}
108 108
 		}
109
-		$result = $this->tar->addModify(array($tmpBase . $path), '', $tmpBase);
110
-		rmdir($tmpBase . $path);
109
+		$result = $this->tar->addModify(array($tmpBase.$path), '', $tmpBase);
110
+		rmdir($tmpBase.$path);
111 111
 		$this->fileList = false;
112 112
 		$this->cachedHeaders = false;
113 113
 		return $result;
@@ -144,12 +144,12 @@  discard block
 block discarded – undo
144 144
 		//no proper way to delete, rename entire archive, rename file and remake archive
145 145
 		$tmp = \OCP\Files::tmpFolder();
146 146
 		$this->tar->extract($tmp);
147
-		rename($tmp . $source, $tmp . $dest);
147
+		rename($tmp.$source, $tmp.$dest);
148 148
 		$this->tar = null;
149 149
 		unlink($this->path);
150 150
 		$types = array(null, 'gz', 'bz');
151 151
 		$this->tar = new \Archive_Tar($this->path, $types[self::getTarType($this->path)]);
152
-		$this->tar->createModify(array($tmp), '', $tmp . '/');
152
+		$this->tar->createModify(array($tmp), '', $tmp.'/');
153 153
 		$this->fileList = false;
154 154
 		$this->cachedHeaders = false;
155 155
 		return true;
@@ -164,9 +164,9 @@  discard block
 block discarded – undo
164 164
 		}
165 165
 		foreach ($this->cachedHeaders as $header) {
166 166
 			if ($file == $header['filename']
167
-				or $file . '/' == $header['filename']
168
-				or '/' . $file . '/' == $header['filename']
169
-				or '/' . $file == $header['filename']
167
+				or $file.'/' == $header['filename']
168
+				or '/'.$file.'/' == $header['filename']
169
+				or '/'.$file == $header['filename']
170 170
 			) {
171 171
 				return $header;
172 172
 			}
@@ -265,13 +265,13 @@  discard block
 block discarded – undo
265 265
 		if (!$this->fileExists($path)) {
266 266
 			return false;
267 267
 		}
268
-		if ($this->fileExists('/' . $path)) {
269
-			$success = $this->tar->extractList(array('/' . $path), $tmp);
268
+		if ($this->fileExists('/'.$path)) {
269
+			$success = $this->tar->extractList(array('/'.$path), $tmp);
270 270
 		} else {
271 271
 			$success = $this->tar->extractList(array($path), $tmp);
272 272
 		}
273 273
 		if ($success) {
274
-			rename($tmp . $path, $dest);
274
+			rename($tmp.$path, $dest);
275 275
 		}
276 276
 		\OCP\Files::rmdirr($tmp);
277 277
 		return $success;
@@ -295,7 +295,7 @@  discard block
 block discarded – undo
295 295
 	 */
296 296
 	function fileExists($path) {
297 297
 		$files = $this->getFiles();
298
-		if ((array_search($path, $files) !== false) or (array_search($path . '/', $files) !== false)) {
298
+		if ((array_search($path, $files) !== false) or (array_search($path.'/', $files) !== false)) {
299 299
 			return true;
300 300
 		} else {
301 301
 			$folderPath = $path;
@@ -310,7 +310,7 @@  discard block
 block discarded – undo
310 310
 			}
311 311
 		}
312 312
 		if ($path[0] != '/') { //not all programs agree on the use of a leading /
313
-			return $this->fileExists('/' . $path);
313
+			return $this->fileExists('/'.$path);
314 314
 		} else {
315 315
 			return false;
316 316
 		}
@@ -331,7 +331,7 @@  discard block
 block discarded – undo
331 331
 		//no proper way to delete, extract entire archive, delete file and remake archive
332 332
 		$tmp = \OCP\Files::tmpFolder();
333 333
 		$this->tar->extract($tmp);
334
-		\OCP\Files::rmdirr($tmp . $path);
334
+		\OCP\Files::rmdirr($tmp.$path);
335 335
 		$this->tar = null;
336 336
 		unlink($this->path);
337 337
 		$this->reopen();
@@ -362,7 +362,7 @@  discard block
 block discarded – undo
362 362
 			return fopen($tmpFile, $mode);
363 363
 		} else {
364 364
 			$handle = fopen($tmpFile, $mode);
365
-			return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
365
+			return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
366 366
 				$this->writeBack($tmpFile, $path);
367 367
 			});
368 368
 		}
Please login to merge, or discard this patch.
lib/private/Files/ObjectStore/ObjectStoreStorage.php 3 patches
Doc Comments   +6 added lines patch added patch discarded remove patch
@@ -162,6 +162,9 @@  discard block
 block discarded – undo
162 162
 		return true;
163 163
 	}
164 164
 
165
+	/**
166
+	 * @param string $path
167
+	 */
165 168
 	private function rmObjects($path) {
166 169
 		$children = $this->getCache()->getFolderContents($path);
167 170
 		foreach ($children as $child) {
@@ -364,6 +367,9 @@  discard block
 block discarded – undo
364 367
 		return true;
365 368
 	}
366 369
 
370
+	/**
371
+	 * @param string $path
372
+	 */
367 373
 	public function writeBack($tmpFile, $path) {
368 374
 		$stat = $this->stat($path);
369 375
 		if (empty($stat)) {
Please login to merge, or discard this patch.
Indentation   +370 added lines, -370 removed lines patch added patch discarded remove patch
@@ -31,374 +31,374 @@
 block discarded – undo
31 31
 use OCP\Files\ObjectStore\IObjectStore;
32 32
 
33 33
 class ObjectStoreStorage extends \OC\Files\Storage\Common {
34
-	/**
35
-	 * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
36
-	 */
37
-	protected $objectStore;
38
-	/**
39
-	 * @var string $id
40
-	 */
41
-	protected $id;
42
-	/**
43
-	 * @var \OC\User\User $user
44
-	 */
45
-	protected $user;
46
-
47
-	private $objectPrefix = 'urn:oid:';
48
-
49
-	public function __construct($params) {
50
-		if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
51
-			$this->objectStore = $params['objectstore'];
52
-		} else {
53
-			throw new \Exception('missing IObjectStore instance');
54
-		}
55
-		if (isset($params['storageid'])) {
56
-			$this->id = 'object::store:' . $params['storageid'];
57
-		} else {
58
-			$this->id = 'object::store:' . $this->objectStore->getStorageId();
59
-		}
60
-		if (isset($params['objectPrefix'])) {
61
-			$this->objectPrefix = $params['objectPrefix'];
62
-		}
63
-		//initialize cache with root directory in cache
64
-		if (!$this->is_dir('/')) {
65
-			$this->mkdir('/');
66
-		}
67
-	}
68
-
69
-	public function mkdir($path) {
70
-		$path = $this->normalizePath($path);
71
-
72
-		if ($this->file_exists($path)) {
73
-			return false;
74
-		}
75
-
76
-		$mTime = time();
77
-		$data = [
78
-			'mimetype' => 'httpd/unix-directory',
79
-			'size' => 0,
80
-			'mtime' => $mTime,
81
-			'storage_mtime' => $mTime,
82
-			'permissions' => \OCP\Constants::PERMISSION_ALL,
83
-		];
84
-		if ($path === '') {
85
-			//create root on the fly
86
-			$data['etag'] = $this->getETag('');
87
-			$this->getCache()->put('', $data);
88
-			return true;
89
-		} else {
90
-			// if parent does not exist, create it
91
-			$parent = $this->normalizePath(dirname($path));
92
-			$parentType = $this->filetype($parent);
93
-			if ($parentType === false) {
94
-				if (!$this->mkdir($parent)) {
95
-					// something went wrong
96
-					return false;
97
-				}
98
-			} else if ($parentType === 'file') {
99
-				// parent is a file
100
-				return false;
101
-			}
102
-			// finally create the new dir
103
-			$mTime = time(); // update mtime
104
-			$data['mtime'] = $mTime;
105
-			$data['storage_mtime'] = $mTime;
106
-			$data['etag'] = $this->getETag($path);
107
-			$this->getCache()->put($path, $data);
108
-			return true;
109
-		}
110
-	}
111
-
112
-	/**
113
-	 * @param string $path
114
-	 * @return string
115
-	 */
116
-	private function normalizePath($path) {
117
-		$path = trim($path, '/');
118
-		//FIXME why do we sometimes get a path like 'files//username'?
119
-		$path = str_replace('//', '/', $path);
120
-
121
-		// dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
122
-		if (!$path || $path === '.') {
123
-			$path = '';
124
-		}
125
-
126
-		return $path;
127
-	}
128
-
129
-	/**
130
-	 * Object Stores use a NoopScanner because metadata is directly stored in
131
-	 * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
132
-	 *
133
-	 * @param string $path
134
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
135
-	 * @return \OC\Files\ObjectStore\NoopScanner
136
-	 */
137
-	public function getScanner($path = '', $storage = null) {
138
-		if (!$storage) {
139
-			$storage = $this;
140
-		}
141
-		if (!isset($this->scanner)) {
142
-			$this->scanner = new NoopScanner($storage);
143
-		}
144
-		return $this->scanner;
145
-	}
146
-
147
-	public function getId() {
148
-		return $this->id;
149
-	}
150
-
151
-	public function rmdir($path) {
152
-		$path = $this->normalizePath($path);
153
-
154
-		if (!$this->is_dir($path)) {
155
-			return false;
156
-		}
157
-
158
-		$this->rmObjects($path);
159
-
160
-		$this->getCache()->remove($path);
161
-
162
-		return true;
163
-	}
164
-
165
-	private function rmObjects($path) {
166
-		$children = $this->getCache()->getFolderContents($path);
167
-		foreach ($children as $child) {
168
-			if ($child['mimetype'] === 'httpd/unix-directory') {
169
-				$this->rmObjects($child['path']);
170
-			} else {
171
-				$this->unlink($child['path']);
172
-			}
173
-		}
174
-	}
175
-
176
-	public function unlink($path) {
177
-		$path = $this->normalizePath($path);
178
-		$stat = $this->stat($path);
179
-
180
-		if ($stat && isset($stat['fileid'])) {
181
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
182
-				return $this->rmdir($path);
183
-			}
184
-			try {
185
-				$this->objectStore->deleteObject($this->getURN($stat['fileid']));
186
-			} catch (\Exception $ex) {
187
-				if ($ex->getCode() !== 404) {
188
-					\OCP\Util::writeLog('objectstore', 'Could not delete object: ' . $ex->getMessage(), \OCP\Util::ERROR);
189
-					return false;
190
-				} else {
191
-					//removing from cache is ok as it does not exist in the objectstore anyway
192
-				}
193
-			}
194
-			$this->getCache()->remove($path);
195
-			return true;
196
-		}
197
-		return false;
198
-	}
199
-
200
-	public function stat($path) {
201
-		$path = $this->normalizePath($path);
202
-		$cacheEntry = $this->getCache()->get($path);
203
-		if ($cacheEntry instanceof CacheEntry) {
204
-			return $cacheEntry->getData();
205
-		} else {
206
-			return false;
207
-		}
208
-	}
209
-
210
-	/**
211
-	 * Override this method if you need a different unique resource identifier for your object storage implementation.
212
-	 * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
213
-	 * You may need a mapping table to store your URN if it cannot be generated from the fileid.
214
-	 *
215
-	 * @param int $fileId the fileid
216
-	 * @return null|string the unified resource name used to identify the object
217
-	 */
218
-	protected function getURN($fileId) {
219
-		if (is_numeric($fileId)) {
220
-			return $this->objectPrefix . $fileId;
221
-		}
222
-		return null;
223
-	}
224
-
225
-	public function opendir($path) {
226
-		$path = $this->normalizePath($path);
227
-
228
-		try {
229
-			$files = array();
230
-			$folderContents = $this->getCache()->getFolderContents($path);
231
-			foreach ($folderContents as $file) {
232
-				$files[] = $file['name'];
233
-			}
234
-
235
-			return IteratorDirectory::wrap($files);
236
-		} catch (\Exception $e) {
237
-			\OCP\Util::writeLog('objectstore', $e->getMessage(), \OCP\Util::ERROR);
238
-			return false;
239
-		}
240
-	}
241
-
242
-	public function filetype($path) {
243
-		$path = $this->normalizePath($path);
244
-		$stat = $this->stat($path);
245
-		if ($stat) {
246
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
247
-				return 'dir';
248
-			}
249
-			return 'file';
250
-		} else {
251
-			return false;
252
-		}
253
-	}
254
-
255
-	public function fopen($path, $mode) {
256
-		$path = $this->normalizePath($path);
257
-
258
-		switch ($mode) {
259
-			case 'r':
260
-			case 'rb':
261
-				$stat = $this->stat($path);
262
-				if (is_array($stat)) {
263
-					try {
264
-						return $this->objectStore->readObject($this->getURN($stat['fileid']));
265
-					} catch (\Exception $ex) {
266
-						\OCP\Util::writeLog('objectstore', 'Could not get object: ' . $ex->getMessage(), \OCP\Util::ERROR);
267
-						return false;
268
-					}
269
-				} else {
270
-					return false;
271
-				}
272
-			case 'w':
273
-			case 'wb':
274
-			case 'a':
275
-			case 'ab':
276
-			case 'r+':
277
-			case 'w+':
278
-			case 'wb+':
279
-			case 'a+':
280
-			case 'x':
281
-			case 'x+':
282
-			case 'c':
283
-			case 'c+':
284
-				if (strrpos($path, '.') !== false) {
285
-					$ext = substr($path, strrpos($path, '.'));
286
-				} else {
287
-					$ext = '';
288
-				}
289
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
290
-				if ($this->file_exists($path)) {
291
-					$source = $this->fopen($path, 'r');
292
-					file_put_contents($tmpFile, $source);
293
-				}
294
-				$handle = fopen($tmpFile, $mode);
295
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
296
-					$this->writeBack($tmpFile, $path);
297
-				});
298
-		}
299
-		return false;
300
-	}
301
-
302
-	public function file_exists($path) {
303
-		$path = $this->normalizePath($path);
304
-		return (bool)$this->stat($path);
305
-	}
306
-
307
-	public function rename($source, $target) {
308
-		$source = $this->normalizePath($source);
309
-		$target = $this->normalizePath($target);
310
-		$this->remove($target);
311
-		$this->getCache()->move($source, $target);
312
-		$this->touch(dirname($target));
313
-		return true;
314
-	}
315
-
316
-	public function getMimeType($path) {
317
-		$path = $this->normalizePath($path);
318
-		$stat = $this->stat($path);
319
-		if (is_array($stat)) {
320
-			return $stat['mimetype'];
321
-		} else {
322
-			return false;
323
-		}
324
-	}
325
-
326
-	public function touch($path, $mtime = null) {
327
-		if (is_null($mtime)) {
328
-			$mtime = time();
329
-		}
330
-
331
-		$path = $this->normalizePath($path);
332
-		$dirName = dirname($path);
333
-		$parentExists = $this->is_dir($dirName);
334
-		if (!$parentExists) {
335
-			return false;
336
-		}
337
-
338
-		$stat = $this->stat($path);
339
-		if (is_array($stat)) {
340
-			// update existing mtime in db
341
-			$stat['mtime'] = $mtime;
342
-			$this->getCache()->update($stat['fileid'], $stat);
343
-		} else {
344
-			$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
345
-			// create new file
346
-			$stat = array(
347
-				'etag' => $this->getETag($path),
348
-				'mimetype' => $mimeType,
349
-				'size' => 0,
350
-				'mtime' => $mtime,
351
-				'storage_mtime' => $mtime,
352
-				'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
353
-			);
354
-			$fileId = $this->getCache()->put($path, $stat);
355
-			try {
356
-				//read an empty file from memory
357
-				$this->objectStore->writeObject($this->getURN($fileId), fopen('php://memory', 'r'));
358
-			} catch (\Exception $ex) {
359
-				$this->getCache()->remove($path);
360
-				\OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR);
361
-				return false;
362
-			}
363
-		}
364
-		return true;
365
-	}
366
-
367
-	public function writeBack($tmpFile, $path) {
368
-		$stat = $this->stat($path);
369
-		if (empty($stat)) {
370
-			// create new file
371
-			$stat = array(
372
-				'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
373
-			);
374
-		}
375
-		// update stat with new data
376
-		$mTime = time();
377
-		$stat['size'] = filesize($tmpFile);
378
-		$stat['mtime'] = $mTime;
379
-		$stat['storage_mtime'] = $mTime;
380
-		$stat['mimetype'] = \OC::$server->getMimeTypeDetector()->detect($tmpFile);
381
-		$stat['etag'] = $this->getETag($path);
382
-
383
-		$fileId = $this->getCache()->put($path, $stat);
384
-		try {
385
-			//upload to object storage
386
-			$this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r'));
387
-		} catch (\Exception $ex) {
388
-			$this->getCache()->remove($path);
389
-			\OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR);
390
-			throw $ex; // make this bubble up
391
-		}
392
-	}
393
-
394
-	/**
395
-	 * external changes are not supported, exclusive access to the object storage is assumed
396
-	 *
397
-	 * @param string $path
398
-	 * @param int $time
399
-	 * @return false
400
-	 */
401
-	public function hasUpdated($path, $time) {
402
-		return false;
403
-	}
34
+    /**
35
+     * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
36
+     */
37
+    protected $objectStore;
38
+    /**
39
+     * @var string $id
40
+     */
41
+    protected $id;
42
+    /**
43
+     * @var \OC\User\User $user
44
+     */
45
+    protected $user;
46
+
47
+    private $objectPrefix = 'urn:oid:';
48
+
49
+    public function __construct($params) {
50
+        if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
51
+            $this->objectStore = $params['objectstore'];
52
+        } else {
53
+            throw new \Exception('missing IObjectStore instance');
54
+        }
55
+        if (isset($params['storageid'])) {
56
+            $this->id = 'object::store:' . $params['storageid'];
57
+        } else {
58
+            $this->id = 'object::store:' . $this->objectStore->getStorageId();
59
+        }
60
+        if (isset($params['objectPrefix'])) {
61
+            $this->objectPrefix = $params['objectPrefix'];
62
+        }
63
+        //initialize cache with root directory in cache
64
+        if (!$this->is_dir('/')) {
65
+            $this->mkdir('/');
66
+        }
67
+    }
68
+
69
+    public function mkdir($path) {
70
+        $path = $this->normalizePath($path);
71
+
72
+        if ($this->file_exists($path)) {
73
+            return false;
74
+        }
75
+
76
+        $mTime = time();
77
+        $data = [
78
+            'mimetype' => 'httpd/unix-directory',
79
+            'size' => 0,
80
+            'mtime' => $mTime,
81
+            'storage_mtime' => $mTime,
82
+            'permissions' => \OCP\Constants::PERMISSION_ALL,
83
+        ];
84
+        if ($path === '') {
85
+            //create root on the fly
86
+            $data['etag'] = $this->getETag('');
87
+            $this->getCache()->put('', $data);
88
+            return true;
89
+        } else {
90
+            // if parent does not exist, create it
91
+            $parent = $this->normalizePath(dirname($path));
92
+            $parentType = $this->filetype($parent);
93
+            if ($parentType === false) {
94
+                if (!$this->mkdir($parent)) {
95
+                    // something went wrong
96
+                    return false;
97
+                }
98
+            } else if ($parentType === 'file') {
99
+                // parent is a file
100
+                return false;
101
+            }
102
+            // finally create the new dir
103
+            $mTime = time(); // update mtime
104
+            $data['mtime'] = $mTime;
105
+            $data['storage_mtime'] = $mTime;
106
+            $data['etag'] = $this->getETag($path);
107
+            $this->getCache()->put($path, $data);
108
+            return true;
109
+        }
110
+    }
111
+
112
+    /**
113
+     * @param string $path
114
+     * @return string
115
+     */
116
+    private function normalizePath($path) {
117
+        $path = trim($path, '/');
118
+        //FIXME why do we sometimes get a path like 'files//username'?
119
+        $path = str_replace('//', '/', $path);
120
+
121
+        // dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
122
+        if (!$path || $path === '.') {
123
+            $path = '';
124
+        }
125
+
126
+        return $path;
127
+    }
128
+
129
+    /**
130
+     * Object Stores use a NoopScanner because metadata is directly stored in
131
+     * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
132
+     *
133
+     * @param string $path
134
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
135
+     * @return \OC\Files\ObjectStore\NoopScanner
136
+     */
137
+    public function getScanner($path = '', $storage = null) {
138
+        if (!$storage) {
139
+            $storage = $this;
140
+        }
141
+        if (!isset($this->scanner)) {
142
+            $this->scanner = new NoopScanner($storage);
143
+        }
144
+        return $this->scanner;
145
+    }
146
+
147
+    public function getId() {
148
+        return $this->id;
149
+    }
150
+
151
+    public function rmdir($path) {
152
+        $path = $this->normalizePath($path);
153
+
154
+        if (!$this->is_dir($path)) {
155
+            return false;
156
+        }
157
+
158
+        $this->rmObjects($path);
159
+
160
+        $this->getCache()->remove($path);
161
+
162
+        return true;
163
+    }
164
+
165
+    private function rmObjects($path) {
166
+        $children = $this->getCache()->getFolderContents($path);
167
+        foreach ($children as $child) {
168
+            if ($child['mimetype'] === 'httpd/unix-directory') {
169
+                $this->rmObjects($child['path']);
170
+            } else {
171
+                $this->unlink($child['path']);
172
+            }
173
+        }
174
+    }
175
+
176
+    public function unlink($path) {
177
+        $path = $this->normalizePath($path);
178
+        $stat = $this->stat($path);
179
+
180
+        if ($stat && isset($stat['fileid'])) {
181
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
182
+                return $this->rmdir($path);
183
+            }
184
+            try {
185
+                $this->objectStore->deleteObject($this->getURN($stat['fileid']));
186
+            } catch (\Exception $ex) {
187
+                if ($ex->getCode() !== 404) {
188
+                    \OCP\Util::writeLog('objectstore', 'Could not delete object: ' . $ex->getMessage(), \OCP\Util::ERROR);
189
+                    return false;
190
+                } else {
191
+                    //removing from cache is ok as it does not exist in the objectstore anyway
192
+                }
193
+            }
194
+            $this->getCache()->remove($path);
195
+            return true;
196
+        }
197
+        return false;
198
+    }
199
+
200
+    public function stat($path) {
201
+        $path = $this->normalizePath($path);
202
+        $cacheEntry = $this->getCache()->get($path);
203
+        if ($cacheEntry instanceof CacheEntry) {
204
+            return $cacheEntry->getData();
205
+        } else {
206
+            return false;
207
+        }
208
+    }
209
+
210
+    /**
211
+     * Override this method if you need a different unique resource identifier for your object storage implementation.
212
+     * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
213
+     * You may need a mapping table to store your URN if it cannot be generated from the fileid.
214
+     *
215
+     * @param int $fileId the fileid
216
+     * @return null|string the unified resource name used to identify the object
217
+     */
218
+    protected function getURN($fileId) {
219
+        if (is_numeric($fileId)) {
220
+            return $this->objectPrefix . $fileId;
221
+        }
222
+        return null;
223
+    }
224
+
225
+    public function opendir($path) {
226
+        $path = $this->normalizePath($path);
227
+
228
+        try {
229
+            $files = array();
230
+            $folderContents = $this->getCache()->getFolderContents($path);
231
+            foreach ($folderContents as $file) {
232
+                $files[] = $file['name'];
233
+            }
234
+
235
+            return IteratorDirectory::wrap($files);
236
+        } catch (\Exception $e) {
237
+            \OCP\Util::writeLog('objectstore', $e->getMessage(), \OCP\Util::ERROR);
238
+            return false;
239
+        }
240
+    }
241
+
242
+    public function filetype($path) {
243
+        $path = $this->normalizePath($path);
244
+        $stat = $this->stat($path);
245
+        if ($stat) {
246
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
247
+                return 'dir';
248
+            }
249
+            return 'file';
250
+        } else {
251
+            return false;
252
+        }
253
+    }
254
+
255
+    public function fopen($path, $mode) {
256
+        $path = $this->normalizePath($path);
257
+
258
+        switch ($mode) {
259
+            case 'r':
260
+            case 'rb':
261
+                $stat = $this->stat($path);
262
+                if (is_array($stat)) {
263
+                    try {
264
+                        return $this->objectStore->readObject($this->getURN($stat['fileid']));
265
+                    } catch (\Exception $ex) {
266
+                        \OCP\Util::writeLog('objectstore', 'Could not get object: ' . $ex->getMessage(), \OCP\Util::ERROR);
267
+                        return false;
268
+                    }
269
+                } else {
270
+                    return false;
271
+                }
272
+            case 'w':
273
+            case 'wb':
274
+            case 'a':
275
+            case 'ab':
276
+            case 'r+':
277
+            case 'w+':
278
+            case 'wb+':
279
+            case 'a+':
280
+            case 'x':
281
+            case 'x+':
282
+            case 'c':
283
+            case 'c+':
284
+                if (strrpos($path, '.') !== false) {
285
+                    $ext = substr($path, strrpos($path, '.'));
286
+                } else {
287
+                    $ext = '';
288
+                }
289
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
290
+                if ($this->file_exists($path)) {
291
+                    $source = $this->fopen($path, 'r');
292
+                    file_put_contents($tmpFile, $source);
293
+                }
294
+                $handle = fopen($tmpFile, $mode);
295
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
296
+                    $this->writeBack($tmpFile, $path);
297
+                });
298
+        }
299
+        return false;
300
+    }
301
+
302
+    public function file_exists($path) {
303
+        $path = $this->normalizePath($path);
304
+        return (bool)$this->stat($path);
305
+    }
306
+
307
+    public function rename($source, $target) {
308
+        $source = $this->normalizePath($source);
309
+        $target = $this->normalizePath($target);
310
+        $this->remove($target);
311
+        $this->getCache()->move($source, $target);
312
+        $this->touch(dirname($target));
313
+        return true;
314
+    }
315
+
316
+    public function getMimeType($path) {
317
+        $path = $this->normalizePath($path);
318
+        $stat = $this->stat($path);
319
+        if (is_array($stat)) {
320
+            return $stat['mimetype'];
321
+        } else {
322
+            return false;
323
+        }
324
+    }
325
+
326
+    public function touch($path, $mtime = null) {
327
+        if (is_null($mtime)) {
328
+            $mtime = time();
329
+        }
330
+
331
+        $path = $this->normalizePath($path);
332
+        $dirName = dirname($path);
333
+        $parentExists = $this->is_dir($dirName);
334
+        if (!$parentExists) {
335
+            return false;
336
+        }
337
+
338
+        $stat = $this->stat($path);
339
+        if (is_array($stat)) {
340
+            // update existing mtime in db
341
+            $stat['mtime'] = $mtime;
342
+            $this->getCache()->update($stat['fileid'], $stat);
343
+        } else {
344
+            $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
345
+            // create new file
346
+            $stat = array(
347
+                'etag' => $this->getETag($path),
348
+                'mimetype' => $mimeType,
349
+                'size' => 0,
350
+                'mtime' => $mtime,
351
+                'storage_mtime' => $mtime,
352
+                'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
353
+            );
354
+            $fileId = $this->getCache()->put($path, $stat);
355
+            try {
356
+                //read an empty file from memory
357
+                $this->objectStore->writeObject($this->getURN($fileId), fopen('php://memory', 'r'));
358
+            } catch (\Exception $ex) {
359
+                $this->getCache()->remove($path);
360
+                \OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR);
361
+                return false;
362
+            }
363
+        }
364
+        return true;
365
+    }
366
+
367
+    public function writeBack($tmpFile, $path) {
368
+        $stat = $this->stat($path);
369
+        if (empty($stat)) {
370
+            // create new file
371
+            $stat = array(
372
+                'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
373
+            );
374
+        }
375
+        // update stat with new data
376
+        $mTime = time();
377
+        $stat['size'] = filesize($tmpFile);
378
+        $stat['mtime'] = $mTime;
379
+        $stat['storage_mtime'] = $mTime;
380
+        $stat['mimetype'] = \OC::$server->getMimeTypeDetector()->detect($tmpFile);
381
+        $stat['etag'] = $this->getETag($path);
382
+
383
+        $fileId = $this->getCache()->put($path, $stat);
384
+        try {
385
+            //upload to object storage
386
+            $this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r'));
387
+        } catch (\Exception $ex) {
388
+            $this->getCache()->remove($path);
389
+            \OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR);
390
+            throw $ex; // make this bubble up
391
+        }
392
+    }
393
+
394
+    /**
395
+     * external changes are not supported, exclusive access to the object storage is assumed
396
+     *
397
+     * @param string $path
398
+     * @param int $time
399
+     * @return false
400
+     */
401
+    public function hasUpdated($path, $time) {
402
+        return false;
403
+    }
404 404
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -53,9 +53,9 @@  discard block
 block discarded – undo
53 53
 			throw new \Exception('missing IObjectStore instance');
54 54
 		}
55 55
 		if (isset($params['storageid'])) {
56
-			$this->id = 'object::store:' . $params['storageid'];
56
+			$this->id = 'object::store:'.$params['storageid'];
57 57
 		} else {
58
-			$this->id = 'object::store:' . $this->objectStore->getStorageId();
58
+			$this->id = 'object::store:'.$this->objectStore->getStorageId();
59 59
 		}
60 60
 		if (isset($params['objectPrefix'])) {
61 61
 			$this->objectPrefix = $params['objectPrefix'];
@@ -185,7 +185,7 @@  discard block
 block discarded – undo
185 185
 				$this->objectStore->deleteObject($this->getURN($stat['fileid']));
186 186
 			} catch (\Exception $ex) {
187 187
 				if ($ex->getCode() !== 404) {
188
-					\OCP\Util::writeLog('objectstore', 'Could not delete object: ' . $ex->getMessage(), \OCP\Util::ERROR);
188
+					\OCP\Util::writeLog('objectstore', 'Could not delete object: '.$ex->getMessage(), \OCP\Util::ERROR);
189 189
 					return false;
190 190
 				} else {
191 191
 					//removing from cache is ok as it does not exist in the objectstore anyway
@@ -217,7 +217,7 @@  discard block
 block discarded – undo
217 217
 	 */
218 218
 	protected function getURN($fileId) {
219 219
 		if (is_numeric($fileId)) {
220
-			return $this->objectPrefix . $fileId;
220
+			return $this->objectPrefix.$fileId;
221 221
 		}
222 222
 		return null;
223 223
 	}
@@ -263,7 +263,7 @@  discard block
 block discarded – undo
263 263
 					try {
264 264
 						return $this->objectStore->readObject($this->getURN($stat['fileid']));
265 265
 					} catch (\Exception $ex) {
266
-						\OCP\Util::writeLog('objectstore', 'Could not get object: ' . $ex->getMessage(), \OCP\Util::ERROR);
266
+						\OCP\Util::writeLog('objectstore', 'Could not get object: '.$ex->getMessage(), \OCP\Util::ERROR);
267 267
 						return false;
268 268
 					}
269 269
 				} else {
@@ -292,7 +292,7 @@  discard block
 block discarded – undo
292 292
 					file_put_contents($tmpFile, $source);
293 293
 				}
294 294
 				$handle = fopen($tmpFile, $mode);
295
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
295
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
296 296
 					$this->writeBack($tmpFile, $path);
297 297
 				});
298 298
 		}
@@ -301,7 +301,7 @@  discard block
 block discarded – undo
301 301
 
302 302
 	public function file_exists($path) {
303 303
 		$path = $this->normalizePath($path);
304
-		return (bool)$this->stat($path);
304
+		return (bool) $this->stat($path);
305 305
 	}
306 306
 
307 307
 	public function rename($source, $target) {
@@ -357,7 +357,7 @@  discard block
 block discarded – undo
357 357
 				$this->objectStore->writeObject($this->getURN($fileId), fopen('php://memory', 'r'));
358 358
 			} catch (\Exception $ex) {
359 359
 				$this->getCache()->remove($path);
360
-				\OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR);
360
+				\OCP\Util::writeLog('objectstore', 'Could not create object: '.$ex->getMessage(), \OCP\Util::ERROR);
361 361
 				return false;
362 362
 			}
363 363
 		}
@@ -386,7 +386,7 @@  discard block
 block discarded – undo
386 386
 			$this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r'));
387 387
 		} catch (\Exception $ex) {
388 388
 			$this->getCache()->remove($path);
389
-			\OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR);
389
+			\OCP\Util::writeLog('objectstore', 'Could not create object: '.$ex->getMessage(), \OCP\Util::ERROR);
390 390
 			throw $ex; // make this bubble up
391 391
 		}
392 392
 	}
Please login to merge, or discard this patch.
lib/private/Files/Storage/DAV.php 4 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -38,7 +38,6 @@
 block discarded – undo
38 38
 use GuzzleHttp\Message\ResponseInterface;
39 39
 use Icewind\Streams\CallbackWrapper;
40 40
 use OC\Files\Filesystem;
41
-use OC\Files\Stream\Close;
42 41
 use Icewind\Streams\IteratorDirectory;
43 42
 use OC\MemCache\ArrayCache;
44 43
 use OCP\AppFramework\Http;
Please login to merge, or discard this patch.
Indentation   +785 added lines, -785 removed lines patch added patch discarded remove patch
@@ -59,790 +59,790 @@
 block discarded – undo
59 59
  * @package OC\Files\Storage
60 60
  */
61 61
 class DAV extends Common {
62
-	/** @var string */
63
-	protected $password;
64
-	/** @var string */
65
-	protected $user;
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['secure'])) {
99
-				if (is_string($params['secure'])) {
100
-					$this->secure = ($params['secure'] === 'true');
101
-				} else {
102
-					$this->secure = (bool)$params['secure'];
103
-				}
104
-			} else {
105
-				$this->secure = false;
106
-			}
107
-			if ($this->secure === true) {
108
-				// inject mock for testing
109
-				$certManager = \OC::$server->getCertificateManager();
110
-				if (is_null($certManager)) { //no user
111
-					$certManager = \OC::$server->getCertificateManager(null);
112
-				}
113
-				$certPath = $certManager->getAbsoluteBundlePath();
114
-				if (file_exists($certPath)) {
115
-					$this->certPath = $certPath;
116
-				}
117
-			}
118
-			$this->root = isset($params['root']) ? $params['root'] : '/';
119
-			if (!$this->root || $this->root[0] != '/') {
120
-				$this->root = '/' . $this->root;
121
-			}
122
-			if (substr($this->root, -1, 1) != '/') {
123
-				$this->root .= '/';
124
-			}
125
-		} else {
126
-			throw new \Exception('Invalid webdav storage configuration');
127
-		}
128
-	}
129
-
130
-	protected function init() {
131
-		if ($this->ready) {
132
-			return;
133
-		}
134
-		$this->ready = true;
135
-
136
-		$settings = array(
137
-			'baseUri' => $this->createBaseUri(),
138
-			'userName' => $this->user,
139
-			'password' => $this->password,
140
-		);
141
-
142
-		$proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
143
-		if($proxy !== '') {
144
-			$settings['proxy'] = $proxy;
145
-		}
146
-
147
-		$this->client = new Client($settings);
148
-		$this->client->setThrowExceptions(true);
149
-		if ($this->secure === true && $this->certPath) {
150
-			$this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
151
-		}
152
-	}
153
-
154
-	/**
155
-	 * Clear the stat cache
156
-	 */
157
-	public function clearStatCache() {
158
-		$this->statCache->clear();
159
-	}
160
-
161
-	/** {@inheritdoc} */
162
-	public function getId() {
163
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
164
-	}
165
-
166
-	/** {@inheritdoc} */
167
-	public function createBaseUri() {
168
-		$baseUri = 'http';
169
-		if ($this->secure) {
170
-			$baseUri .= 's';
171
-		}
172
-		$baseUri .= '://' . $this->host . $this->root;
173
-		return $baseUri;
174
-	}
175
-
176
-	/** {@inheritdoc} */
177
-	public function mkdir($path) {
178
-		$this->init();
179
-		$path = $this->cleanPath($path);
180
-		$result = $this->simpleResponse('MKCOL', $path, null, 201);
181
-		if ($result) {
182
-			$this->statCache->set($path, true);
183
-		}
184
-		return $result;
185
-	}
186
-
187
-	/** {@inheritdoc} */
188
-	public function rmdir($path) {
189
-		$this->init();
190
-		$path = $this->cleanPath($path);
191
-		// FIXME: some WebDAV impl return 403 when trying to DELETE
192
-		// a non-empty folder
193
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
194
-		$this->statCache->clear($path . '/');
195
-		$this->statCache->remove($path);
196
-		return $result;
197
-	}
198
-
199
-	/** {@inheritdoc} */
200
-	public function opendir($path) {
201
-		$this->init();
202
-		$path = $this->cleanPath($path);
203
-		try {
204
-			$response = $this->client->propFind(
205
-				$this->encodePath($path),
206
-				['{DAV:}href'],
207
-				1
208
-			);
209
-			if ($response === false) {
210
-				return false;
211
-			}
212
-			$content = [];
213
-			$files = array_keys($response);
214
-			array_shift($files); //the first entry is the current directory
215
-
216
-			if (!$this->statCache->hasKey($path)) {
217
-				$this->statCache->set($path, true);
218
-			}
219
-			foreach ($files as $file) {
220
-				$file = urldecode($file);
221
-				// do not store the real entry, we might not have all properties
222
-				if (!$this->statCache->hasKey($path)) {
223
-					$this->statCache->set($file, true);
224
-				}
225
-				$file = basename($file);
226
-				$content[] = $file;
227
-			}
228
-			return IteratorDirectory::wrap($content);
229
-		} catch (\Exception $e) {
230
-			$this->convertException($e, $path);
231
-		}
232
-		return false;
233
-	}
234
-
235
-	/**
236
-	 * Propfind call with cache handling.
237
-	 *
238
-	 * First checks if information is cached.
239
-	 * If not, request it from the server then store to cache.
240
-	 *
241
-	 * @param string $path path to propfind
242
-	 *
243
-	 * @return array|boolean propfind response or false if the entry was not found
244
-	 *
245
-	 * @throws ClientHttpException
246
-	 */
247
-	protected function propfind($path) {
248
-		$path = $this->cleanPath($path);
249
-		$cachedResponse = $this->statCache->get($path);
250
-		// we either don't know it, or we know it exists but need more details
251
-		if (is_null($cachedResponse) || $cachedResponse === true) {
252
-			$this->init();
253
-			try {
254
-				$response = $this->client->propFind(
255
-					$this->encodePath($path),
256
-					array(
257
-						'{DAV:}getlastmodified',
258
-						'{DAV:}getcontentlength',
259
-						'{DAV:}getcontenttype',
260
-						'{http://owncloud.org/ns}permissions',
261
-						'{http://open-collaboration-services.org/ns}share-permissions',
262
-						'{DAV:}resourcetype',
263
-						'{DAV:}getetag',
264
-					)
265
-				);
266
-				$this->statCache->set($path, $response);
267
-			} catch (ClientHttpException $e) {
268
-				if ($e->getHttpStatus() === 404) {
269
-					$this->statCache->clear($path . '/');
270
-					$this->statCache->set($path, false);
271
-					return false;
272
-				}
273
-				$this->convertException($e, $path);
274
-			} catch (\Exception $e) {
275
-				$this->convertException($e, $path);
276
-			}
277
-		} else {
278
-			$response = $cachedResponse;
279
-		}
280
-		return $response;
281
-	}
282
-
283
-	/** {@inheritdoc} */
284
-	public function filetype($path) {
285
-		try {
286
-			$response = $this->propfind($path);
287
-			if ($response === false) {
288
-				return false;
289
-			}
290
-			$responseType = [];
291
-			if (isset($response["{DAV:}resourcetype"])) {
292
-				/** @var ResourceType[] $response */
293
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
294
-			}
295
-			return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
296
-		} catch (\Exception $e) {
297
-			$this->convertException($e, $path);
298
-		}
299
-		return false;
300
-	}
301
-
302
-	/** {@inheritdoc} */
303
-	public function file_exists($path) {
304
-		try {
305
-			$path = $this->cleanPath($path);
306
-			$cachedState = $this->statCache->get($path);
307
-			if ($cachedState === false) {
308
-				// we know the file doesn't exist
309
-				return false;
310
-			} else if (!is_null($cachedState)) {
311
-				return true;
312
-			}
313
-			// need to get from server
314
-			return ($this->propfind($path) !== false);
315
-		} catch (\Exception $e) {
316
-			$this->convertException($e, $path);
317
-		}
318
-		return false;
319
-	}
320
-
321
-	/** {@inheritdoc} */
322
-	public function unlink($path) {
323
-		$this->init();
324
-		$path = $this->cleanPath($path);
325
-		$result = $this->simpleResponse('DELETE', $path, null, 204);
326
-		$this->statCache->clear($path . '/');
327
-		$this->statCache->remove($path);
328
-		return $result;
329
-	}
330
-
331
-	/** {@inheritdoc} */
332
-	public function fopen($path, $mode) {
333
-		$this->init();
334
-		$path = $this->cleanPath($path);
335
-		switch ($mode) {
336
-			case 'r':
337
-			case 'rb':
338
-				try {
339
-					$response = $this->httpClientService
340
-							->newClient()
341
-							->get($this->createBaseUri() . $this->encodePath($path), [
342
-									'auth' => [$this->user, $this->password],
343
-									'stream' => true
344
-							]);
345
-				} catch (RequestException $e) {
346
-					if ($e->getResponse() instanceof ResponseInterface
347
-						&& $e->getResponse()->getStatusCode() === 404) {
348
-						return false;
349
-					} else {
350
-						throw $e;
351
-					}
352
-				}
353
-
354
-				if ($response->getStatusCode() !== Http::STATUS_OK) {
355
-					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
356
-						throw new \OCP\Lock\LockedException($path);
357
-					} else {
358
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
359
-					}
360
-				}
361
-
362
-				return $response->getBody();
363
-			case 'w':
364
-			case 'wb':
365
-			case 'a':
366
-			case 'ab':
367
-			case 'r+':
368
-			case 'w+':
369
-			case 'wb+':
370
-			case 'a+':
371
-			case 'x':
372
-			case 'x+':
373
-			case 'c':
374
-			case 'c+':
375
-				//emulate these
376
-				$tempManager = \OC::$server->getTempManager();
377
-				if (strrpos($path, '.') !== false) {
378
-					$ext = substr($path, strrpos($path, '.'));
379
-				} else {
380
-					$ext = '';
381
-				}
382
-				if ($this->file_exists($path)) {
383
-					if (!$this->isUpdatable($path)) {
384
-						return false;
385
-					}
386
-					if ($mode === 'w' or $mode === 'w+') {
387
-						$tmpFile = $tempManager->getTemporaryFile($ext);
388
-					} else {
389
-						$tmpFile = $this->getCachedFile($path);
390
-					}
391
-				} else {
392
-					if (!$this->isCreatable(dirname($path))) {
393
-						return false;
394
-					}
395
-					$tmpFile = $tempManager->getTemporaryFile($ext);
396
-				}
397
-				$handle = fopen($tmpFile, $mode);
398
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
399
-					$this->writeBack($tmpFile, $path);
400
-				});
401
-		}
402
-	}
403
-
404
-	/**
405
-	 * @param string $tmpFile
406
-	 */
407
-	public function writeBack($tmpFile, $path) {
408
-		$this->uploadFile($tmpFile, $path);
409
-		unlink($tmpFile);
410
-	}
411
-
412
-	/** {@inheritdoc} */
413
-	public function free_space($path) {
414
-		$this->init();
415
-		$path = $this->cleanPath($path);
416
-		try {
417
-			// TODO: cacheable ?
418
-			$response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
419
-			if ($response === false) {
420
-				return FileInfo::SPACE_UNKNOWN;
421
-			}
422
-			if (isset($response['{DAV:}quota-available-bytes'])) {
423
-				return (int)$response['{DAV:}quota-available-bytes'];
424
-			} else {
425
-				return FileInfo::SPACE_UNKNOWN;
426
-			}
427
-		} catch (\Exception $e) {
428
-			return FileInfo::SPACE_UNKNOWN;
429
-		}
430
-	}
431
-
432
-	/** {@inheritdoc} */
433
-	public function touch($path, $mtime = null) {
434
-		$this->init();
435
-		if (is_null($mtime)) {
436
-			$mtime = time();
437
-		}
438
-		$path = $this->cleanPath($path);
439
-
440
-		// if file exists, update the mtime, else create a new empty file
441
-		if ($this->file_exists($path)) {
442
-			try {
443
-				$this->statCache->remove($path);
444
-				$this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
445
-				// non-owncloud clients might not have accepted the property, need to recheck it
446
-				$response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
447
-				if ($response === false) {
448
-					return false;
449
-				}
450
-				if (isset($response['{DAV:}getlastmodified'])) {
451
-					$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
452
-					if ($remoteMtime !== $mtime) {
453
-						// server has not accepted the mtime
454
-						return false;
455
-					}
456
-				}
457
-			} catch (ClientHttpException $e) {
458
-				if ($e->getHttpStatus() === 501) {
459
-					return false;
460
-				}
461
-				$this->convertException($e, $path);
462
-				return false;
463
-			} catch (\Exception $e) {
464
-				$this->convertException($e, $path);
465
-				return false;
466
-			}
467
-		} else {
468
-			$this->file_put_contents($path, '');
469
-		}
470
-		return true;
471
-	}
472
-
473
-	/**
474
-	 * @param string $path
475
-	 * @param string $data
476
-	 * @return int
477
-	 */
478
-	public function file_put_contents($path, $data) {
479
-		$path = $this->cleanPath($path);
480
-		$result = parent::file_put_contents($path, $data);
481
-		$this->statCache->remove($path);
482
-		return $result;
483
-	}
484
-
485
-	/**
486
-	 * @param string $path
487
-	 * @param string $target
488
-	 */
489
-	protected function uploadFile($path, $target) {
490
-		$this->init();
491
-
492
-		// invalidate
493
-		$target = $this->cleanPath($target);
494
-		$this->statCache->remove($target);
495
-		$source = fopen($path, 'r');
496
-
497
-		$this->httpClientService
498
-			->newClient()
499
-			->put($this->createBaseUri() . $this->encodePath($target), [
500
-				'body' => $source,
501
-				'auth' => [$this->user, $this->password]
502
-			]);
503
-
504
-		$this->removeCachedFile($target);
505
-	}
506
-
507
-	/** {@inheritdoc} */
508
-	public function rename($path1, $path2) {
509
-		$this->init();
510
-		$path1 = $this->cleanPath($path1);
511
-		$path2 = $this->cleanPath($path2);
512
-		try {
513
-			// overwrite directory ?
514
-			if ($this->is_dir($path2)) {
515
-				// needs trailing slash in destination
516
-				$path2 = rtrim($path2, '/') . '/';
517
-			}
518
-			$this->client->request(
519
-				'MOVE',
520
-				$this->encodePath($path1),
521
-				null,
522
-				[
523
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
524
-				]
525
-			);
526
-			$this->statCache->clear($path1 . '/');
527
-			$this->statCache->clear($path2 . '/');
528
-			$this->statCache->set($path1, false);
529
-			$this->statCache->set($path2, true);
530
-			$this->removeCachedFile($path1);
531
-			$this->removeCachedFile($path2);
532
-			return true;
533
-		} catch (\Exception $e) {
534
-			$this->convertException($e);
535
-		}
536
-		return false;
537
-	}
538
-
539
-	/** {@inheritdoc} */
540
-	public function copy($path1, $path2) {
541
-		$this->init();
542
-		$path1 = $this->cleanPath($path1);
543
-		$path2 = $this->cleanPath($path2);
544
-		try {
545
-			// overwrite directory ?
546
-			if ($this->is_dir($path2)) {
547
-				// needs trailing slash in destination
548
-				$path2 = rtrim($path2, '/') . '/';
549
-			}
550
-			$this->client->request(
551
-				'COPY',
552
-				$this->encodePath($path1),
553
-				null,
554
-				[
555
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
556
-				]
557
-			);
558
-			$this->statCache->clear($path2 . '/');
559
-			$this->statCache->set($path2, true);
560
-			$this->removeCachedFile($path2);
561
-			return true;
562
-		} catch (\Exception $e) {
563
-			$this->convertException($e);
564
-		}
565
-		return false;
566
-	}
567
-
568
-	/** {@inheritdoc} */
569
-	public function stat($path) {
570
-		try {
571
-			$response = $this->propfind($path);
572
-			if (!$response) {
573
-				return false;
574
-			}
575
-			return [
576
-				'mtime' => strtotime($response['{DAV:}getlastmodified']),
577
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
578
-			];
579
-		} catch (\Exception $e) {
580
-			$this->convertException($e, $path);
581
-		}
582
-		return array();
583
-	}
584
-
585
-	/** {@inheritdoc} */
586
-	public function getMimeType($path) {
587
-		try {
588
-			$response = $this->propfind($path);
589
-			if ($response === false) {
590
-				return false;
591
-			}
592
-			$responseType = [];
593
-			if (isset($response["{DAV:}resourcetype"])) {
594
-				/** @var ResourceType[] $response */
595
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
596
-			}
597
-			$type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
598
-			if ($type == 'dir') {
599
-				return 'httpd/unix-directory';
600
-			} elseif (isset($response['{DAV:}getcontenttype'])) {
601
-				return $response['{DAV:}getcontenttype'];
602
-			} else {
603
-				return false;
604
-			}
605
-		} catch (\Exception $e) {
606
-			$this->convertException($e, $path);
607
-		}
608
-		return false;
609
-	}
610
-
611
-	/**
612
-	 * @param string $path
613
-	 * @return string
614
-	 */
615
-	public function cleanPath($path) {
616
-		if ($path === '') {
617
-			return $path;
618
-		}
619
-		$path = Filesystem::normalizePath($path);
620
-		// remove leading slash
621
-		return substr($path, 1);
622
-	}
623
-
624
-	/**
625
-	 * URL encodes the given path but keeps the slashes
626
-	 *
627
-	 * @param string $path to encode
628
-	 * @return string encoded path
629
-	 */
630
-	protected function encodePath($path) {
631
-		// slashes need to stay
632
-		return str_replace('%2F', '/', rawurlencode($path));
633
-	}
634
-
635
-	/**
636
-	 * @param string $method
637
-	 * @param string $path
638
-	 * @param string|resource|null $body
639
-	 * @param int $expected
640
-	 * @return bool
641
-	 * @throws StorageInvalidException
642
-	 * @throws StorageNotAvailableException
643
-	 */
644
-	protected function simpleResponse($method, $path, $body, $expected) {
645
-		$path = $this->cleanPath($path);
646
-		try {
647
-			$response = $this->client->request($method, $this->encodePath($path), $body);
648
-			return $response['statusCode'] == $expected;
649
-		} catch (ClientHttpException $e) {
650
-			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
651
-				$this->statCache->clear($path . '/');
652
-				$this->statCache->set($path, false);
653
-				return false;
654
-			}
655
-
656
-			$this->convertException($e, $path);
657
-		} catch (\Exception $e) {
658
-			$this->convertException($e, $path);
659
-		}
660
-		return false;
661
-	}
662
-
663
-	/**
664
-	 * check if curl is installed
665
-	 */
666
-	public static function checkDependencies() {
667
-		return true;
668
-	}
669
-
670
-	/** {@inheritdoc} */
671
-	public function isUpdatable($path) {
672
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
673
-	}
674
-
675
-	/** {@inheritdoc} */
676
-	public function isCreatable($path) {
677
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
678
-	}
679
-
680
-	/** {@inheritdoc} */
681
-	public function isSharable($path) {
682
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
683
-	}
684
-
685
-	/** {@inheritdoc} */
686
-	public function isDeletable($path) {
687
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
688
-	}
689
-
690
-	/** {@inheritdoc} */
691
-	public function getPermissions($path) {
692
-		$this->init();
693
-		$path = $this->cleanPath($path);
694
-		$response = $this->propfind($path);
695
-		if ($response === false) {
696
-			return 0;
697
-		}
698
-		if (isset($response['{http://owncloud.org/ns}permissions'])) {
699
-			return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
700
-		} else if ($this->is_dir($path)) {
701
-			return Constants::PERMISSION_ALL;
702
-		} else if ($this->file_exists($path)) {
703
-			return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
704
-		} else {
705
-			return 0;
706
-		}
707
-	}
708
-
709
-	/** {@inheritdoc} */
710
-	public function getETag($path) {
711
-		$this->init();
712
-		$path = $this->cleanPath($path);
713
-		$response = $this->propfind($path);
714
-		if ($response === false) {
715
-			return null;
716
-		}
717
-		if (isset($response['{DAV:}getetag'])) {
718
-			return trim($response['{DAV:}getetag'], '"');
719
-		}
720
-		return parent::getEtag($path);
721
-	}
722
-
723
-	/**
724
-	 * @param string $permissionsString
725
-	 * @return int
726
-	 */
727
-	protected function parsePermissions($permissionsString) {
728
-		$permissions = Constants::PERMISSION_READ;
729
-		if (strpos($permissionsString, 'R') !== false) {
730
-			$permissions |= Constants::PERMISSION_SHARE;
731
-		}
732
-		if (strpos($permissionsString, 'D') !== false) {
733
-			$permissions |= Constants::PERMISSION_DELETE;
734
-		}
735
-		if (strpos($permissionsString, 'W') !== false) {
736
-			$permissions |= Constants::PERMISSION_UPDATE;
737
-		}
738
-		if (strpos($permissionsString, 'CK') !== false) {
739
-			$permissions |= Constants::PERMISSION_CREATE;
740
-			$permissions |= Constants::PERMISSION_UPDATE;
741
-		}
742
-		return $permissions;
743
-	}
744
-
745
-	/**
746
-	 * check if a file or folder has been updated since $time
747
-	 *
748
-	 * @param string $path
749
-	 * @param int $time
750
-	 * @throws \OCP\Files\StorageNotAvailableException
751
-	 * @return bool
752
-	 */
753
-	public function hasUpdated($path, $time) {
754
-		$this->init();
755
-		$path = $this->cleanPath($path);
756
-		try {
757
-			// force refresh for $path
758
-			$this->statCache->remove($path);
759
-			$response = $this->propfind($path);
760
-			if ($response === false) {
761
-				if ($path === '') {
762
-					// if root is gone it means the storage is not available
763
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
764
-				}
765
-				return false;
766
-			}
767
-			if (isset($response['{DAV:}getetag'])) {
768
-				$cachedData = $this->getCache()->get($path);
769
-				$etag = null;
770
-				if (isset($response['{DAV:}getetag'])) {
771
-					$etag = trim($response['{DAV:}getetag'], '"');
772
-				}
773
-				if (!empty($etag) && $cachedData['etag'] !== $etag) {
774
-					return true;
775
-				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
776
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
777
-					return $sharePermissions !== $cachedData['permissions'];
778
-				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
779
-					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
780
-					return $permissions !== $cachedData['permissions'];
781
-				} else {
782
-					return false;
783
-				}
784
-			} else {
785
-				$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
786
-				return $remoteMtime > $time;
787
-			}
788
-		} catch (ClientHttpException $e) {
789
-			if ($e->getHttpStatus() === 405) {
790
-				if ($path === '') {
791
-					// if root is gone it means the storage is not available
792
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
793
-				}
794
-				return false;
795
-			}
796
-			$this->convertException($e, $path);
797
-			return false;
798
-		} catch (\Exception $e) {
799
-			$this->convertException($e, $path);
800
-			return false;
801
-		}
802
-	}
803
-
804
-	/**
805
-	 * Interpret the given exception and decide whether it is due to an
806
-	 * unavailable storage, invalid storage or other.
807
-	 * This will either throw StorageInvalidException, StorageNotAvailableException
808
-	 * or do nothing.
809
-	 *
810
-	 * @param Exception $e sabre exception
811
-	 * @param string $path optional path from the operation
812
-	 *
813
-	 * @throws StorageInvalidException if the storage is invalid, for example
814
-	 * when the authentication expired or is invalid
815
-	 * @throws StorageNotAvailableException if the storage is not available,
816
-	 * which might be temporary
817
-	 */
818
-	protected function convertException(Exception $e, $path = '') {
819
-		\OC::$server->getLogger()->logException($e);
820
-		Util::writeLog('files_external', $e->getMessage(), Util::ERROR);
821
-		if ($e instanceof ClientHttpException) {
822
-			if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
823
-				throw new \OCP\Lock\LockedException($path);
824
-			}
825
-			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
826
-				// either password was changed or was invalid all along
827
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
828
-			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
829
-				// ignore exception for MethodNotAllowed, false will be returned
830
-				return;
831
-			}
832
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
833
-		} else if ($e instanceof ClientException) {
834
-			// connection timeout or refused, server could be temporarily down
835
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
836
-		} else if ($e instanceof \InvalidArgumentException) {
837
-			// parse error because the server returned HTML instead of XML,
838
-			// possibly temporarily down
839
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
840
-		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
841
-			// rethrow
842
-			throw $e;
843
-		}
844
-
845
-		// TODO: only log for now, but in the future need to wrap/rethrow exception
846
-	}
62
+    /** @var string */
63
+    protected $password;
64
+    /** @var string */
65
+    protected $user;
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['secure'])) {
99
+                if (is_string($params['secure'])) {
100
+                    $this->secure = ($params['secure'] === 'true');
101
+                } else {
102
+                    $this->secure = (bool)$params['secure'];
103
+                }
104
+            } else {
105
+                $this->secure = false;
106
+            }
107
+            if ($this->secure === true) {
108
+                // inject mock for testing
109
+                $certManager = \OC::$server->getCertificateManager();
110
+                if (is_null($certManager)) { //no user
111
+                    $certManager = \OC::$server->getCertificateManager(null);
112
+                }
113
+                $certPath = $certManager->getAbsoluteBundlePath();
114
+                if (file_exists($certPath)) {
115
+                    $this->certPath = $certPath;
116
+                }
117
+            }
118
+            $this->root = isset($params['root']) ? $params['root'] : '/';
119
+            if (!$this->root || $this->root[0] != '/') {
120
+                $this->root = '/' . $this->root;
121
+            }
122
+            if (substr($this->root, -1, 1) != '/') {
123
+                $this->root .= '/';
124
+            }
125
+        } else {
126
+            throw new \Exception('Invalid webdav storage configuration');
127
+        }
128
+    }
129
+
130
+    protected function init() {
131
+        if ($this->ready) {
132
+            return;
133
+        }
134
+        $this->ready = true;
135
+
136
+        $settings = array(
137
+            'baseUri' => $this->createBaseUri(),
138
+            'userName' => $this->user,
139
+            'password' => $this->password,
140
+        );
141
+
142
+        $proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
143
+        if($proxy !== '') {
144
+            $settings['proxy'] = $proxy;
145
+        }
146
+
147
+        $this->client = new Client($settings);
148
+        $this->client->setThrowExceptions(true);
149
+        if ($this->secure === true && $this->certPath) {
150
+            $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
151
+        }
152
+    }
153
+
154
+    /**
155
+     * Clear the stat cache
156
+     */
157
+    public function clearStatCache() {
158
+        $this->statCache->clear();
159
+    }
160
+
161
+    /** {@inheritdoc} */
162
+    public function getId() {
163
+        return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
164
+    }
165
+
166
+    /** {@inheritdoc} */
167
+    public function createBaseUri() {
168
+        $baseUri = 'http';
169
+        if ($this->secure) {
170
+            $baseUri .= 's';
171
+        }
172
+        $baseUri .= '://' . $this->host . $this->root;
173
+        return $baseUri;
174
+    }
175
+
176
+    /** {@inheritdoc} */
177
+    public function mkdir($path) {
178
+        $this->init();
179
+        $path = $this->cleanPath($path);
180
+        $result = $this->simpleResponse('MKCOL', $path, null, 201);
181
+        if ($result) {
182
+            $this->statCache->set($path, true);
183
+        }
184
+        return $result;
185
+    }
186
+
187
+    /** {@inheritdoc} */
188
+    public function rmdir($path) {
189
+        $this->init();
190
+        $path = $this->cleanPath($path);
191
+        // FIXME: some WebDAV impl return 403 when trying to DELETE
192
+        // a non-empty folder
193
+        $result = $this->simpleResponse('DELETE', $path . '/', null, 204);
194
+        $this->statCache->clear($path . '/');
195
+        $this->statCache->remove($path);
196
+        return $result;
197
+    }
198
+
199
+    /** {@inheritdoc} */
200
+    public function opendir($path) {
201
+        $this->init();
202
+        $path = $this->cleanPath($path);
203
+        try {
204
+            $response = $this->client->propFind(
205
+                $this->encodePath($path),
206
+                ['{DAV:}href'],
207
+                1
208
+            );
209
+            if ($response === false) {
210
+                return false;
211
+            }
212
+            $content = [];
213
+            $files = array_keys($response);
214
+            array_shift($files); //the first entry is the current directory
215
+
216
+            if (!$this->statCache->hasKey($path)) {
217
+                $this->statCache->set($path, true);
218
+            }
219
+            foreach ($files as $file) {
220
+                $file = urldecode($file);
221
+                // do not store the real entry, we might not have all properties
222
+                if (!$this->statCache->hasKey($path)) {
223
+                    $this->statCache->set($file, true);
224
+                }
225
+                $file = basename($file);
226
+                $content[] = $file;
227
+            }
228
+            return IteratorDirectory::wrap($content);
229
+        } catch (\Exception $e) {
230
+            $this->convertException($e, $path);
231
+        }
232
+        return false;
233
+    }
234
+
235
+    /**
236
+     * Propfind call with cache handling.
237
+     *
238
+     * First checks if information is cached.
239
+     * If not, request it from the server then store to cache.
240
+     *
241
+     * @param string $path path to propfind
242
+     *
243
+     * @return array|boolean propfind response or false if the entry was not found
244
+     *
245
+     * @throws ClientHttpException
246
+     */
247
+    protected function propfind($path) {
248
+        $path = $this->cleanPath($path);
249
+        $cachedResponse = $this->statCache->get($path);
250
+        // we either don't know it, or we know it exists but need more details
251
+        if (is_null($cachedResponse) || $cachedResponse === true) {
252
+            $this->init();
253
+            try {
254
+                $response = $this->client->propFind(
255
+                    $this->encodePath($path),
256
+                    array(
257
+                        '{DAV:}getlastmodified',
258
+                        '{DAV:}getcontentlength',
259
+                        '{DAV:}getcontenttype',
260
+                        '{http://owncloud.org/ns}permissions',
261
+                        '{http://open-collaboration-services.org/ns}share-permissions',
262
+                        '{DAV:}resourcetype',
263
+                        '{DAV:}getetag',
264
+                    )
265
+                );
266
+                $this->statCache->set($path, $response);
267
+            } catch (ClientHttpException $e) {
268
+                if ($e->getHttpStatus() === 404) {
269
+                    $this->statCache->clear($path . '/');
270
+                    $this->statCache->set($path, false);
271
+                    return false;
272
+                }
273
+                $this->convertException($e, $path);
274
+            } catch (\Exception $e) {
275
+                $this->convertException($e, $path);
276
+            }
277
+        } else {
278
+            $response = $cachedResponse;
279
+        }
280
+        return $response;
281
+    }
282
+
283
+    /** {@inheritdoc} */
284
+    public function filetype($path) {
285
+        try {
286
+            $response = $this->propfind($path);
287
+            if ($response === false) {
288
+                return false;
289
+            }
290
+            $responseType = [];
291
+            if (isset($response["{DAV:}resourcetype"])) {
292
+                /** @var ResourceType[] $response */
293
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
294
+            }
295
+            return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
296
+        } catch (\Exception $e) {
297
+            $this->convertException($e, $path);
298
+        }
299
+        return false;
300
+    }
301
+
302
+    /** {@inheritdoc} */
303
+    public function file_exists($path) {
304
+        try {
305
+            $path = $this->cleanPath($path);
306
+            $cachedState = $this->statCache->get($path);
307
+            if ($cachedState === false) {
308
+                // we know the file doesn't exist
309
+                return false;
310
+            } else if (!is_null($cachedState)) {
311
+                return true;
312
+            }
313
+            // need to get from server
314
+            return ($this->propfind($path) !== false);
315
+        } catch (\Exception $e) {
316
+            $this->convertException($e, $path);
317
+        }
318
+        return false;
319
+    }
320
+
321
+    /** {@inheritdoc} */
322
+    public function unlink($path) {
323
+        $this->init();
324
+        $path = $this->cleanPath($path);
325
+        $result = $this->simpleResponse('DELETE', $path, null, 204);
326
+        $this->statCache->clear($path . '/');
327
+        $this->statCache->remove($path);
328
+        return $result;
329
+    }
330
+
331
+    /** {@inheritdoc} */
332
+    public function fopen($path, $mode) {
333
+        $this->init();
334
+        $path = $this->cleanPath($path);
335
+        switch ($mode) {
336
+            case 'r':
337
+            case 'rb':
338
+                try {
339
+                    $response = $this->httpClientService
340
+                            ->newClient()
341
+                            ->get($this->createBaseUri() . $this->encodePath($path), [
342
+                                    'auth' => [$this->user, $this->password],
343
+                                    'stream' => true
344
+                            ]);
345
+                } catch (RequestException $e) {
346
+                    if ($e->getResponse() instanceof ResponseInterface
347
+                        && $e->getResponse()->getStatusCode() === 404) {
348
+                        return false;
349
+                    } else {
350
+                        throw $e;
351
+                    }
352
+                }
353
+
354
+                if ($response->getStatusCode() !== Http::STATUS_OK) {
355
+                    if ($response->getStatusCode() === Http::STATUS_LOCKED) {
356
+                        throw new \OCP\Lock\LockedException($path);
357
+                    } else {
358
+                        Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
359
+                    }
360
+                }
361
+
362
+                return $response->getBody();
363
+            case 'w':
364
+            case 'wb':
365
+            case 'a':
366
+            case 'ab':
367
+            case 'r+':
368
+            case 'w+':
369
+            case 'wb+':
370
+            case 'a+':
371
+            case 'x':
372
+            case 'x+':
373
+            case 'c':
374
+            case 'c+':
375
+                //emulate these
376
+                $tempManager = \OC::$server->getTempManager();
377
+                if (strrpos($path, '.') !== false) {
378
+                    $ext = substr($path, strrpos($path, '.'));
379
+                } else {
380
+                    $ext = '';
381
+                }
382
+                if ($this->file_exists($path)) {
383
+                    if (!$this->isUpdatable($path)) {
384
+                        return false;
385
+                    }
386
+                    if ($mode === 'w' or $mode === 'w+') {
387
+                        $tmpFile = $tempManager->getTemporaryFile($ext);
388
+                    } else {
389
+                        $tmpFile = $this->getCachedFile($path);
390
+                    }
391
+                } else {
392
+                    if (!$this->isCreatable(dirname($path))) {
393
+                        return false;
394
+                    }
395
+                    $tmpFile = $tempManager->getTemporaryFile($ext);
396
+                }
397
+                $handle = fopen($tmpFile, $mode);
398
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
399
+                    $this->writeBack($tmpFile, $path);
400
+                });
401
+        }
402
+    }
403
+
404
+    /**
405
+     * @param string $tmpFile
406
+     */
407
+    public function writeBack($tmpFile, $path) {
408
+        $this->uploadFile($tmpFile, $path);
409
+        unlink($tmpFile);
410
+    }
411
+
412
+    /** {@inheritdoc} */
413
+    public function free_space($path) {
414
+        $this->init();
415
+        $path = $this->cleanPath($path);
416
+        try {
417
+            // TODO: cacheable ?
418
+            $response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
419
+            if ($response === false) {
420
+                return FileInfo::SPACE_UNKNOWN;
421
+            }
422
+            if (isset($response['{DAV:}quota-available-bytes'])) {
423
+                return (int)$response['{DAV:}quota-available-bytes'];
424
+            } else {
425
+                return FileInfo::SPACE_UNKNOWN;
426
+            }
427
+        } catch (\Exception $e) {
428
+            return FileInfo::SPACE_UNKNOWN;
429
+        }
430
+    }
431
+
432
+    /** {@inheritdoc} */
433
+    public function touch($path, $mtime = null) {
434
+        $this->init();
435
+        if (is_null($mtime)) {
436
+            $mtime = time();
437
+        }
438
+        $path = $this->cleanPath($path);
439
+
440
+        // if file exists, update the mtime, else create a new empty file
441
+        if ($this->file_exists($path)) {
442
+            try {
443
+                $this->statCache->remove($path);
444
+                $this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
445
+                // non-owncloud clients might not have accepted the property, need to recheck it
446
+                $response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
447
+                if ($response === false) {
448
+                    return false;
449
+                }
450
+                if (isset($response['{DAV:}getlastmodified'])) {
451
+                    $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
452
+                    if ($remoteMtime !== $mtime) {
453
+                        // server has not accepted the mtime
454
+                        return false;
455
+                    }
456
+                }
457
+            } catch (ClientHttpException $e) {
458
+                if ($e->getHttpStatus() === 501) {
459
+                    return false;
460
+                }
461
+                $this->convertException($e, $path);
462
+                return false;
463
+            } catch (\Exception $e) {
464
+                $this->convertException($e, $path);
465
+                return false;
466
+            }
467
+        } else {
468
+            $this->file_put_contents($path, '');
469
+        }
470
+        return true;
471
+    }
472
+
473
+    /**
474
+     * @param string $path
475
+     * @param string $data
476
+     * @return int
477
+     */
478
+    public function file_put_contents($path, $data) {
479
+        $path = $this->cleanPath($path);
480
+        $result = parent::file_put_contents($path, $data);
481
+        $this->statCache->remove($path);
482
+        return $result;
483
+    }
484
+
485
+    /**
486
+     * @param string $path
487
+     * @param string $target
488
+     */
489
+    protected function uploadFile($path, $target) {
490
+        $this->init();
491
+
492
+        // invalidate
493
+        $target = $this->cleanPath($target);
494
+        $this->statCache->remove($target);
495
+        $source = fopen($path, 'r');
496
+
497
+        $this->httpClientService
498
+            ->newClient()
499
+            ->put($this->createBaseUri() . $this->encodePath($target), [
500
+                'body' => $source,
501
+                'auth' => [$this->user, $this->password]
502
+            ]);
503
+
504
+        $this->removeCachedFile($target);
505
+    }
506
+
507
+    /** {@inheritdoc} */
508
+    public function rename($path1, $path2) {
509
+        $this->init();
510
+        $path1 = $this->cleanPath($path1);
511
+        $path2 = $this->cleanPath($path2);
512
+        try {
513
+            // overwrite directory ?
514
+            if ($this->is_dir($path2)) {
515
+                // needs trailing slash in destination
516
+                $path2 = rtrim($path2, '/') . '/';
517
+            }
518
+            $this->client->request(
519
+                'MOVE',
520
+                $this->encodePath($path1),
521
+                null,
522
+                [
523
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
524
+                ]
525
+            );
526
+            $this->statCache->clear($path1 . '/');
527
+            $this->statCache->clear($path2 . '/');
528
+            $this->statCache->set($path1, false);
529
+            $this->statCache->set($path2, true);
530
+            $this->removeCachedFile($path1);
531
+            $this->removeCachedFile($path2);
532
+            return true;
533
+        } catch (\Exception $e) {
534
+            $this->convertException($e);
535
+        }
536
+        return false;
537
+    }
538
+
539
+    /** {@inheritdoc} */
540
+    public function copy($path1, $path2) {
541
+        $this->init();
542
+        $path1 = $this->cleanPath($path1);
543
+        $path2 = $this->cleanPath($path2);
544
+        try {
545
+            // overwrite directory ?
546
+            if ($this->is_dir($path2)) {
547
+                // needs trailing slash in destination
548
+                $path2 = rtrim($path2, '/') . '/';
549
+            }
550
+            $this->client->request(
551
+                'COPY',
552
+                $this->encodePath($path1),
553
+                null,
554
+                [
555
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
556
+                ]
557
+            );
558
+            $this->statCache->clear($path2 . '/');
559
+            $this->statCache->set($path2, true);
560
+            $this->removeCachedFile($path2);
561
+            return true;
562
+        } catch (\Exception $e) {
563
+            $this->convertException($e);
564
+        }
565
+        return false;
566
+    }
567
+
568
+    /** {@inheritdoc} */
569
+    public function stat($path) {
570
+        try {
571
+            $response = $this->propfind($path);
572
+            if (!$response) {
573
+                return false;
574
+            }
575
+            return [
576
+                'mtime' => strtotime($response['{DAV:}getlastmodified']),
577
+                'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
578
+            ];
579
+        } catch (\Exception $e) {
580
+            $this->convertException($e, $path);
581
+        }
582
+        return array();
583
+    }
584
+
585
+    /** {@inheritdoc} */
586
+    public function getMimeType($path) {
587
+        try {
588
+            $response = $this->propfind($path);
589
+            if ($response === false) {
590
+                return false;
591
+            }
592
+            $responseType = [];
593
+            if (isset($response["{DAV:}resourcetype"])) {
594
+                /** @var ResourceType[] $response */
595
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
596
+            }
597
+            $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
598
+            if ($type == 'dir') {
599
+                return 'httpd/unix-directory';
600
+            } elseif (isset($response['{DAV:}getcontenttype'])) {
601
+                return $response['{DAV:}getcontenttype'];
602
+            } else {
603
+                return false;
604
+            }
605
+        } catch (\Exception $e) {
606
+            $this->convertException($e, $path);
607
+        }
608
+        return false;
609
+    }
610
+
611
+    /**
612
+     * @param string $path
613
+     * @return string
614
+     */
615
+    public function cleanPath($path) {
616
+        if ($path === '') {
617
+            return $path;
618
+        }
619
+        $path = Filesystem::normalizePath($path);
620
+        // remove leading slash
621
+        return substr($path, 1);
622
+    }
623
+
624
+    /**
625
+     * URL encodes the given path but keeps the slashes
626
+     *
627
+     * @param string $path to encode
628
+     * @return string encoded path
629
+     */
630
+    protected function encodePath($path) {
631
+        // slashes need to stay
632
+        return str_replace('%2F', '/', rawurlencode($path));
633
+    }
634
+
635
+    /**
636
+     * @param string $method
637
+     * @param string $path
638
+     * @param string|resource|null $body
639
+     * @param int $expected
640
+     * @return bool
641
+     * @throws StorageInvalidException
642
+     * @throws StorageNotAvailableException
643
+     */
644
+    protected function simpleResponse($method, $path, $body, $expected) {
645
+        $path = $this->cleanPath($path);
646
+        try {
647
+            $response = $this->client->request($method, $this->encodePath($path), $body);
648
+            return $response['statusCode'] == $expected;
649
+        } catch (ClientHttpException $e) {
650
+            if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
651
+                $this->statCache->clear($path . '/');
652
+                $this->statCache->set($path, false);
653
+                return false;
654
+            }
655
+
656
+            $this->convertException($e, $path);
657
+        } catch (\Exception $e) {
658
+            $this->convertException($e, $path);
659
+        }
660
+        return false;
661
+    }
662
+
663
+    /**
664
+     * check if curl is installed
665
+     */
666
+    public static function checkDependencies() {
667
+        return true;
668
+    }
669
+
670
+    /** {@inheritdoc} */
671
+    public function isUpdatable($path) {
672
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
673
+    }
674
+
675
+    /** {@inheritdoc} */
676
+    public function isCreatable($path) {
677
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
678
+    }
679
+
680
+    /** {@inheritdoc} */
681
+    public function isSharable($path) {
682
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
683
+    }
684
+
685
+    /** {@inheritdoc} */
686
+    public function isDeletable($path) {
687
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
688
+    }
689
+
690
+    /** {@inheritdoc} */
691
+    public function getPermissions($path) {
692
+        $this->init();
693
+        $path = $this->cleanPath($path);
694
+        $response = $this->propfind($path);
695
+        if ($response === false) {
696
+            return 0;
697
+        }
698
+        if (isset($response['{http://owncloud.org/ns}permissions'])) {
699
+            return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
700
+        } else if ($this->is_dir($path)) {
701
+            return Constants::PERMISSION_ALL;
702
+        } else if ($this->file_exists($path)) {
703
+            return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
704
+        } else {
705
+            return 0;
706
+        }
707
+    }
708
+
709
+    /** {@inheritdoc} */
710
+    public function getETag($path) {
711
+        $this->init();
712
+        $path = $this->cleanPath($path);
713
+        $response = $this->propfind($path);
714
+        if ($response === false) {
715
+            return null;
716
+        }
717
+        if (isset($response['{DAV:}getetag'])) {
718
+            return trim($response['{DAV:}getetag'], '"');
719
+        }
720
+        return parent::getEtag($path);
721
+    }
722
+
723
+    /**
724
+     * @param string $permissionsString
725
+     * @return int
726
+     */
727
+    protected function parsePermissions($permissionsString) {
728
+        $permissions = Constants::PERMISSION_READ;
729
+        if (strpos($permissionsString, 'R') !== false) {
730
+            $permissions |= Constants::PERMISSION_SHARE;
731
+        }
732
+        if (strpos($permissionsString, 'D') !== false) {
733
+            $permissions |= Constants::PERMISSION_DELETE;
734
+        }
735
+        if (strpos($permissionsString, 'W') !== false) {
736
+            $permissions |= Constants::PERMISSION_UPDATE;
737
+        }
738
+        if (strpos($permissionsString, 'CK') !== false) {
739
+            $permissions |= Constants::PERMISSION_CREATE;
740
+            $permissions |= Constants::PERMISSION_UPDATE;
741
+        }
742
+        return $permissions;
743
+    }
744
+
745
+    /**
746
+     * check if a file or folder has been updated since $time
747
+     *
748
+     * @param string $path
749
+     * @param int $time
750
+     * @throws \OCP\Files\StorageNotAvailableException
751
+     * @return bool
752
+     */
753
+    public function hasUpdated($path, $time) {
754
+        $this->init();
755
+        $path = $this->cleanPath($path);
756
+        try {
757
+            // force refresh for $path
758
+            $this->statCache->remove($path);
759
+            $response = $this->propfind($path);
760
+            if ($response === false) {
761
+                if ($path === '') {
762
+                    // if root is gone it means the storage is not available
763
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
764
+                }
765
+                return false;
766
+            }
767
+            if (isset($response['{DAV:}getetag'])) {
768
+                $cachedData = $this->getCache()->get($path);
769
+                $etag = null;
770
+                if (isset($response['{DAV:}getetag'])) {
771
+                    $etag = trim($response['{DAV:}getetag'], '"');
772
+                }
773
+                if (!empty($etag) && $cachedData['etag'] !== $etag) {
774
+                    return true;
775
+                } else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
776
+                    $sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
777
+                    return $sharePermissions !== $cachedData['permissions'];
778
+                } else if (isset($response['{http://owncloud.org/ns}permissions'])) {
779
+                    $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
780
+                    return $permissions !== $cachedData['permissions'];
781
+                } else {
782
+                    return false;
783
+                }
784
+            } else {
785
+                $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
786
+                return $remoteMtime > $time;
787
+            }
788
+        } catch (ClientHttpException $e) {
789
+            if ($e->getHttpStatus() === 405) {
790
+                if ($path === '') {
791
+                    // if root is gone it means the storage is not available
792
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
793
+                }
794
+                return false;
795
+            }
796
+            $this->convertException($e, $path);
797
+            return false;
798
+        } catch (\Exception $e) {
799
+            $this->convertException($e, $path);
800
+            return false;
801
+        }
802
+    }
803
+
804
+    /**
805
+     * Interpret the given exception and decide whether it is due to an
806
+     * unavailable storage, invalid storage or other.
807
+     * This will either throw StorageInvalidException, StorageNotAvailableException
808
+     * or do nothing.
809
+     *
810
+     * @param Exception $e sabre exception
811
+     * @param string $path optional path from the operation
812
+     *
813
+     * @throws StorageInvalidException if the storage is invalid, for example
814
+     * when the authentication expired or is invalid
815
+     * @throws StorageNotAvailableException if the storage is not available,
816
+     * which might be temporary
817
+     */
818
+    protected function convertException(Exception $e, $path = '') {
819
+        \OC::$server->getLogger()->logException($e);
820
+        Util::writeLog('files_external', $e->getMessage(), Util::ERROR);
821
+        if ($e instanceof ClientHttpException) {
822
+            if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
823
+                throw new \OCP\Lock\LockedException($path);
824
+            }
825
+            if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
826
+                // either password was changed or was invalid all along
827
+                throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
828
+            } else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
829
+                // ignore exception for MethodNotAllowed, false will be returned
830
+                return;
831
+            }
832
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
833
+        } else if ($e instanceof ClientException) {
834
+            // connection timeout or refused, server could be temporarily down
835
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
836
+        } else if ($e instanceof \InvalidArgumentException) {
837
+            // parse error because the server returned HTML instead of XML,
838
+            // possibly temporarily down
839
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
840
+        } else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
841
+            // rethrow
842
+            throw $e;
843
+        }
844
+
845
+        // TODO: only log for now, but in the future need to wrap/rethrow exception
846
+    }
847 847
 }
848 848
 
Please login to merge, or discard this patch.
Spacing   +34 added lines, -34 removed lines patch added patch discarded remove patch
@@ -99,7 +99,7 @@  discard block
 block discarded – undo
99 99
 				if (is_string($params['secure'])) {
100 100
 					$this->secure = ($params['secure'] === 'true');
101 101
 				} else {
102
-					$this->secure = (bool)$params['secure'];
102
+					$this->secure = (bool) $params['secure'];
103 103
 				}
104 104
 			} else {
105 105
 				$this->secure = false;
@@ -117,7 +117,7 @@  discard block
 block discarded – undo
117 117
 			}
118 118
 			$this->root = isset($params['root']) ? $params['root'] : '/';
119 119
 			if (!$this->root || $this->root[0] != '/') {
120
-				$this->root = '/' . $this->root;
120
+				$this->root = '/'.$this->root;
121 121
 			}
122 122
 			if (substr($this->root, -1, 1) != '/') {
123 123
 				$this->root .= '/';
@@ -140,7 +140,7 @@  discard block
 block discarded – undo
140 140
 		);
141 141
 
142 142
 		$proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
143
-		if($proxy !== '') {
143
+		if ($proxy !== '') {
144 144
 			$settings['proxy'] = $proxy;
145 145
 		}
146 146
 
@@ -160,7 +160,7 @@  discard block
 block discarded – undo
160 160
 
161 161
 	/** {@inheritdoc} */
162 162
 	public function getId() {
163
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
163
+		return 'webdav::'.$this->user.'@'.$this->host.'/'.$this->root;
164 164
 	}
165 165
 
166 166
 	/** {@inheritdoc} */
@@ -169,7 +169,7 @@  discard block
 block discarded – undo
169 169
 		if ($this->secure) {
170 170
 			$baseUri .= 's';
171 171
 		}
172
-		$baseUri .= '://' . $this->host . $this->root;
172
+		$baseUri .= '://'.$this->host.$this->root;
173 173
 		return $baseUri;
174 174
 	}
175 175
 
@@ -190,8 +190,8 @@  discard block
 block discarded – undo
190 190
 		$path = $this->cleanPath($path);
191 191
 		// FIXME: some WebDAV impl return 403 when trying to DELETE
192 192
 		// a non-empty folder
193
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
194
-		$this->statCache->clear($path . '/');
193
+		$result = $this->simpleResponse('DELETE', $path.'/', null, 204);
194
+		$this->statCache->clear($path.'/');
195 195
 		$this->statCache->remove($path);
196 196
 		return $result;
197 197
 	}
@@ -266,7 +266,7 @@  discard block
 block discarded – undo
266 266
 				$this->statCache->set($path, $response);
267 267
 			} catch (ClientHttpException $e) {
268 268
 				if ($e->getHttpStatus() === 404) {
269
-					$this->statCache->clear($path . '/');
269
+					$this->statCache->clear($path.'/');
270 270
 					$this->statCache->set($path, false);
271 271
 					return false;
272 272
 				}
@@ -323,7 +323,7 @@  discard block
 block discarded – undo
323 323
 		$this->init();
324 324
 		$path = $this->cleanPath($path);
325 325
 		$result = $this->simpleResponse('DELETE', $path, null, 204);
326
-		$this->statCache->clear($path . '/');
326
+		$this->statCache->clear($path.'/');
327 327
 		$this->statCache->remove($path);
328 328
 		return $result;
329 329
 	}
@@ -338,7 +338,7 @@  discard block
 block discarded – undo
338 338
 				try {
339 339
 					$response = $this->httpClientService
340 340
 							->newClient()
341
-							->get($this->createBaseUri() . $this->encodePath($path), [
341
+							->get($this->createBaseUri().$this->encodePath($path), [
342 342
 									'auth' => [$this->user, $this->password],
343 343
 									'stream' => true
344 344
 							]);
@@ -355,7 +355,7 @@  discard block
 block discarded – undo
355 355
 					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
356 356
 						throw new \OCP\Lock\LockedException($path);
357 357
 					} else {
358
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
358
+						Util::writeLog("webdav client", 'Guzzle get returned status code '.$response->getStatusCode(), Util::ERROR);
359 359
 					}
360 360
 				}
361 361
 
@@ -395,7 +395,7 @@  discard block
 block discarded – undo
395 395
 					$tmpFile = $tempManager->getTemporaryFile($ext);
396 396
 				}
397 397
 				$handle = fopen($tmpFile, $mode);
398
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
398
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
399 399
 					$this->writeBack($tmpFile, $path);
400 400
 				});
401 401
 		}
@@ -420,7 +420,7 @@  discard block
 block discarded – undo
420 420
 				return FileInfo::SPACE_UNKNOWN;
421 421
 			}
422 422
 			if (isset($response['{DAV:}quota-available-bytes'])) {
423
-				return (int)$response['{DAV:}quota-available-bytes'];
423
+				return (int) $response['{DAV:}quota-available-bytes'];
424 424
 			} else {
425 425
 				return FileInfo::SPACE_UNKNOWN;
426 426
 			}
@@ -496,7 +496,7 @@  discard block
 block discarded – undo
496 496
 
497 497
 		$this->httpClientService
498 498
 			->newClient()
499
-			->put($this->createBaseUri() . $this->encodePath($target), [
499
+			->put($this->createBaseUri().$this->encodePath($target), [
500 500
 				'body' => $source,
501 501
 				'auth' => [$this->user, $this->password]
502 502
 			]);
@@ -513,18 +513,18 @@  discard block
 block discarded – undo
513 513
 			// overwrite directory ?
514 514
 			if ($this->is_dir($path2)) {
515 515
 				// needs trailing slash in destination
516
-				$path2 = rtrim($path2, '/') . '/';
516
+				$path2 = rtrim($path2, '/').'/';
517 517
 			}
518 518
 			$this->client->request(
519 519
 				'MOVE',
520 520
 				$this->encodePath($path1),
521 521
 				null,
522 522
 				[
523
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
523
+					'Destination' => $this->createBaseUri().$this->encodePath($path2),
524 524
 				]
525 525
 			);
526
-			$this->statCache->clear($path1 . '/');
527
-			$this->statCache->clear($path2 . '/');
526
+			$this->statCache->clear($path1.'/');
527
+			$this->statCache->clear($path2.'/');
528 528
 			$this->statCache->set($path1, false);
529 529
 			$this->statCache->set($path2, true);
530 530
 			$this->removeCachedFile($path1);
@@ -545,17 +545,17 @@  discard block
 block discarded – undo
545 545
 			// overwrite directory ?
546 546
 			if ($this->is_dir($path2)) {
547 547
 				// needs trailing slash in destination
548
-				$path2 = rtrim($path2, '/') . '/';
548
+				$path2 = rtrim($path2, '/').'/';
549 549
 			}
550 550
 			$this->client->request(
551 551
 				'COPY',
552 552
 				$this->encodePath($path1),
553 553
 				null,
554 554
 				[
555
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
555
+					'Destination' => $this->createBaseUri().$this->encodePath($path2),
556 556
 				]
557 557
 			);
558
-			$this->statCache->clear($path2 . '/');
558
+			$this->statCache->clear($path2.'/');
559 559
 			$this->statCache->set($path2, true);
560 560
 			$this->removeCachedFile($path2);
561 561
 			return true;
@@ -574,7 +574,7 @@  discard block
 block discarded – undo
574 574
 			}
575 575
 			return [
576 576
 				'mtime' => strtotime($response['{DAV:}getlastmodified']),
577
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
577
+				'size' => (int) isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
578 578
 			];
579 579
 		} catch (\Exception $e) {
580 580
 			$this->convertException($e, $path);
@@ -648,7 +648,7 @@  discard block
 block discarded – undo
648 648
 			return $response['statusCode'] == $expected;
649 649
 		} catch (ClientHttpException $e) {
650 650
 			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
651
-				$this->statCache->clear($path . '/');
651
+				$this->statCache->clear($path.'/');
652 652
 				$this->statCache->set($path, false);
653 653
 				return false;
654 654
 			}
@@ -669,22 +669,22 @@  discard block
 block discarded – undo
669 669
 
670 670
 	/** {@inheritdoc} */
671 671
 	public function isUpdatable($path) {
672
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
672
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
673 673
 	}
674 674
 
675 675
 	/** {@inheritdoc} */
676 676
 	public function isCreatable($path) {
677
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
677
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_CREATE);
678 678
 	}
679 679
 
680 680
 	/** {@inheritdoc} */
681 681
 	public function isSharable($path) {
682
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
682
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_SHARE);
683 683
 	}
684 684
 
685 685
 	/** {@inheritdoc} */
686 686
 	public function isDeletable($path) {
687
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
687
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_DELETE);
688 688
 	}
689 689
 
690 690
 	/** {@inheritdoc} */
@@ -760,7 +760,7 @@  discard block
 block discarded – undo
760 760
 			if ($response === false) {
761 761
 				if ($path === '') {
762 762
 					// if root is gone it means the storage is not available
763
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
763
+					throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
764 764
 				}
765 765
 				return false;
766 766
 			}
@@ -773,7 +773,7 @@  discard block
 block discarded – undo
773 773
 				if (!empty($etag) && $cachedData['etag'] !== $etag) {
774 774
 					return true;
775 775
 				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
776
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
776
+					$sharePermissions = (int) $response['{http://open-collaboration-services.org/ns}share-permissions'];
777 777
 					return $sharePermissions !== $cachedData['permissions'];
778 778
 				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
779 779
 					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
@@ -789,7 +789,7 @@  discard block
 block discarded – undo
789 789
 			if ($e->getHttpStatus() === 405) {
790 790
 				if ($path === '') {
791 791
 					// if root is gone it means the storage is not available
792
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
792
+					throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
793 793
 				}
794 794
 				return false;
795 795
 			}
@@ -824,19 +824,19 @@  discard block
 block discarded – undo
824 824
 			}
825 825
 			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
826 826
 				// either password was changed or was invalid all along
827
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
827
+				throw new StorageInvalidException(get_class($e).': '.$e->getMessage());
828 828
 			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
829 829
 				// ignore exception for MethodNotAllowed, false will be returned
830 830
 				return;
831 831
 			}
832
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
832
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
833 833
 		} else if ($e instanceof ClientException) {
834 834
 			// connection timeout or refused, server could be temporarily down
835
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
835
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
836 836
 		} else if ($e instanceof \InvalidArgumentException) {
837 837
 			// parse error because the server returned HTML instead of XML,
838 838
 			// possibly temporarily down
839
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
839
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
840 840
 		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
841 841
 			// rethrow
842 842
 			throw $e;
Please login to merge, or discard this patch.
Braces   +5 added lines, -2 removed lines patch added patch discarded remove patch
@@ -90,8 +90,11 @@
 block discarded – undo
90 90
 		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
91 91
 			$host = $params['host'];
92 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);
93
+			if (substr($host, 0, 8) == "https://") {
94
+			    $host = substr($host, 8);
95
+			} else if (substr($host, 0, 7) == "http://") {
96
+			    $host = substr($host, 7);
97
+			}
95 98
 			$this->host = $host;
96 99
 			$this->user = $params['user'];
97 100
 			$this->password = $params['password'];
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Notify/SMBNotifyHandler.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -102,7 +102,7 @@
 block discarded – undo
102 102
 
103 103
 	/**
104 104
 	 * @param \Icewind\SMB\Change $change
105
-	 * @return IChange|null
105
+	 * @return null|Change
106 106
 	 */
107 107
 	private function mapChange(\Icewind\SMB\Change $change) {
108 108
 		$path = $this->relativePath($change->getPath());
Please login to merge, or discard this patch.
Indentation   +109 added lines, -109 removed lines patch added patch discarded remove patch
@@ -29,122 +29,122 @@
 block discarded – undo
29 29
 use OCP\Files\Notify\INotifyHandler;
30 30
 
31 31
 class SMBNotifyHandler implements INotifyHandler {
32
-	/**
33
-	 * @var \Icewind\SMB\INotifyHandler
34
-	 */
35
-	private $shareNotifyHandler;
32
+    /**
33
+     * @var \Icewind\SMB\INotifyHandler
34
+     */
35
+    private $shareNotifyHandler;
36 36
 
37
-	/**
38
-	 * @var string
39
-	 */
40
-	private $root;
37
+    /**
38
+     * @var string
39
+     */
40
+    private $root;
41 41
 
42
-	private $oldRenamePath = null;
42
+    private $oldRenamePath = null;
43 43
 
44
-	/**
45
-	 * SMBNotifyHandler constructor.
46
-	 *
47
-	 * @param \Icewind\SMB\INotifyHandler $shareNotifyHandler
48
-	 * @param string $root
49
-	 */
50
-	public function __construct(\Icewind\SMB\INotifyHandler $shareNotifyHandler, $root) {
51
-		$this->shareNotifyHandler = $shareNotifyHandler;
52
-		$this->root = $root;
53
-	}
44
+    /**
45
+     * SMBNotifyHandler constructor.
46
+     *
47
+     * @param \Icewind\SMB\INotifyHandler $shareNotifyHandler
48
+     * @param string $root
49
+     */
50
+    public function __construct(\Icewind\SMB\INotifyHandler $shareNotifyHandler, $root) {
51
+        $this->shareNotifyHandler = $shareNotifyHandler;
52
+        $this->root = $root;
53
+    }
54 54
 
55
-	private function relativePath($fullPath) {
56
-		if ($fullPath === $this->root) {
57
-			return '';
58
-		} else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
59
-			return substr($fullPath, strlen($this->root));
60
-		} else {
61
-			return null;
62
-		}
63
-	}
55
+    private function relativePath($fullPath) {
56
+        if ($fullPath === $this->root) {
57
+            return '';
58
+        } else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
59
+            return substr($fullPath, strlen($this->root));
60
+        } else {
61
+            return null;
62
+        }
63
+    }
64 64
 
65
-	public function listen(callable $callback) {
66
-		$oldRenamePath = null;
67
-		$this->shareNotifyHandler->listen(function (\Icewind\SMB\Change $shareChange) use ($callback) {
68
-			$change = $this->mapChange($shareChange);
69
-			if (!is_null($change)) {
70
-				return $callback($change);
71
-			} else {
72
-				return true;
73
-			}
74
-		});
75
-	}
65
+    public function listen(callable $callback) {
66
+        $oldRenamePath = null;
67
+        $this->shareNotifyHandler->listen(function (\Icewind\SMB\Change $shareChange) use ($callback) {
68
+            $change = $this->mapChange($shareChange);
69
+            if (!is_null($change)) {
70
+                return $callback($change);
71
+            } else {
72
+                return true;
73
+            }
74
+        });
75
+    }
76 76
 
77
-	/**
78
-	 * Get all changes detected since the start of the notify process or the last call to getChanges
79
-	 *
80
-	 * @return IChange[]
81
-	 */
82
-	public function getChanges() {
83
-		$shareChanges = $this->shareNotifyHandler->getChanges();
84
-		$changes = [];
85
-		foreach ($shareChanges as $shareChange) {
86
-			$change = $this->mapChange($shareChange);
87
-			if ($change) {
88
-				$changes[] = $change;
89
-			}
90
-		}
91
-		return $changes;
92
-	}
77
+    /**
78
+     * Get all changes detected since the start of the notify process or the last call to getChanges
79
+     *
80
+     * @return IChange[]
81
+     */
82
+    public function getChanges() {
83
+        $shareChanges = $this->shareNotifyHandler->getChanges();
84
+        $changes = [];
85
+        foreach ($shareChanges as $shareChange) {
86
+            $change = $this->mapChange($shareChange);
87
+            if ($change) {
88
+                $changes[] = $change;
89
+            }
90
+        }
91
+        return $changes;
92
+    }
93 93
 
94
-	/**
95
-	 * Stop listening for changes
96
-	 *
97
-	 * Note that any pending changes will be discarded
98
-	 */
99
-	public function stop() {
100
-		$this->shareNotifyHandler->stop();
101
-	}
94
+    /**
95
+     * Stop listening for changes
96
+     *
97
+     * Note that any pending changes will be discarded
98
+     */
99
+    public function stop() {
100
+        $this->shareNotifyHandler->stop();
101
+    }
102 102
 
103
-	/**
104
-	 * @param \Icewind\SMB\Change $change
105
-	 * @return IChange|null
106
-	 */
107
-	private function mapChange(\Icewind\SMB\Change $change) {
108
-		$path = $this->relativePath($change->getPath());
109
-		if (is_null($path)) {
110
-			return null;
111
-		}
112
-		if ($change->getCode() === \Icewind\SMB\INotifyHandler::NOTIFY_RENAMED_OLD) {
113
-			$this->oldRenamePath = $path;
114
-			return null;
115
-		}
116
-		$type = $this->mapNotifyType($change->getCode());
117
-		if (is_null($type)) {
118
-			return null;
119
-		}
120
-		if ($type === IChange::RENAMED) {
121
-			if (!is_null($this->oldRenamePath)) {
122
-				$result = new RenameChange($type, $this->oldRenamePath, $path);
123
-				$this->oldRenamePath = null;
124
-			} else {
125
-				$result = null;
126
-			}
127
-		} else {
128
-			$result = new Change($type, $path);
129
-		}
130
-		return $result;
131
-	}
103
+    /**
104
+     * @param \Icewind\SMB\Change $change
105
+     * @return IChange|null
106
+     */
107
+    private function mapChange(\Icewind\SMB\Change $change) {
108
+        $path = $this->relativePath($change->getPath());
109
+        if (is_null($path)) {
110
+            return null;
111
+        }
112
+        if ($change->getCode() === \Icewind\SMB\INotifyHandler::NOTIFY_RENAMED_OLD) {
113
+            $this->oldRenamePath = $path;
114
+            return null;
115
+        }
116
+        $type = $this->mapNotifyType($change->getCode());
117
+        if (is_null($type)) {
118
+            return null;
119
+        }
120
+        if ($type === IChange::RENAMED) {
121
+            if (!is_null($this->oldRenamePath)) {
122
+                $result = new RenameChange($type, $this->oldRenamePath, $path);
123
+                $this->oldRenamePath = null;
124
+            } else {
125
+                $result = null;
126
+            }
127
+        } else {
128
+            $result = new Change($type, $path);
129
+        }
130
+        return $result;
131
+    }
132 132
 
133
-	private function mapNotifyType($smbType) {
134
-		switch ($smbType) {
135
-			case \Icewind\SMB\INotifyHandler::NOTIFY_ADDED:
136
-				return IChange::ADDED;
137
-			case \Icewind\SMB\INotifyHandler::NOTIFY_REMOVED:
138
-				return IChange::REMOVED;
139
-			case \Icewind\SMB\INotifyHandler::NOTIFY_MODIFIED:
140
-			case \Icewind\SMB\INotifyHandler::NOTIFY_ADDED_STREAM:
141
-			case \Icewind\SMB\INotifyHandler::NOTIFY_MODIFIED_STREAM:
142
-			case \Icewind\SMB\INotifyHandler::NOTIFY_REMOVED_STREAM:
143
-				return IChange::MODIFIED;
144
-			case \Icewind\SMB\INotifyHandler::NOTIFY_RENAMED_NEW:
145
-				return IChange::RENAMED;
146
-			default:
147
-				return null;
148
-		}
149
-	}
133
+    private function mapNotifyType($smbType) {
134
+        switch ($smbType) {
135
+            case \Icewind\SMB\INotifyHandler::NOTIFY_ADDED:
136
+                return IChange::ADDED;
137
+            case \Icewind\SMB\INotifyHandler::NOTIFY_REMOVED:
138
+                return IChange::REMOVED;
139
+            case \Icewind\SMB\INotifyHandler::NOTIFY_MODIFIED:
140
+            case \Icewind\SMB\INotifyHandler::NOTIFY_ADDED_STREAM:
141
+            case \Icewind\SMB\INotifyHandler::NOTIFY_MODIFIED_STREAM:
142
+            case \Icewind\SMB\INotifyHandler::NOTIFY_REMOVED_STREAM:
143
+                return IChange::MODIFIED;
144
+            case \Icewind\SMB\INotifyHandler::NOTIFY_RENAMED_NEW:
145
+                return IChange::RENAMED;
146
+            default:
147
+                return null;
148
+        }
149
+    }
150 150
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -64,7 +64,7 @@
 block discarded – undo
64 64
 
65 65
 	public function listen(callable $callback) {
66 66
 		$oldRenamePath = null;
67
-		$this->shareNotifyHandler->listen(function (\Icewind\SMB\Change $shareChange) use ($callback) {
67
+		$this->shareNotifyHandler->listen(function(\Icewind\SMB\Change $shareChange) use ($callback) {
68 68
 			$change = $this->mapChange($shareChange);
69 69
 			if (!is_null($change)) {
70 70
 				return $callback($change);
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/SMB.php 4 patches
Unused Use Statements   -3 removed lines patch added patch discarded remove patch
@@ -31,14 +31,11 @@
 block discarded – undo
31 31
 
32 32
 namespace OCA\Files_External\Lib\Storage;
33 33
 
34
-use Icewind\SMB\Change;
35 34
 use Icewind\SMB\Exception\ConnectException;
36 35
 use Icewind\SMB\Exception\Exception;
37 36
 use Icewind\SMB\Exception\ForbiddenException;
38 37
 use Icewind\SMB\Exception\NotFoundException;
39
-use Icewind\SMB\INotifyHandler;
40 38
 use Icewind\SMB\IFileInfo;
41
-use Icewind\SMB\IShare;
42 39
 use Icewind\SMB\NativeServer;
43 40
 use Icewind\SMB\Server;
44 41
 use Icewind\Streams\CallbackWrapper;
Please login to merge, or discard this patch.
Doc Comments   +3 added lines patch added patch discarded remove patch
@@ -498,6 +498,9 @@
 block discarded – undo
498 498
 		});
499 499
 	}
500 500
 
501
+	/**
502
+	 * @param string $path
503
+	 */
501 504
 	public function notify($path) {
502 505
 		$path = '/' . ltrim($path, '/');
503 506
 		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
Please login to merge, or discard this patch.
Indentation   +450 added lines, -450 removed lines patch added patch discarded remove patch
@@ -53,454 +53,454 @@
 block discarded – undo
53 53
 use OCP\Files\StorageNotAvailableException;
54 54
 
55 55
 class SMB extends Common implements INotifyStorage {
56
-	/**
57
-	 * @var \Icewind\SMB\Server
58
-	 */
59
-	protected $server;
60
-
61
-	/**
62
-	 * @var \Icewind\SMB\Share
63
-	 */
64
-	protected $share;
65
-
66
-	/**
67
-	 * @var string
68
-	 */
69
-	protected $root;
70
-
71
-	/**
72
-	 * @var \Icewind\SMB\FileInfo[]
73
-	 */
74
-	protected $statCache;
75
-
76
-	public function __construct($params) {
77
-		if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) {
78
-			if (Server::NativeAvailable()) {
79
-				$this->server = new NativeServer($params['host'], $params['user'], $params['password']);
80
-			} else {
81
-				$this->server = new Server($params['host'], $params['user'], $params['password']);
82
-			}
83
-			$this->share = $this->server->getShare(trim($params['share'], '/'));
84
-
85
-			$this->root = isset($params['root']) ? $params['root'] : '/';
86
-			if (!$this->root || $this->root[0] != '/') {
87
-				$this->root = '/' . $this->root;
88
-			}
89
-			if (substr($this->root, -1, 1) != '/') {
90
-				$this->root .= '/';
91
-			}
92
-		} else {
93
-			throw new \Exception('Invalid configuration');
94
-		}
95
-		$this->statCache = new CappedMemoryCache();
96
-	}
97
-
98
-	/**
99
-	 * @return string
100
-	 */
101
-	public function getId() {
102
-		// FIXME: double slash to keep compatible with the old storage ids,
103
-		// failure to do so will lead to creation of a new storage id and
104
-		// loss of shares from the storage
105
-		return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
106
-	}
107
-
108
-	/**
109
-	 * @param string $path
110
-	 * @return string
111
-	 */
112
-	protected function buildPath($path) {
113
-		return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
114
-	}
115
-
116
-	protected function relativePath($fullPath) {
117
-		if ($fullPath === $this->root) {
118
-			return '';
119
-		} else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
120
-			return substr($fullPath, strlen($this->root));
121
-		} else {
122
-			return null;
123
-		}
124
-	}
125
-
126
-	/**
127
-	 * @param string $path
128
-	 * @return \Icewind\SMB\IFileInfo
129
-	 * @throws StorageNotAvailableException
130
-	 */
131
-	protected function getFileInfo($path) {
132
-		try {
133
-			$path = $this->buildPath($path);
134
-			if (!isset($this->statCache[$path])) {
135
-				$this->statCache[$path] = $this->share->stat($path);
136
-			}
137
-			return $this->statCache[$path];
138
-		} catch (ConnectException $e) {
139
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
140
-		}
141
-	}
142
-
143
-	/**
144
-	 * @param string $path
145
-	 * @return \Icewind\SMB\IFileInfo[]
146
-	 * @throws StorageNotAvailableException
147
-	 */
148
-	protected function getFolderContents($path) {
149
-		try {
150
-			$path = $this->buildPath($path);
151
-			$files = $this->share->dir($path);
152
-			foreach ($files as $file) {
153
-				$this->statCache[$path . '/' . $file->getName()] = $file;
154
-			}
155
-			return array_filter($files, function (IFileInfo $file) {
156
-				return !$file->isHidden();
157
-			});
158
-		} catch (ConnectException $e) {
159
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
160
-		}
161
-	}
162
-
163
-	/**
164
-	 * @param \Icewind\SMB\IFileInfo $info
165
-	 * @return array
166
-	 */
167
-	protected function formatInfo($info) {
168
-		return array(
169
-			'size' => $info->getSize(),
170
-			'mtime' => $info->getMTime()
171
-		);
172
-	}
173
-
174
-	/**
175
-	 * @param string $path
176
-	 * @return array
177
-	 */
178
-	public function stat($path) {
179
-		$result = $this->formatInfo($this->getFileInfo($path));
180
-		if ($this->remoteIsShare() && $this->isRootDir($path)) {
181
-			$result['mtime'] = $this->shareMTime();
182
-		}
183
-		return $result;
184
-	}
185
-
186
-	/**
187
-	 * get the best guess for the modification time of the share
188
-	 *
189
-	 * @return int
190
-	 */
191
-	private function shareMTime() {
192
-		$highestMTime = 0;
193
-		$files = $this->share->dir($this->root);
194
-		foreach ($files as $fileInfo) {
195
-			if ($fileInfo->getMTime() > $highestMTime) {
196
-				$highestMTime = $fileInfo->getMTime();
197
-			}
198
-		}
199
-		return $highestMTime;
200
-	}
201
-
202
-	/**
203
-	 * Check if the path is our root dir (not the smb one)
204
-	 *
205
-	 * @param string $path the path
206
-	 * @return bool
207
-	 */
208
-	private function isRootDir($path) {
209
-		return $path === '' || $path === '/' || $path === '.';
210
-	}
211
-
212
-	/**
213
-	 * Check if our root points to a smb share
214
-	 *
215
-	 * @return bool true if our root points to a share false otherwise
216
-	 */
217
-	private function remoteIsShare() {
218
-		return $this->share->getName() && (!$this->root || $this->root === '/');
219
-	}
220
-
221
-	/**
222
-	 * @param string $path
223
-	 * @return bool
224
-	 */
225
-	public function unlink($path) {
226
-		try {
227
-			if ($this->is_dir($path)) {
228
-				return $this->rmdir($path);
229
-			} else {
230
-				$path = $this->buildPath($path);
231
-				unset($this->statCache[$path]);
232
-				$this->share->del($path);
233
-				return true;
234
-			}
235
-		} catch (NotFoundException $e) {
236
-			return false;
237
-		} catch (ForbiddenException $e) {
238
-			return false;
239
-		} catch (ConnectException $e) {
240
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
241
-		}
242
-	}
243
-
244
-	/**
245
-	 * @param string $path1 the old name
246
-	 * @param string $path2 the new name
247
-	 * @return bool
248
-	 */
249
-	public function rename($path1, $path2) {
250
-		try {
251
-			$this->remove($path2);
252
-			$path1 = $this->buildPath($path1);
253
-			$path2 = $this->buildPath($path2);
254
-			return $this->share->rename($path1, $path2);
255
-		} catch (NotFoundException $e) {
256
-			return false;
257
-		} catch (ForbiddenException $e) {
258
-			return false;
259
-		} catch (ConnectException $e) {
260
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
261
-		}
262
-	}
263
-
264
-	/**
265
-	 * check if a file or folder has been updated since $time
266
-	 *
267
-	 * @param string $path
268
-	 * @param int $time
269
-	 * @return bool
270
-	 */
271
-	public function hasUpdated($path, $time) {
272
-		if (!$path and $this->root == '/') {
273
-			// mtime doesn't work for shares, but giving the nature of the backend,
274
-			// doing a full update is still just fast enough
275
-			return true;
276
-		} else {
277
-			$actualTime = $this->filemtime($path);
278
-			return $actualTime > $time;
279
-		}
280
-	}
281
-
282
-	/**
283
-	 * @param string $path
284
-	 * @param string $mode
285
-	 * @return resource|false
286
-	 */
287
-	public function fopen($path, $mode) {
288
-		$fullPath = $this->buildPath($path);
289
-		try {
290
-			switch ($mode) {
291
-				case 'r':
292
-				case 'rb':
293
-					if (!$this->file_exists($path)) {
294
-						return false;
295
-					}
296
-					return $this->share->read($fullPath);
297
-				case 'w':
298
-				case 'wb':
299
-					$source = $this->share->write($fullPath);
300
-					return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
301
-						unset($this->statCache[$fullPath]);
302
-					});
303
-				case 'a':
304
-				case 'ab':
305
-				case 'r+':
306
-				case 'w+':
307
-				case 'wb+':
308
-				case 'a+':
309
-				case 'x':
310
-				case 'x+':
311
-				case 'c':
312
-				case 'c+':
313
-					//emulate these
314
-					if (strrpos($path, '.') !== false) {
315
-						$ext = substr($path, strrpos($path, '.'));
316
-					} else {
317
-						$ext = '';
318
-					}
319
-					if ($this->file_exists($path)) {
320
-						if (!$this->isUpdatable($path)) {
321
-							return false;
322
-						}
323
-						$tmpFile = $this->getCachedFile($path);
324
-					} else {
325
-						if (!$this->isCreatable(dirname($path))) {
326
-							return false;
327
-						}
328
-						$tmpFile = \OCP\Files::tmpFile($ext);
329
-					}
330
-					$source = fopen($tmpFile, $mode);
331
-					$share = $this->share;
332
-					return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
333
-						unset($this->statCache[$fullPath]);
334
-						$share->put($tmpFile, $fullPath);
335
-						unlink($tmpFile);
336
-					});
337
-			}
338
-			return false;
339
-		} catch (NotFoundException $e) {
340
-			return false;
341
-		} catch (ForbiddenException $e) {
342
-			return false;
343
-		} catch (ConnectException $e) {
344
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
345
-		}
346
-	}
347
-
348
-	public function rmdir($path) {
349
-		try {
350
-			$this->statCache = array();
351
-			$content = $this->share->dir($this->buildPath($path));
352
-			foreach ($content as $file) {
353
-				if ($file->isDirectory()) {
354
-					$this->rmdir($path . '/' . $file->getName());
355
-				} else {
356
-					$this->share->del($file->getPath());
357
-				}
358
-			}
359
-			$this->share->rmdir($this->buildPath($path));
360
-			return true;
361
-		} catch (NotFoundException $e) {
362
-			return false;
363
-		} catch (ForbiddenException $e) {
364
-			return false;
365
-		} catch (ConnectException $e) {
366
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
367
-		}
368
-	}
369
-
370
-	public function touch($path, $time = null) {
371
-		try {
372
-			if (!$this->file_exists($path)) {
373
-				$fh = $this->share->write($this->buildPath($path));
374
-				fclose($fh);
375
-				return true;
376
-			}
377
-			return false;
378
-		} catch (ConnectException $e) {
379
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
380
-		}
381
-	}
382
-
383
-	public function opendir($path) {
384
-		try {
385
-			$files = $this->getFolderContents($path);
386
-		} catch (NotFoundException $e) {
387
-			return false;
388
-		} catch (ForbiddenException $e) {
389
-			return false;
390
-		}
391
-		$names = array_map(function ($info) {
392
-			/** @var \Icewind\SMB\IFileInfo $info */
393
-			return $info->getName();
394
-		}, $files);
395
-		return IteratorDirectory::wrap($names);
396
-	}
397
-
398
-	public function filetype($path) {
399
-		try {
400
-			return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
401
-		} catch (NotFoundException $e) {
402
-			return false;
403
-		} catch (ForbiddenException $e) {
404
-			return false;
405
-		}
406
-	}
407
-
408
-	public function mkdir($path) {
409
-		$path = $this->buildPath($path);
410
-		try {
411
-			$this->share->mkdir($path);
412
-			return true;
413
-		} catch (ConnectException $e) {
414
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
415
-		} catch (Exception $e) {
416
-			return false;
417
-		}
418
-	}
419
-
420
-	public function file_exists($path) {
421
-		try {
422
-			$this->getFileInfo($path);
423
-			return true;
424
-		} catch (NotFoundException $e) {
425
-			return false;
426
-		} catch (ForbiddenException $e) {
427
-			return false;
428
-		} catch (ConnectException $e) {
429
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
430
-		}
431
-	}
432
-
433
-	public function isReadable($path) {
434
-		try {
435
-			$info = $this->getFileInfo($path);
436
-			return !$info->isHidden();
437
-		} catch (NotFoundException $e) {
438
-			return false;
439
-		} catch (ForbiddenException $e) {
440
-			return false;
441
-		}
442
-	}
443
-
444
-	public function isUpdatable($path) {
445
-		try {
446
-			$info = $this->getFileInfo($path);
447
-			// following windows behaviour for read-only folders: they can be written into
448
-			// (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
449
-			return !$info->isHidden() && (!$info->isReadOnly() || $this->is_dir($path));
450
-		} catch (NotFoundException $e) {
451
-			return false;
452
-		} catch (ForbiddenException $e) {
453
-			return false;
454
-		}
455
-	}
456
-
457
-	public function isDeletable($path) {
458
-		try {
459
-			$info = $this->getFileInfo($path);
460
-			return !$info->isHidden() && !$info->isReadOnly();
461
-		} catch (NotFoundException $e) {
462
-			return false;
463
-		} catch (ForbiddenException $e) {
464
-			return false;
465
-		}
466
-	}
467
-
468
-	/**
469
-	 * check if smbclient is installed
470
-	 */
471
-	public static function checkDependencies() {
472
-		return (
473
-			(bool)\OC_Helper::findBinaryPath('smbclient')
474
-			|| Server::NativeAvailable()
475
-		) ? true : ['smbclient'];
476
-	}
477
-
478
-	/**
479
-	 * Test a storage for availability
480
-	 *
481
-	 * @return bool
482
-	 */
483
-	public function test() {
484
-		try {
485
-			return parent::test();
486
-		} catch (Exception $e) {
487
-			return false;
488
-		}
489
-	}
490
-
491
-	public function listen($path, callable $callback) {
492
-		$this->notify($path)->listen(function (IChange $change) use ($callback) {
493
-			if ($change instanceof IRenameChange) {
494
-				return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
495
-			} else {
496
-				return $callback($change->getType(), $change->getPath());
497
-			}
498
-		});
499
-	}
500
-
501
-	public function notify($path) {
502
-		$path = '/' . ltrim($path, '/');
503
-		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
504
-		return new SMBNotifyHandler($shareNotifyHandler, $this->root);
505
-	}
56
+    /**
57
+     * @var \Icewind\SMB\Server
58
+     */
59
+    protected $server;
60
+
61
+    /**
62
+     * @var \Icewind\SMB\Share
63
+     */
64
+    protected $share;
65
+
66
+    /**
67
+     * @var string
68
+     */
69
+    protected $root;
70
+
71
+    /**
72
+     * @var \Icewind\SMB\FileInfo[]
73
+     */
74
+    protected $statCache;
75
+
76
+    public function __construct($params) {
77
+        if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) {
78
+            if (Server::NativeAvailable()) {
79
+                $this->server = new NativeServer($params['host'], $params['user'], $params['password']);
80
+            } else {
81
+                $this->server = new Server($params['host'], $params['user'], $params['password']);
82
+            }
83
+            $this->share = $this->server->getShare(trim($params['share'], '/'));
84
+
85
+            $this->root = isset($params['root']) ? $params['root'] : '/';
86
+            if (!$this->root || $this->root[0] != '/') {
87
+                $this->root = '/' . $this->root;
88
+            }
89
+            if (substr($this->root, -1, 1) != '/') {
90
+                $this->root .= '/';
91
+            }
92
+        } else {
93
+            throw new \Exception('Invalid configuration');
94
+        }
95
+        $this->statCache = new CappedMemoryCache();
96
+    }
97
+
98
+    /**
99
+     * @return string
100
+     */
101
+    public function getId() {
102
+        // FIXME: double slash to keep compatible with the old storage ids,
103
+        // failure to do so will lead to creation of a new storage id and
104
+        // loss of shares from the storage
105
+        return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
106
+    }
107
+
108
+    /**
109
+     * @param string $path
110
+     * @return string
111
+     */
112
+    protected function buildPath($path) {
113
+        return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
114
+    }
115
+
116
+    protected function relativePath($fullPath) {
117
+        if ($fullPath === $this->root) {
118
+            return '';
119
+        } else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
120
+            return substr($fullPath, strlen($this->root));
121
+        } else {
122
+            return null;
123
+        }
124
+    }
125
+
126
+    /**
127
+     * @param string $path
128
+     * @return \Icewind\SMB\IFileInfo
129
+     * @throws StorageNotAvailableException
130
+     */
131
+    protected function getFileInfo($path) {
132
+        try {
133
+            $path = $this->buildPath($path);
134
+            if (!isset($this->statCache[$path])) {
135
+                $this->statCache[$path] = $this->share->stat($path);
136
+            }
137
+            return $this->statCache[$path];
138
+        } catch (ConnectException $e) {
139
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
140
+        }
141
+    }
142
+
143
+    /**
144
+     * @param string $path
145
+     * @return \Icewind\SMB\IFileInfo[]
146
+     * @throws StorageNotAvailableException
147
+     */
148
+    protected function getFolderContents($path) {
149
+        try {
150
+            $path = $this->buildPath($path);
151
+            $files = $this->share->dir($path);
152
+            foreach ($files as $file) {
153
+                $this->statCache[$path . '/' . $file->getName()] = $file;
154
+            }
155
+            return array_filter($files, function (IFileInfo $file) {
156
+                return !$file->isHidden();
157
+            });
158
+        } catch (ConnectException $e) {
159
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
160
+        }
161
+    }
162
+
163
+    /**
164
+     * @param \Icewind\SMB\IFileInfo $info
165
+     * @return array
166
+     */
167
+    protected function formatInfo($info) {
168
+        return array(
169
+            'size' => $info->getSize(),
170
+            'mtime' => $info->getMTime()
171
+        );
172
+    }
173
+
174
+    /**
175
+     * @param string $path
176
+     * @return array
177
+     */
178
+    public function stat($path) {
179
+        $result = $this->formatInfo($this->getFileInfo($path));
180
+        if ($this->remoteIsShare() && $this->isRootDir($path)) {
181
+            $result['mtime'] = $this->shareMTime();
182
+        }
183
+        return $result;
184
+    }
185
+
186
+    /**
187
+     * get the best guess for the modification time of the share
188
+     *
189
+     * @return int
190
+     */
191
+    private function shareMTime() {
192
+        $highestMTime = 0;
193
+        $files = $this->share->dir($this->root);
194
+        foreach ($files as $fileInfo) {
195
+            if ($fileInfo->getMTime() > $highestMTime) {
196
+                $highestMTime = $fileInfo->getMTime();
197
+            }
198
+        }
199
+        return $highestMTime;
200
+    }
201
+
202
+    /**
203
+     * Check if the path is our root dir (not the smb one)
204
+     *
205
+     * @param string $path the path
206
+     * @return bool
207
+     */
208
+    private function isRootDir($path) {
209
+        return $path === '' || $path === '/' || $path === '.';
210
+    }
211
+
212
+    /**
213
+     * Check if our root points to a smb share
214
+     *
215
+     * @return bool true if our root points to a share false otherwise
216
+     */
217
+    private function remoteIsShare() {
218
+        return $this->share->getName() && (!$this->root || $this->root === '/');
219
+    }
220
+
221
+    /**
222
+     * @param string $path
223
+     * @return bool
224
+     */
225
+    public function unlink($path) {
226
+        try {
227
+            if ($this->is_dir($path)) {
228
+                return $this->rmdir($path);
229
+            } else {
230
+                $path = $this->buildPath($path);
231
+                unset($this->statCache[$path]);
232
+                $this->share->del($path);
233
+                return true;
234
+            }
235
+        } catch (NotFoundException $e) {
236
+            return false;
237
+        } catch (ForbiddenException $e) {
238
+            return false;
239
+        } catch (ConnectException $e) {
240
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
241
+        }
242
+    }
243
+
244
+    /**
245
+     * @param string $path1 the old name
246
+     * @param string $path2 the new name
247
+     * @return bool
248
+     */
249
+    public function rename($path1, $path2) {
250
+        try {
251
+            $this->remove($path2);
252
+            $path1 = $this->buildPath($path1);
253
+            $path2 = $this->buildPath($path2);
254
+            return $this->share->rename($path1, $path2);
255
+        } catch (NotFoundException $e) {
256
+            return false;
257
+        } catch (ForbiddenException $e) {
258
+            return false;
259
+        } catch (ConnectException $e) {
260
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
261
+        }
262
+    }
263
+
264
+    /**
265
+     * check if a file or folder has been updated since $time
266
+     *
267
+     * @param string $path
268
+     * @param int $time
269
+     * @return bool
270
+     */
271
+    public function hasUpdated($path, $time) {
272
+        if (!$path and $this->root == '/') {
273
+            // mtime doesn't work for shares, but giving the nature of the backend,
274
+            // doing a full update is still just fast enough
275
+            return true;
276
+        } else {
277
+            $actualTime = $this->filemtime($path);
278
+            return $actualTime > $time;
279
+        }
280
+    }
281
+
282
+    /**
283
+     * @param string $path
284
+     * @param string $mode
285
+     * @return resource|false
286
+     */
287
+    public function fopen($path, $mode) {
288
+        $fullPath = $this->buildPath($path);
289
+        try {
290
+            switch ($mode) {
291
+                case 'r':
292
+                case 'rb':
293
+                    if (!$this->file_exists($path)) {
294
+                        return false;
295
+                    }
296
+                    return $this->share->read($fullPath);
297
+                case 'w':
298
+                case 'wb':
299
+                    $source = $this->share->write($fullPath);
300
+                    return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
301
+                        unset($this->statCache[$fullPath]);
302
+                    });
303
+                case 'a':
304
+                case 'ab':
305
+                case 'r+':
306
+                case 'w+':
307
+                case 'wb+':
308
+                case 'a+':
309
+                case 'x':
310
+                case 'x+':
311
+                case 'c':
312
+                case 'c+':
313
+                    //emulate these
314
+                    if (strrpos($path, '.') !== false) {
315
+                        $ext = substr($path, strrpos($path, '.'));
316
+                    } else {
317
+                        $ext = '';
318
+                    }
319
+                    if ($this->file_exists($path)) {
320
+                        if (!$this->isUpdatable($path)) {
321
+                            return false;
322
+                        }
323
+                        $tmpFile = $this->getCachedFile($path);
324
+                    } else {
325
+                        if (!$this->isCreatable(dirname($path))) {
326
+                            return false;
327
+                        }
328
+                        $tmpFile = \OCP\Files::tmpFile($ext);
329
+                    }
330
+                    $source = fopen($tmpFile, $mode);
331
+                    $share = $this->share;
332
+                    return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
333
+                        unset($this->statCache[$fullPath]);
334
+                        $share->put($tmpFile, $fullPath);
335
+                        unlink($tmpFile);
336
+                    });
337
+            }
338
+            return false;
339
+        } catch (NotFoundException $e) {
340
+            return false;
341
+        } catch (ForbiddenException $e) {
342
+            return false;
343
+        } catch (ConnectException $e) {
344
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
345
+        }
346
+    }
347
+
348
+    public function rmdir($path) {
349
+        try {
350
+            $this->statCache = array();
351
+            $content = $this->share->dir($this->buildPath($path));
352
+            foreach ($content as $file) {
353
+                if ($file->isDirectory()) {
354
+                    $this->rmdir($path . '/' . $file->getName());
355
+                } else {
356
+                    $this->share->del($file->getPath());
357
+                }
358
+            }
359
+            $this->share->rmdir($this->buildPath($path));
360
+            return true;
361
+        } catch (NotFoundException $e) {
362
+            return false;
363
+        } catch (ForbiddenException $e) {
364
+            return false;
365
+        } catch (ConnectException $e) {
366
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
367
+        }
368
+    }
369
+
370
+    public function touch($path, $time = null) {
371
+        try {
372
+            if (!$this->file_exists($path)) {
373
+                $fh = $this->share->write($this->buildPath($path));
374
+                fclose($fh);
375
+                return true;
376
+            }
377
+            return false;
378
+        } catch (ConnectException $e) {
379
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
380
+        }
381
+    }
382
+
383
+    public function opendir($path) {
384
+        try {
385
+            $files = $this->getFolderContents($path);
386
+        } catch (NotFoundException $e) {
387
+            return false;
388
+        } catch (ForbiddenException $e) {
389
+            return false;
390
+        }
391
+        $names = array_map(function ($info) {
392
+            /** @var \Icewind\SMB\IFileInfo $info */
393
+            return $info->getName();
394
+        }, $files);
395
+        return IteratorDirectory::wrap($names);
396
+    }
397
+
398
+    public function filetype($path) {
399
+        try {
400
+            return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
401
+        } catch (NotFoundException $e) {
402
+            return false;
403
+        } catch (ForbiddenException $e) {
404
+            return false;
405
+        }
406
+    }
407
+
408
+    public function mkdir($path) {
409
+        $path = $this->buildPath($path);
410
+        try {
411
+            $this->share->mkdir($path);
412
+            return true;
413
+        } catch (ConnectException $e) {
414
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
415
+        } catch (Exception $e) {
416
+            return false;
417
+        }
418
+    }
419
+
420
+    public function file_exists($path) {
421
+        try {
422
+            $this->getFileInfo($path);
423
+            return true;
424
+        } catch (NotFoundException $e) {
425
+            return false;
426
+        } catch (ForbiddenException $e) {
427
+            return false;
428
+        } catch (ConnectException $e) {
429
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
430
+        }
431
+    }
432
+
433
+    public function isReadable($path) {
434
+        try {
435
+            $info = $this->getFileInfo($path);
436
+            return !$info->isHidden();
437
+        } catch (NotFoundException $e) {
438
+            return false;
439
+        } catch (ForbiddenException $e) {
440
+            return false;
441
+        }
442
+    }
443
+
444
+    public function isUpdatable($path) {
445
+        try {
446
+            $info = $this->getFileInfo($path);
447
+            // following windows behaviour for read-only folders: they can be written into
448
+            // (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
449
+            return !$info->isHidden() && (!$info->isReadOnly() || $this->is_dir($path));
450
+        } catch (NotFoundException $e) {
451
+            return false;
452
+        } catch (ForbiddenException $e) {
453
+            return false;
454
+        }
455
+    }
456
+
457
+    public function isDeletable($path) {
458
+        try {
459
+            $info = $this->getFileInfo($path);
460
+            return !$info->isHidden() && !$info->isReadOnly();
461
+        } catch (NotFoundException $e) {
462
+            return false;
463
+        } catch (ForbiddenException $e) {
464
+            return false;
465
+        }
466
+    }
467
+
468
+    /**
469
+     * check if smbclient is installed
470
+     */
471
+    public static function checkDependencies() {
472
+        return (
473
+            (bool)\OC_Helper::findBinaryPath('smbclient')
474
+            || Server::NativeAvailable()
475
+        ) ? true : ['smbclient'];
476
+    }
477
+
478
+    /**
479
+     * Test a storage for availability
480
+     *
481
+     * @return bool
482
+     */
483
+    public function test() {
484
+        try {
485
+            return parent::test();
486
+        } catch (Exception $e) {
487
+            return false;
488
+        }
489
+    }
490
+
491
+    public function listen($path, callable $callback) {
492
+        $this->notify($path)->listen(function (IChange $change) use ($callback) {
493
+            if ($change instanceof IRenameChange) {
494
+                return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
495
+            } else {
496
+                return $callback($change->getType(), $change->getPath());
497
+            }
498
+        });
499
+    }
500
+
501
+    public function notify($path) {
502
+        $path = '/' . ltrim($path, '/');
503
+        $shareNotifyHandler = $this->share->notify($this->buildPath($path));
504
+        return new SMBNotifyHandler($shareNotifyHandler, $this->root);
505
+    }
506 506
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -84,7 +84,7 @@  discard block
 block discarded – undo
84 84
 
85 85
 			$this->root = isset($params['root']) ? $params['root'] : '/';
86 86
 			if (!$this->root || $this->root[0] != '/') {
87
-				$this->root = '/' . $this->root;
87
+				$this->root = '/'.$this->root;
88 88
 			}
89 89
 			if (substr($this->root, -1, 1) != '/') {
90 90
 				$this->root .= '/';
@@ -102,7 +102,7 @@  discard block
 block discarded – undo
102 102
 		// FIXME: double slash to keep compatible with the old storage ids,
103 103
 		// failure to do so will lead to creation of a new storage id and
104 104
 		// loss of shares from the storage
105
-		return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
105
+		return 'smb::'.$this->server->getUser().'@'.$this->server->getHost().'//'.$this->share->getName().'/'.$this->root;
106 106
 	}
107 107
 
108 108
 	/**
@@ -110,7 +110,7 @@  discard block
 block discarded – undo
110 110
 	 * @return string
111 111
 	 */
112 112
 	protected function buildPath($path) {
113
-		return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
113
+		return Filesystem::normalizePath($this->root.'/'.$path, true, false, true);
114 114
 	}
115 115
 
116 116
 	protected function relativePath($fullPath) {
@@ -150,9 +150,9 @@  discard block
 block discarded – undo
150 150
 			$path = $this->buildPath($path);
151 151
 			$files = $this->share->dir($path);
152 152
 			foreach ($files as $file) {
153
-				$this->statCache[$path . '/' . $file->getName()] = $file;
153
+				$this->statCache[$path.'/'.$file->getName()] = $file;
154 154
 			}
155
-			return array_filter($files, function (IFileInfo $file) {
155
+			return array_filter($files, function(IFileInfo $file) {
156 156
 				return !$file->isHidden();
157 157
 			});
158 158
 		} catch (ConnectException $e) {
@@ -297,7 +297,7 @@  discard block
 block discarded – undo
297 297
 				case 'w':
298 298
 				case 'wb':
299 299
 					$source = $this->share->write($fullPath);
300
-					return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
300
+					return CallBackWrapper::wrap($source, null, null, function() use ($fullPath) {
301 301
 						unset($this->statCache[$fullPath]);
302 302
 					});
303 303
 				case 'a':
@@ -329,7 +329,7 @@  discard block
 block discarded – undo
329 329
 					}
330 330
 					$source = fopen($tmpFile, $mode);
331 331
 					$share = $this->share;
332
-					return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
332
+					return CallbackWrapper::wrap($source, null, null, function() use ($tmpFile, $fullPath, $share) {
333 333
 						unset($this->statCache[$fullPath]);
334 334
 						$share->put($tmpFile, $fullPath);
335 335
 						unlink($tmpFile);
@@ -351,7 +351,7 @@  discard block
 block discarded – undo
351 351
 			$content = $this->share->dir($this->buildPath($path));
352 352
 			foreach ($content as $file) {
353 353
 				if ($file->isDirectory()) {
354
-					$this->rmdir($path . '/' . $file->getName());
354
+					$this->rmdir($path.'/'.$file->getName());
355 355
 				} else {
356 356
 					$this->share->del($file->getPath());
357 357
 				}
@@ -388,7 +388,7 @@  discard block
 block discarded – undo
388 388
 		} catch (ForbiddenException $e) {
389 389
 			return false;
390 390
 		}
391
-		$names = array_map(function ($info) {
391
+		$names = array_map(function($info) {
392 392
 			/** @var \Icewind\SMB\IFileInfo $info */
393 393
 			return $info->getName();
394 394
 		}, $files);
@@ -470,7 +470,7 @@  discard block
 block discarded – undo
470 470
 	 */
471 471
 	public static function checkDependencies() {
472 472
 		return (
473
-			(bool)\OC_Helper::findBinaryPath('smbclient')
473
+			(bool) \OC_Helper::findBinaryPath('smbclient')
474 474
 			|| Server::NativeAvailable()
475 475
 		) ? true : ['smbclient'];
476 476
 	}
@@ -489,7 +489,7 @@  discard block
 block discarded – undo
489 489
 	}
490 490
 
491 491
 	public function listen($path, callable $callback) {
492
-		$this->notify($path)->listen(function (IChange $change) use ($callback) {
492
+		$this->notify($path)->listen(function(IChange $change) use ($callback) {
493 493
 			if ($change instanceof IRenameChange) {
494 494
 				return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
495 495
 			} else {
@@ -499,7 +499,7 @@  discard block
 block discarded – undo
499 499
 	}
500 500
 
501 501
 	public function notify($path) {
502
-		$path = '/' . ltrim($path, '/');
502
+		$path = '/'.ltrim($path, '/');
503 503
 		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
504 504
 		return new SMBNotifyHandler($shareNotifyHandler, $this->root);
505 505
 	}
Please login to merge, or discard this patch.
lib/private/Files/Node/File.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -31,7 +31,7 @@
 block discarded – undo
31 31
 	 * Creates a Folder that represents a non-existing path
32 32
 	 *
33 33
 	 * @param string $path path
34
-	 * @return string non-existing node class
34
+	 * @return NonExistingFile non-existing node class
35 35
 	 */
36 36
 	protected function createNonExistingNode($path) {
37 37
 		return new NonExistingFile($this->root, $this->view, $path);
Please login to merge, or discard this patch.
Indentation   +102 added lines, -102 removed lines patch added patch discarded remove patch
@@ -29,113 +29,113 @@
 block discarded – undo
29 29
 use OCP\Files\NotPermittedException;
30 30
 
31 31
 class File extends Node implements \OCP\Files\File {
32
-	/**
33
-	 * Creates a Folder that represents a non-existing path
34
-	 *
35
-	 * @param string $path path
36
-	 * @return string non-existing node class
37
-	 */
38
-	protected function createNonExistingNode($path) {
39
-		return new NonExistingFile($this->root, $this->view, $path);
40
-	}
32
+    /**
33
+     * Creates a Folder that represents a non-existing path
34
+     *
35
+     * @param string $path path
36
+     * @return string non-existing node class
37
+     */
38
+    protected function createNonExistingNode($path) {
39
+        return new NonExistingFile($this->root, $this->view, $path);
40
+    }
41 41
 
42
-	/**
43
-	 * @return string
44
-	 * @throws \OCP\Files\NotPermittedException
45
-	 */
46
-	public function getContent() {
47
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_READ)) {
48
-			/**
49
-			 * @var \OC\Files\Storage\Storage $storage;
50
-			 */
51
-			return $this->view->file_get_contents($this->path);
52
-		} else {
53
-			throw new NotPermittedException();
54
-		}
55
-	}
42
+    /**
43
+     * @return string
44
+     * @throws \OCP\Files\NotPermittedException
45
+     */
46
+    public function getContent() {
47
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_READ)) {
48
+            /**
49
+             * @var \OC\Files\Storage\Storage $storage;
50
+             */
51
+            return $this->view->file_get_contents($this->path);
52
+        } else {
53
+            throw new NotPermittedException();
54
+        }
55
+    }
56 56
 
57
-	/**
58
-	 * @param string $data
59
-	 * @throws \OCP\Files\NotPermittedException
60
-	 */
61
-	public function putContent($data) {
62
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) {
63
-			$this->sendHooks(array('preWrite'));
64
-			$this->view->file_put_contents($this->path, $data);
65
-			$this->fileInfo = null;
66
-			$this->sendHooks(array('postWrite'));
67
-		} else {
68
-			throw new NotPermittedException();
69
-		}
70
-	}
57
+    /**
58
+     * @param string $data
59
+     * @throws \OCP\Files\NotPermittedException
60
+     */
61
+    public function putContent($data) {
62
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) {
63
+            $this->sendHooks(array('preWrite'));
64
+            $this->view->file_put_contents($this->path, $data);
65
+            $this->fileInfo = null;
66
+            $this->sendHooks(array('postWrite'));
67
+        } else {
68
+            throw new NotPermittedException();
69
+        }
70
+    }
71 71
 
72
-	/**
73
-	 * @param string $mode
74
-	 * @return resource
75
-	 * @throws \OCP\Files\NotPermittedException
76
-	 */
77
-	public function fopen($mode) {
78
-		$preHooks = array();
79
-		$postHooks = array();
80
-		$requiredPermissions = \OCP\Constants::PERMISSION_READ;
81
-		switch ($mode) {
82
-			case 'r+':
83
-			case 'rb+':
84
-			case 'w+':
85
-			case 'wb+':
86
-			case 'x+':
87
-			case 'xb+':
88
-			case 'a+':
89
-			case 'ab+':
90
-			case 'w':
91
-			case 'wb':
92
-			case 'x':
93
-			case 'xb':
94
-			case 'a':
95
-			case 'ab':
96
-				$preHooks[] = 'preWrite';
97
-				$postHooks[] = 'postWrite';
98
-				$requiredPermissions |= \OCP\Constants::PERMISSION_UPDATE;
99
-				break;
100
-		}
72
+    /**
73
+     * @param string $mode
74
+     * @return resource
75
+     * @throws \OCP\Files\NotPermittedException
76
+     */
77
+    public function fopen($mode) {
78
+        $preHooks = array();
79
+        $postHooks = array();
80
+        $requiredPermissions = \OCP\Constants::PERMISSION_READ;
81
+        switch ($mode) {
82
+            case 'r+':
83
+            case 'rb+':
84
+            case 'w+':
85
+            case 'wb+':
86
+            case 'x+':
87
+            case 'xb+':
88
+            case 'a+':
89
+            case 'ab+':
90
+            case 'w':
91
+            case 'wb':
92
+            case 'x':
93
+            case 'xb':
94
+            case 'a':
95
+            case 'ab':
96
+                $preHooks[] = 'preWrite';
97
+                $postHooks[] = 'postWrite';
98
+                $requiredPermissions |= \OCP\Constants::PERMISSION_UPDATE;
99
+                break;
100
+        }
101 101
 
102
-		if ($this->checkPermissions($requiredPermissions)) {
103
-			$this->sendHooks($preHooks);
104
-			$result = $this->view->fopen($this->path, $mode);
105
-			$this->sendHooks($postHooks);
106
-			return $result;
107
-		} else {
108
-			throw new NotPermittedException();
109
-		}
110
-	}
102
+        if ($this->checkPermissions($requiredPermissions)) {
103
+            $this->sendHooks($preHooks);
104
+            $result = $this->view->fopen($this->path, $mode);
105
+            $this->sendHooks($postHooks);
106
+            return $result;
107
+        } else {
108
+            throw new NotPermittedException();
109
+        }
110
+    }
111 111
 
112
-	public function delete() {
113
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
114
-			$this->sendHooks(array('preDelete'));
115
-			$fileInfo = $this->getFileInfo();
116
-			$this->view->unlink($this->path);
117
-			$nonExisting = new NonExistingFile($this->root, $this->view, $this->path, $fileInfo);
118
-			$this->root->emit('\OC\Files', 'postDelete', array($nonExisting));
119
-			$this->exists = false;
120
-			$this->fileInfo = null;
121
-		} else {
122
-			throw new NotPermittedException();
123
-		}
124
-	}
112
+    public function delete() {
113
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
114
+            $this->sendHooks(array('preDelete'));
115
+            $fileInfo = $this->getFileInfo();
116
+            $this->view->unlink($this->path);
117
+            $nonExisting = new NonExistingFile($this->root, $this->view, $this->path, $fileInfo);
118
+            $this->root->emit('\OC\Files', 'postDelete', array($nonExisting));
119
+            $this->exists = false;
120
+            $this->fileInfo = null;
121
+        } else {
122
+            throw new NotPermittedException();
123
+        }
124
+    }
125 125
 
126
-	/**
127
-	 * @param string $type
128
-	 * @param bool $raw
129
-	 * @return string
130
-	 */
131
-	public function hash($type, $raw = false) {
132
-		return $this->view->hash($type, $this->path, $raw);
133
-	}
126
+    /**
127
+     * @param string $type
128
+     * @param bool $raw
129
+     * @return string
130
+     */
131
+    public function hash($type, $raw = false) {
132
+        return $this->view->hash($type, $this->path, $raw);
133
+    }
134 134
 
135
-	/**
136
-	 * @inheritdoc
137
-	 */
138
-	public function getChecksum() {
139
-		return $this->getFileInfo()->getChecksum();
140
-	}
135
+    /**
136
+     * @inheritdoc
137
+     */
138
+    public function getChecksum() {
139
+        return $this->getFileInfo()->getChecksum();
140
+    }
141 141
 }
Please login to merge, or discard this patch.
lib/private/Files/Node/Folder.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -37,7 +37,7 @@
 block discarded – undo
37 37
 	 * Creates a Folder that represents a non-existing path
38 38
 	 *
39 39
 	 * @param string $path path
40
-	 * @return string non-existing node class
40
+	 * @return NonExistingFolder non-existing node class
41 41
 	 */
42 42
 	protected function createNonExistingNode($path) {
43 43
 		return new NonExistingFolder($this->root, $this->view, $path);
Please login to merge, or discard this patch.
Indentation   +386 added lines, -386 removed lines patch added patch discarded remove patch
@@ -35,390 +35,390 @@
 block discarded – undo
35 35
 use OCP\Files\NotPermittedException;
36 36
 
37 37
 class Folder extends Node implements \OCP\Files\Folder {
38
-	/**
39
-	 * Creates a Folder that represents a non-existing path
40
-	 *
41
-	 * @param string $path path
42
-	 * @return string non-existing node class
43
-	 */
44
-	protected function createNonExistingNode($path) {
45
-		return new NonExistingFolder($this->root, $this->view, $path);
46
-	}
47
-
48
-	/**
49
-	 * @param string $path path relative to the folder
50
-	 * @return string
51
-	 * @throws \OCP\Files\NotPermittedException
52
-	 */
53
-	public function getFullPath($path) {
54
-		if (!$this->isValidPath($path)) {
55
-			throw new NotPermittedException('Invalid path');
56
-		}
57
-		return $this->path . $this->normalizePath($path);
58
-	}
59
-
60
-	/**
61
-	 * @param string $path
62
-	 * @return string
63
-	 */
64
-	public function getRelativePath($path) {
65
-		if ($this->path === '' or $this->path === '/') {
66
-			return $this->normalizePath($path);
67
-		}
68
-		if ($path === $this->path) {
69
-			return '/';
70
-		} else if (strpos($path, $this->path . '/') !== 0) {
71
-			return null;
72
-		} else {
73
-			$path = substr($path, strlen($this->path));
74
-			return $this->normalizePath($path);
75
-		}
76
-	}
77
-
78
-	/**
79
-	 * check if a node is a (grand-)child of the folder
80
-	 *
81
-	 * @param \OC\Files\Node\Node $node
82
-	 * @return bool
83
-	 */
84
-	public function isSubNode($node) {
85
-		return strpos($node->getPath(), $this->path . '/') === 0;
86
-	}
87
-
88
-	/**
89
-	 * get the content of this directory
90
-	 *
91
-	 * @throws \OCP\Files\NotFoundException
92
-	 * @return Node[]
93
-	 */
94
-	public function getDirectoryListing() {
95
-		$folderContent = $this->view->getDirectoryContent($this->path);
96
-
97
-		return array_map(function (FileInfo $info) {
98
-			if ($info->getMimetype() === 'httpd/unix-directory') {
99
-				return new Folder($this->root, $this->view, $info->getPath(), $info);
100
-			} else {
101
-				return new File($this->root, $this->view, $info->getPath(), $info);
102
-			}
103
-		}, $folderContent);
104
-	}
105
-
106
-	/**
107
-	 * @param string $path
108
-	 * @param FileInfo $info
109
-	 * @return File|Folder
110
-	 */
111
-	protected function createNode($path, FileInfo $info = null) {
112
-		if (is_null($info)) {
113
-			$isDir = $this->view->is_dir($path);
114
-		} else {
115
-			$isDir = $info->getType() === FileInfo::TYPE_FOLDER;
116
-		}
117
-		if ($isDir) {
118
-			return new Folder($this->root, $this->view, $path, $info);
119
-		} else {
120
-			return new File($this->root, $this->view, $path, $info);
121
-		}
122
-	}
123
-
124
-	/**
125
-	 * Get the node at $path
126
-	 *
127
-	 * @param string $path
128
-	 * @return \OC\Files\Node\Node
129
-	 * @throws \OCP\Files\NotFoundException
130
-	 */
131
-	public function get($path) {
132
-		return $this->root->get($this->getFullPath($path));
133
-	}
134
-
135
-	/**
136
-	 * @param string $path
137
-	 * @return bool
138
-	 */
139
-	public function nodeExists($path) {
140
-		try {
141
-			$this->get($path);
142
-			return true;
143
-		} catch (NotFoundException $e) {
144
-			return false;
145
-		}
146
-	}
147
-
148
-	/**
149
-	 * @param string $path
150
-	 * @return \OC\Files\Node\Folder
151
-	 * @throws \OCP\Files\NotPermittedException
152
-	 */
153
-	public function newFolder($path) {
154
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
155
-			$fullPath = $this->getFullPath($path);
156
-			$nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath);
157
-			$this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
158
-			$this->root->emit('\OC\Files', 'preCreate', array($nonExisting));
159
-			$this->view->mkdir($fullPath);
160
-			$node = new Folder($this->root, $this->view, $fullPath);
161
-			$this->root->emit('\OC\Files', 'postWrite', array($node));
162
-			$this->root->emit('\OC\Files', 'postCreate', array($node));
163
-			return $node;
164
-		} else {
165
-			throw new NotPermittedException('No create permission for folder');
166
-		}
167
-	}
168
-
169
-	/**
170
-	 * @param string $path
171
-	 * @return \OC\Files\Node\File
172
-	 * @throws \OCP\Files\NotPermittedException
173
-	 */
174
-	public function newFile($path) {
175
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
176
-			$fullPath = $this->getFullPath($path);
177
-			$nonExisting = new NonExistingFile($this->root, $this->view, $fullPath);
178
-			$this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
179
-			$this->root->emit('\OC\Files', 'preCreate', array($nonExisting));
180
-			$this->view->touch($fullPath);
181
-			$node = new File($this->root, $this->view, $fullPath);
182
-			$this->root->emit('\OC\Files', 'postWrite', array($node));
183
-			$this->root->emit('\OC\Files', 'postCreate', array($node));
184
-			return $node;
185
-		} else {
186
-			throw new NotPermittedException('No create permission for path');
187
-		}
188
-	}
189
-
190
-	/**
191
-	 * search for files with the name matching $query
192
-	 *
193
-	 * @param string $query
194
-	 * @return \OC\Files\Node\Node[]
195
-	 */
196
-	public function search($query) {
197
-		return $this->searchCommon('search', array('%' . $query . '%'));
198
-	}
199
-
200
-	/**
201
-	 * search for files by mimetype
202
-	 *
203
-	 * @param string $mimetype
204
-	 * @return Node[]
205
-	 */
206
-	public function searchByMime($mimetype) {
207
-		return $this->searchCommon('searchByMime', array($mimetype));
208
-	}
209
-
210
-	/**
211
-	 * search for files by tag
212
-	 *
213
-	 * @param string|int $tag name or tag id
214
-	 * @param string $userId owner of the tags
215
-	 * @return Node[]
216
-	 */
217
-	public function searchByTag($tag, $userId) {
218
-		return $this->searchCommon('searchByTag', array($tag, $userId));
219
-	}
220
-
221
-	/**
222
-	 * @param string $method cache method
223
-	 * @param array $args call args
224
-	 * @return \OC\Files\Node\Node[]
225
-	 */
226
-	private function searchCommon($method, $args) {
227
-		$files = array();
228
-		$rootLength = strlen($this->path);
229
-		$mount = $this->root->getMount($this->path);
230
-		$storage = $mount->getStorage();
231
-		$internalPath = $mount->getInternalPath($this->path);
232
-		$internalPath = rtrim($internalPath, '/');
233
-		if ($internalPath !== '') {
234
-			$internalPath = $internalPath . '/';
235
-		}
236
-		$internalRootLength = strlen($internalPath);
237
-
238
-		$cache = $storage->getCache('');
239
-
240
-		$results = call_user_func_array(array($cache, $method), $args);
241
-		foreach ($results as $result) {
242
-			if ($internalRootLength === 0 or substr($result['path'], 0, $internalRootLength) === $internalPath) {
243
-				$result['internalPath'] = $result['path'];
244
-				$result['path'] = substr($result['path'], $internalRootLength);
245
-				$result['storage'] = $storage;
246
-				$files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
247
-			}
248
-		}
249
-
250
-		$mounts = $this->root->getMountsIn($this->path);
251
-		foreach ($mounts as $mount) {
252
-			$storage = $mount->getStorage();
253
-			if ($storage) {
254
-				$cache = $storage->getCache('');
255
-
256
-				$relativeMountPoint = substr($mount->getMountPoint(), $rootLength);
257
-				$results = call_user_func_array(array($cache, $method), $args);
258
-				foreach ($results as $result) {
259
-					$result['internalPath'] = $result['path'];
260
-					$result['path'] = $relativeMountPoint . $result['path'];
261
-					$result['storage'] = $storage;
262
-					$files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
263
-				}
264
-			}
265
-		}
266
-
267
-		return array_map(function (FileInfo $file) {
268
-			return $this->createNode($file->getPath(), $file);
269
-		}, $files);
270
-	}
271
-
272
-	/**
273
-	 * @param int $id
274
-	 * @return \OC\Files\Node\Node[]
275
-	 */
276
-	public function getById($id) {
277
-		$mountCache = $this->root->getUserMountCache();
278
-		$mountsContainingFile = $mountCache->getMountsForFileId((int)$id);
279
-		$mounts = $this->root->getMountsIn($this->path);
280
-		$mounts[] = $this->root->getMount($this->path);
281
-		/** @var IMountPoint[] $folderMounts */
282
-		$folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) {
283
-			return $mountPoint->getMountPoint();
284
-		}, $mounts), $mounts);
285
-
286
-		/** @var ICachedMountInfo[] $mountsContainingFile */
287
-		$mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
288
-			return isset($folderMounts[$cachedMountInfo->getMountPoint()]);
289
-		}));
290
-
291
-		if (count($mountsContainingFile) === 0) {
292
-			return [];
293
-		}
294
-
295
-		// we only need to get the cache info once, since all mounts we found point to the same storage
296
-
297
-		$mount = $folderMounts[$mountsContainingFile[0]->getMountPoint()];
298
-		$cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
299
-		if (!$cacheEntry) {
300
-			return [];
301
-		}
302
-		// cache jails will hide the "true" internal path
303
-		$internalPath = ltrim($mountsContainingFile[0]->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/');
304
-
305
-		$nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($cacheEntry, $folderMounts, $internalPath) {
306
-			$mount = $folderMounts[$cachedMountInfo->getMountPoint()];
307
-			$pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath()));
308
-			$pathRelativeToMount = ltrim($pathRelativeToMount, '/');
309
-			$absolutePath = $cachedMountInfo->getMountPoint() . $pathRelativeToMount;
310
-			return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
311
-				$absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
312
-				\OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
313
-			));
314
-		}, $mountsContainingFile);
315
-
316
-		return array_filter($nodes, function (Node $node) {
317
-			return $this->getRelativePath($node->getPath());
318
-		});
319
-	}
320
-
321
-	public function getFreeSpace() {
322
-		return $this->view->free_space($this->path);
323
-	}
324
-
325
-	public function delete() {
326
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
327
-			$this->sendHooks(array('preDelete'));
328
-			$fileInfo = $this->getFileInfo();
329
-			$this->view->rmdir($this->path);
330
-			$nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
331
-			$this->root->emit('\OC\Files', 'postDelete', array($nonExisting));
332
-			$this->exists = false;
333
-		} else {
334
-			throw new NotPermittedException('No delete permission for path');
335
-		}
336
-	}
337
-
338
-	/**
339
-	 * Add a suffix to the name in case the file exists
340
-	 *
341
-	 * @param string $name
342
-	 * @return string
343
-	 * @throws NotPermittedException
344
-	 */
345
-	public function getNonExistingName($name) {
346
-		$uniqueName = \OC_Helper::buildNotExistingFileNameForView($this->getPath(), $name, $this->view);
347
-		return trim($this->getRelativePath($uniqueName), '/');
348
-	}
349
-
350
-	/**
351
-	 * @param int $limit
352
-	 * @param int $offset
353
-	 * @return \OCP\Files\Node[]
354
-	 */
355
-	public function getRecent($limit, $offset = 0) {
356
-		$mimetypeLoader = \OC::$server->getMimeTypeLoader();
357
-		$mounts = $this->root->getMountsIn($this->path);
358
-		$mounts[] = $this->getMountPoint();
359
-
360
-		$mounts = array_filter($mounts, function (IMountPoint $mount) {
361
-			return $mount->getStorage();
362
-		});
363
-		$storageIds = array_map(function (IMountPoint $mount) {
364
-			return $mount->getStorage()->getCache()->getNumericStorageId();
365
-		}, $mounts);
366
-		/** @var IMountPoint[] $mountMap */
367
-		$mountMap = array_combine($storageIds, $mounts);
368
-		$folderMimetype = $mimetypeLoader->getId(FileInfo::MIMETYPE_FOLDER);
369
-
370
-		//todo look into options of filtering path based on storage id (only search in files/ for home storage, filter by share root for shared, etc)
371
-
372
-		$builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
373
-		$query = $builder
374
-			->select('f.*')
375
-			->from('filecache', 'f')
376
-			->andWhere($builder->expr()->in('f.storage', $builder->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)))
377
-			->andWhere($builder->expr()->orX(
378
-			// handle non empty folders separate
379
-				$builder->expr()->neq('f.mimetype', $builder->createNamedParameter($folderMimetype, IQueryBuilder::PARAM_INT)),
380
-				$builder->expr()->eq('f.size', new Literal(0))
381
-			))
382
-			->orderBy('f.mtime', 'DESC')
383
-			->setMaxResults($limit)
384
-			->setFirstResult($offset);
385
-
386
-		$result = $query->execute()->fetchAll();
387
-
388
-		$files = array_filter(array_map(function (array $entry) use ($mountMap, $mimetypeLoader) {
389
-			$mount = $mountMap[$entry['storage']];
390
-			$entry['internalPath'] = $entry['path'];
391
-			$entry['mimetype'] = $mimetypeLoader->getMimetypeById($entry['mimetype']);
392
-			$entry['mimepart'] = $mimetypeLoader->getMimetypeById($entry['mimepart']);
393
-			$path = $this->getAbsolutePath($mount, $entry['path']);
394
-			if (is_null($path)) {
395
-				return null;
396
-			}
397
-			$fileInfo = new \OC\Files\FileInfo($path, $mount->getStorage(), $entry['internalPath'], $entry, $mount);
398
-			return $this->root->createNode($fileInfo->getPath(), $fileInfo);
399
-		}, $result));
400
-
401
-		return array_values(array_filter($files, function (Node $node) {
402
-			$relative = $this->getRelativePath($node->getPath());
403
-			return $relative !== null && $relative !== '/';
404
-		}));
405
-	}
406
-
407
-	private function getAbsolutePath(IMountPoint $mount, $path) {
408
-		$storage = $mount->getStorage();
409
-		if ($storage->instanceOfStorage('\OC\Files\Storage\Wrapper\Jail')) {
410
-			/** @var \OC\Files\Storage\Wrapper\Jail $storage */
411
-			$jailRoot = $storage->getSourcePath('');
412
-			$rootLength = strlen($jailRoot) + 1;
413
-			if ($path === $jailRoot) {
414
-				return $mount->getMountPoint();
415
-			} else if (substr($path, 0, $rootLength) === $jailRoot . '/') {
416
-				return $mount->getMountPoint() . substr($path, $rootLength);
417
-			} else {
418
-				return null;
419
-			}
420
-		} else {
421
-			return $mount->getMountPoint() . $path;
422
-		}
423
-	}
38
+    /**
39
+     * Creates a Folder that represents a non-existing path
40
+     *
41
+     * @param string $path path
42
+     * @return string non-existing node class
43
+     */
44
+    protected function createNonExistingNode($path) {
45
+        return new NonExistingFolder($this->root, $this->view, $path);
46
+    }
47
+
48
+    /**
49
+     * @param string $path path relative to the folder
50
+     * @return string
51
+     * @throws \OCP\Files\NotPermittedException
52
+     */
53
+    public function getFullPath($path) {
54
+        if (!$this->isValidPath($path)) {
55
+            throw new NotPermittedException('Invalid path');
56
+        }
57
+        return $this->path . $this->normalizePath($path);
58
+    }
59
+
60
+    /**
61
+     * @param string $path
62
+     * @return string
63
+     */
64
+    public function getRelativePath($path) {
65
+        if ($this->path === '' or $this->path === '/') {
66
+            return $this->normalizePath($path);
67
+        }
68
+        if ($path === $this->path) {
69
+            return '/';
70
+        } else if (strpos($path, $this->path . '/') !== 0) {
71
+            return null;
72
+        } else {
73
+            $path = substr($path, strlen($this->path));
74
+            return $this->normalizePath($path);
75
+        }
76
+    }
77
+
78
+    /**
79
+     * check if a node is a (grand-)child of the folder
80
+     *
81
+     * @param \OC\Files\Node\Node $node
82
+     * @return bool
83
+     */
84
+    public function isSubNode($node) {
85
+        return strpos($node->getPath(), $this->path . '/') === 0;
86
+    }
87
+
88
+    /**
89
+     * get the content of this directory
90
+     *
91
+     * @throws \OCP\Files\NotFoundException
92
+     * @return Node[]
93
+     */
94
+    public function getDirectoryListing() {
95
+        $folderContent = $this->view->getDirectoryContent($this->path);
96
+
97
+        return array_map(function (FileInfo $info) {
98
+            if ($info->getMimetype() === 'httpd/unix-directory') {
99
+                return new Folder($this->root, $this->view, $info->getPath(), $info);
100
+            } else {
101
+                return new File($this->root, $this->view, $info->getPath(), $info);
102
+            }
103
+        }, $folderContent);
104
+    }
105
+
106
+    /**
107
+     * @param string $path
108
+     * @param FileInfo $info
109
+     * @return File|Folder
110
+     */
111
+    protected function createNode($path, FileInfo $info = null) {
112
+        if (is_null($info)) {
113
+            $isDir = $this->view->is_dir($path);
114
+        } else {
115
+            $isDir = $info->getType() === FileInfo::TYPE_FOLDER;
116
+        }
117
+        if ($isDir) {
118
+            return new Folder($this->root, $this->view, $path, $info);
119
+        } else {
120
+            return new File($this->root, $this->view, $path, $info);
121
+        }
122
+    }
123
+
124
+    /**
125
+     * Get the node at $path
126
+     *
127
+     * @param string $path
128
+     * @return \OC\Files\Node\Node
129
+     * @throws \OCP\Files\NotFoundException
130
+     */
131
+    public function get($path) {
132
+        return $this->root->get($this->getFullPath($path));
133
+    }
134
+
135
+    /**
136
+     * @param string $path
137
+     * @return bool
138
+     */
139
+    public function nodeExists($path) {
140
+        try {
141
+            $this->get($path);
142
+            return true;
143
+        } catch (NotFoundException $e) {
144
+            return false;
145
+        }
146
+    }
147
+
148
+    /**
149
+     * @param string $path
150
+     * @return \OC\Files\Node\Folder
151
+     * @throws \OCP\Files\NotPermittedException
152
+     */
153
+    public function newFolder($path) {
154
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
155
+            $fullPath = $this->getFullPath($path);
156
+            $nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath);
157
+            $this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
158
+            $this->root->emit('\OC\Files', 'preCreate', array($nonExisting));
159
+            $this->view->mkdir($fullPath);
160
+            $node = new Folder($this->root, $this->view, $fullPath);
161
+            $this->root->emit('\OC\Files', 'postWrite', array($node));
162
+            $this->root->emit('\OC\Files', 'postCreate', array($node));
163
+            return $node;
164
+        } else {
165
+            throw new NotPermittedException('No create permission for folder');
166
+        }
167
+    }
168
+
169
+    /**
170
+     * @param string $path
171
+     * @return \OC\Files\Node\File
172
+     * @throws \OCP\Files\NotPermittedException
173
+     */
174
+    public function newFile($path) {
175
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
176
+            $fullPath = $this->getFullPath($path);
177
+            $nonExisting = new NonExistingFile($this->root, $this->view, $fullPath);
178
+            $this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
179
+            $this->root->emit('\OC\Files', 'preCreate', array($nonExisting));
180
+            $this->view->touch($fullPath);
181
+            $node = new File($this->root, $this->view, $fullPath);
182
+            $this->root->emit('\OC\Files', 'postWrite', array($node));
183
+            $this->root->emit('\OC\Files', 'postCreate', array($node));
184
+            return $node;
185
+        } else {
186
+            throw new NotPermittedException('No create permission for path');
187
+        }
188
+    }
189
+
190
+    /**
191
+     * search for files with the name matching $query
192
+     *
193
+     * @param string $query
194
+     * @return \OC\Files\Node\Node[]
195
+     */
196
+    public function search($query) {
197
+        return $this->searchCommon('search', array('%' . $query . '%'));
198
+    }
199
+
200
+    /**
201
+     * search for files by mimetype
202
+     *
203
+     * @param string $mimetype
204
+     * @return Node[]
205
+     */
206
+    public function searchByMime($mimetype) {
207
+        return $this->searchCommon('searchByMime', array($mimetype));
208
+    }
209
+
210
+    /**
211
+     * search for files by tag
212
+     *
213
+     * @param string|int $tag name or tag id
214
+     * @param string $userId owner of the tags
215
+     * @return Node[]
216
+     */
217
+    public function searchByTag($tag, $userId) {
218
+        return $this->searchCommon('searchByTag', array($tag, $userId));
219
+    }
220
+
221
+    /**
222
+     * @param string $method cache method
223
+     * @param array $args call args
224
+     * @return \OC\Files\Node\Node[]
225
+     */
226
+    private function searchCommon($method, $args) {
227
+        $files = array();
228
+        $rootLength = strlen($this->path);
229
+        $mount = $this->root->getMount($this->path);
230
+        $storage = $mount->getStorage();
231
+        $internalPath = $mount->getInternalPath($this->path);
232
+        $internalPath = rtrim($internalPath, '/');
233
+        if ($internalPath !== '') {
234
+            $internalPath = $internalPath . '/';
235
+        }
236
+        $internalRootLength = strlen($internalPath);
237
+
238
+        $cache = $storage->getCache('');
239
+
240
+        $results = call_user_func_array(array($cache, $method), $args);
241
+        foreach ($results as $result) {
242
+            if ($internalRootLength === 0 or substr($result['path'], 0, $internalRootLength) === $internalPath) {
243
+                $result['internalPath'] = $result['path'];
244
+                $result['path'] = substr($result['path'], $internalRootLength);
245
+                $result['storage'] = $storage;
246
+                $files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
247
+            }
248
+        }
249
+
250
+        $mounts = $this->root->getMountsIn($this->path);
251
+        foreach ($mounts as $mount) {
252
+            $storage = $mount->getStorage();
253
+            if ($storage) {
254
+                $cache = $storage->getCache('');
255
+
256
+                $relativeMountPoint = substr($mount->getMountPoint(), $rootLength);
257
+                $results = call_user_func_array(array($cache, $method), $args);
258
+                foreach ($results as $result) {
259
+                    $result['internalPath'] = $result['path'];
260
+                    $result['path'] = $relativeMountPoint . $result['path'];
261
+                    $result['storage'] = $storage;
262
+                    $files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
263
+                }
264
+            }
265
+        }
266
+
267
+        return array_map(function (FileInfo $file) {
268
+            return $this->createNode($file->getPath(), $file);
269
+        }, $files);
270
+    }
271
+
272
+    /**
273
+     * @param int $id
274
+     * @return \OC\Files\Node\Node[]
275
+     */
276
+    public function getById($id) {
277
+        $mountCache = $this->root->getUserMountCache();
278
+        $mountsContainingFile = $mountCache->getMountsForFileId((int)$id);
279
+        $mounts = $this->root->getMountsIn($this->path);
280
+        $mounts[] = $this->root->getMount($this->path);
281
+        /** @var IMountPoint[] $folderMounts */
282
+        $folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) {
283
+            return $mountPoint->getMountPoint();
284
+        }, $mounts), $mounts);
285
+
286
+        /** @var ICachedMountInfo[] $mountsContainingFile */
287
+        $mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
288
+            return isset($folderMounts[$cachedMountInfo->getMountPoint()]);
289
+        }));
290
+
291
+        if (count($mountsContainingFile) === 0) {
292
+            return [];
293
+        }
294
+
295
+        // we only need to get the cache info once, since all mounts we found point to the same storage
296
+
297
+        $mount = $folderMounts[$mountsContainingFile[0]->getMountPoint()];
298
+        $cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
299
+        if (!$cacheEntry) {
300
+            return [];
301
+        }
302
+        // cache jails will hide the "true" internal path
303
+        $internalPath = ltrim($mountsContainingFile[0]->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/');
304
+
305
+        $nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($cacheEntry, $folderMounts, $internalPath) {
306
+            $mount = $folderMounts[$cachedMountInfo->getMountPoint()];
307
+            $pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath()));
308
+            $pathRelativeToMount = ltrim($pathRelativeToMount, '/');
309
+            $absolutePath = $cachedMountInfo->getMountPoint() . $pathRelativeToMount;
310
+            return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
311
+                $absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
312
+                \OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
313
+            ));
314
+        }, $mountsContainingFile);
315
+
316
+        return array_filter($nodes, function (Node $node) {
317
+            return $this->getRelativePath($node->getPath());
318
+        });
319
+    }
320
+
321
+    public function getFreeSpace() {
322
+        return $this->view->free_space($this->path);
323
+    }
324
+
325
+    public function delete() {
326
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
327
+            $this->sendHooks(array('preDelete'));
328
+            $fileInfo = $this->getFileInfo();
329
+            $this->view->rmdir($this->path);
330
+            $nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
331
+            $this->root->emit('\OC\Files', 'postDelete', array($nonExisting));
332
+            $this->exists = false;
333
+        } else {
334
+            throw new NotPermittedException('No delete permission for path');
335
+        }
336
+    }
337
+
338
+    /**
339
+     * Add a suffix to the name in case the file exists
340
+     *
341
+     * @param string $name
342
+     * @return string
343
+     * @throws NotPermittedException
344
+     */
345
+    public function getNonExistingName($name) {
346
+        $uniqueName = \OC_Helper::buildNotExistingFileNameForView($this->getPath(), $name, $this->view);
347
+        return trim($this->getRelativePath($uniqueName), '/');
348
+    }
349
+
350
+    /**
351
+     * @param int $limit
352
+     * @param int $offset
353
+     * @return \OCP\Files\Node[]
354
+     */
355
+    public function getRecent($limit, $offset = 0) {
356
+        $mimetypeLoader = \OC::$server->getMimeTypeLoader();
357
+        $mounts = $this->root->getMountsIn($this->path);
358
+        $mounts[] = $this->getMountPoint();
359
+
360
+        $mounts = array_filter($mounts, function (IMountPoint $mount) {
361
+            return $mount->getStorage();
362
+        });
363
+        $storageIds = array_map(function (IMountPoint $mount) {
364
+            return $mount->getStorage()->getCache()->getNumericStorageId();
365
+        }, $mounts);
366
+        /** @var IMountPoint[] $mountMap */
367
+        $mountMap = array_combine($storageIds, $mounts);
368
+        $folderMimetype = $mimetypeLoader->getId(FileInfo::MIMETYPE_FOLDER);
369
+
370
+        //todo look into options of filtering path based on storage id (only search in files/ for home storage, filter by share root for shared, etc)
371
+
372
+        $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
373
+        $query = $builder
374
+            ->select('f.*')
375
+            ->from('filecache', 'f')
376
+            ->andWhere($builder->expr()->in('f.storage', $builder->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)))
377
+            ->andWhere($builder->expr()->orX(
378
+            // handle non empty folders separate
379
+                $builder->expr()->neq('f.mimetype', $builder->createNamedParameter($folderMimetype, IQueryBuilder::PARAM_INT)),
380
+                $builder->expr()->eq('f.size', new Literal(0))
381
+            ))
382
+            ->orderBy('f.mtime', 'DESC')
383
+            ->setMaxResults($limit)
384
+            ->setFirstResult($offset);
385
+
386
+        $result = $query->execute()->fetchAll();
387
+
388
+        $files = array_filter(array_map(function (array $entry) use ($mountMap, $mimetypeLoader) {
389
+            $mount = $mountMap[$entry['storage']];
390
+            $entry['internalPath'] = $entry['path'];
391
+            $entry['mimetype'] = $mimetypeLoader->getMimetypeById($entry['mimetype']);
392
+            $entry['mimepart'] = $mimetypeLoader->getMimetypeById($entry['mimepart']);
393
+            $path = $this->getAbsolutePath($mount, $entry['path']);
394
+            if (is_null($path)) {
395
+                return null;
396
+            }
397
+            $fileInfo = new \OC\Files\FileInfo($path, $mount->getStorage(), $entry['internalPath'], $entry, $mount);
398
+            return $this->root->createNode($fileInfo->getPath(), $fileInfo);
399
+        }, $result));
400
+
401
+        return array_values(array_filter($files, function (Node $node) {
402
+            $relative = $this->getRelativePath($node->getPath());
403
+            return $relative !== null && $relative !== '/';
404
+        }));
405
+    }
406
+
407
+    private function getAbsolutePath(IMountPoint $mount, $path) {
408
+        $storage = $mount->getStorage();
409
+        if ($storage->instanceOfStorage('\OC\Files\Storage\Wrapper\Jail')) {
410
+            /** @var \OC\Files\Storage\Wrapper\Jail $storage */
411
+            $jailRoot = $storage->getSourcePath('');
412
+            $rootLength = strlen($jailRoot) + 1;
413
+            if ($path === $jailRoot) {
414
+                return $mount->getMountPoint();
415
+            } else if (substr($path, 0, $rootLength) === $jailRoot . '/') {
416
+                return $mount->getMountPoint() . substr($path, $rootLength);
417
+            } else {
418
+                return null;
419
+            }
420
+        } else {
421
+            return $mount->getMountPoint() . $path;
422
+        }
423
+    }
424 424
 }
Please login to merge, or discard this patch.
Spacing   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -54,7 +54,7 @@  discard block
 block discarded – undo
54 54
 		if (!$this->isValidPath($path)) {
55 55
 			throw new NotPermittedException('Invalid path');
56 56
 		}
57
-		return $this->path . $this->normalizePath($path);
57
+		return $this->path.$this->normalizePath($path);
58 58
 	}
59 59
 
60 60
 	/**
@@ -67,7 +67,7 @@  discard block
 block discarded – undo
67 67
 		}
68 68
 		if ($path === $this->path) {
69 69
 			return '/';
70
-		} else if (strpos($path, $this->path . '/') !== 0) {
70
+		} else if (strpos($path, $this->path.'/') !== 0) {
71 71
 			return null;
72 72
 		} else {
73 73
 			$path = substr($path, strlen($this->path));
@@ -82,7 +82,7 @@  discard block
 block discarded – undo
82 82
 	 * @return bool
83 83
 	 */
84 84
 	public function isSubNode($node) {
85
-		return strpos($node->getPath(), $this->path . '/') === 0;
85
+		return strpos($node->getPath(), $this->path.'/') === 0;
86 86
 	}
87 87
 
88 88
 	/**
@@ -94,7 +94,7 @@  discard block
 block discarded – undo
94 94
 	public function getDirectoryListing() {
95 95
 		$folderContent = $this->view->getDirectoryContent($this->path);
96 96
 
97
-		return array_map(function (FileInfo $info) {
97
+		return array_map(function(FileInfo $info) {
98 98
 			if ($info->getMimetype() === 'httpd/unix-directory') {
99 99
 				return new Folder($this->root, $this->view, $info->getPath(), $info);
100 100
 			} else {
@@ -194,7 +194,7 @@  discard block
 block discarded – undo
194 194
 	 * @return \OC\Files\Node\Node[]
195 195
 	 */
196 196
 	public function search($query) {
197
-		return $this->searchCommon('search', array('%' . $query . '%'));
197
+		return $this->searchCommon('search', array('%'.$query.'%'));
198 198
 	}
199 199
 
200 200
 	/**
@@ -231,7 +231,7 @@  discard block
 block discarded – undo
231 231
 		$internalPath = $mount->getInternalPath($this->path);
232 232
 		$internalPath = rtrim($internalPath, '/');
233 233
 		if ($internalPath !== '') {
234
-			$internalPath = $internalPath . '/';
234
+			$internalPath = $internalPath.'/';
235 235
 		}
236 236
 		$internalRootLength = strlen($internalPath);
237 237
 
@@ -243,7 +243,7 @@  discard block
 block discarded – undo
243 243
 				$result['internalPath'] = $result['path'];
244 244
 				$result['path'] = substr($result['path'], $internalRootLength);
245 245
 				$result['storage'] = $storage;
246
-				$files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
246
+				$files[] = new \OC\Files\FileInfo($this->path.'/'.$result['path'], $storage, $result['internalPath'], $result, $mount);
247 247
 			}
248 248
 		}
249 249
 
@@ -257,14 +257,14 @@  discard block
 block discarded – undo
257 257
 				$results = call_user_func_array(array($cache, $method), $args);
258 258
 				foreach ($results as $result) {
259 259
 					$result['internalPath'] = $result['path'];
260
-					$result['path'] = $relativeMountPoint . $result['path'];
260
+					$result['path'] = $relativeMountPoint.$result['path'];
261 261
 					$result['storage'] = $storage;
262
-					$files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
262
+					$files[] = new \OC\Files\FileInfo($this->path.'/'.$result['path'], $storage, $result['internalPath'], $result, $mount);
263 263
 				}
264 264
 			}
265 265
 		}
266 266
 
267
-		return array_map(function (FileInfo $file) {
267
+		return array_map(function(FileInfo $file) {
268 268
 			return $this->createNode($file->getPath(), $file);
269 269
 		}, $files);
270 270
 	}
@@ -275,16 +275,16 @@  discard block
 block discarded – undo
275 275
 	 */
276 276
 	public function getById($id) {
277 277
 		$mountCache = $this->root->getUserMountCache();
278
-		$mountsContainingFile = $mountCache->getMountsForFileId((int)$id);
278
+		$mountsContainingFile = $mountCache->getMountsForFileId((int) $id);
279 279
 		$mounts = $this->root->getMountsIn($this->path);
280 280
 		$mounts[] = $this->root->getMount($this->path);
281 281
 		/** @var IMountPoint[] $folderMounts */
282
-		$folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) {
282
+		$folderMounts = array_combine(array_map(function(IMountPoint $mountPoint) {
283 283
 			return $mountPoint->getMountPoint();
284 284
 		}, $mounts), $mounts);
285 285
 
286 286
 		/** @var ICachedMountInfo[] $mountsContainingFile */
287
-		$mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
287
+		$mountsContainingFile = array_values(array_filter($mountsContainingFile, function(ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
288 288
 			return isset($folderMounts[$cachedMountInfo->getMountPoint()]);
289 289
 		}));
290 290
 
@@ -295,25 +295,25 @@  discard block
 block discarded – undo
295 295
 		// we only need to get the cache info once, since all mounts we found point to the same storage
296 296
 
297 297
 		$mount = $folderMounts[$mountsContainingFile[0]->getMountPoint()];
298
-		$cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
298
+		$cacheEntry = $mount->getStorage()->getCache()->get((int) $id);
299 299
 		if (!$cacheEntry) {
300 300
 			return [];
301 301
 		}
302 302
 		// cache jails will hide the "true" internal path
303
-		$internalPath = ltrim($mountsContainingFile[0]->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/');
303
+		$internalPath = ltrim($mountsContainingFile[0]->getRootInternalPath().'/'.$cacheEntry->getPath(), '/');
304 304
 
305
-		$nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($cacheEntry, $folderMounts, $internalPath) {
305
+		$nodes = array_map(function(ICachedMountInfo $cachedMountInfo) use ($cacheEntry, $folderMounts, $internalPath) {
306 306
 			$mount = $folderMounts[$cachedMountInfo->getMountPoint()];
307 307
 			$pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath()));
308 308
 			$pathRelativeToMount = ltrim($pathRelativeToMount, '/');
309
-			$absolutePath = $cachedMountInfo->getMountPoint() . $pathRelativeToMount;
309
+			$absolutePath = $cachedMountInfo->getMountPoint().$pathRelativeToMount;
310 310
 			return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
311 311
 				$absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
312 312
 				\OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
313 313
 			));
314 314
 		}, $mountsContainingFile);
315 315
 
316
-		return array_filter($nodes, function (Node $node) {
316
+		return array_filter($nodes, function(Node $node) {
317 317
 			return $this->getRelativePath($node->getPath());
318 318
 		});
319 319
 	}
@@ -357,10 +357,10 @@  discard block
 block discarded – undo
357 357
 		$mounts = $this->root->getMountsIn($this->path);
358 358
 		$mounts[] = $this->getMountPoint();
359 359
 
360
-		$mounts = array_filter($mounts, function (IMountPoint $mount) {
360
+		$mounts = array_filter($mounts, function(IMountPoint $mount) {
361 361
 			return $mount->getStorage();
362 362
 		});
363
-		$storageIds = array_map(function (IMountPoint $mount) {
363
+		$storageIds = array_map(function(IMountPoint $mount) {
364 364
 			return $mount->getStorage()->getCache()->getNumericStorageId();
365 365
 		}, $mounts);
366 366
 		/** @var IMountPoint[] $mountMap */
@@ -385,7 +385,7 @@  discard block
 block discarded – undo
385 385
 
386 386
 		$result = $query->execute()->fetchAll();
387 387
 
388
-		$files = array_filter(array_map(function (array $entry) use ($mountMap, $mimetypeLoader) {
388
+		$files = array_filter(array_map(function(array $entry) use ($mountMap, $mimetypeLoader) {
389 389
 			$mount = $mountMap[$entry['storage']];
390 390
 			$entry['internalPath'] = $entry['path'];
391 391
 			$entry['mimetype'] = $mimetypeLoader->getMimetypeById($entry['mimetype']);
@@ -398,7 +398,7 @@  discard block
 block discarded – undo
398 398
 			return $this->root->createNode($fileInfo->getPath(), $fileInfo);
399 399
 		}, $result));
400 400
 
401
-		return array_values(array_filter($files, function (Node $node) {
401
+		return array_values(array_filter($files, function(Node $node) {
402 402
 			$relative = $this->getRelativePath($node->getPath());
403 403
 			return $relative !== null && $relative !== '/';
404 404
 		}));
@@ -412,13 +412,13 @@  discard block
 block discarded – undo
412 412
 			$rootLength = strlen($jailRoot) + 1;
413 413
 			if ($path === $jailRoot) {
414 414
 				return $mount->getMountPoint();
415
-			} else if (substr($path, 0, $rootLength) === $jailRoot . '/') {
416
-				return $mount->getMountPoint() . substr($path, $rootLength);
415
+			} else if (substr($path, 0, $rootLength) === $jailRoot.'/') {
416
+				return $mount->getMountPoint().substr($path, $rootLength);
417 417
 			} else {
418 418
 				return null;
419 419
 			}
420 420
 		} else {
421
-			return $mount->getMountPoint() . $path;
421
+			return $mount->getMountPoint().$path;
422 422
 		}
423 423
 	}
424 424
 }
Please login to merge, or discard this patch.
lib/public/Settings/IIconSection.php 2 patches
Doc Comments   +1 added lines patch added patch discarded remove patch
@@ -33,6 +33,7 @@
 block discarded – undo
33 33
 	 *
34 34
 	 * @returns string
35 35
 	 * @since 12
36
+	 * @return string
36 37
 	 */
37 38
 	public function getIcon();
38 39
 }
Please login to merge, or discard this patch.
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -27,12 +27,12 @@
 block discarded – undo
27 27
  * @since 12
28 28
  */
29 29
 interface IIconSection extends ISection {
30
-	/**
31
-	 * returns the relative path to an 16*16 icon describing the section.
32
-	 * e.g. '/core/img/places/files.svg'
33
-	 *
34
-	 * @returns string
35
-	 * @since 12
36
-	 */
37
-	public function getIcon();
30
+    /**
31
+     * returns the relative path to an 16*16 icon describing the section.
32
+     * e.g. '/core/img/places/files.svg'
33
+     *
34
+     * @returns string
35
+     * @since 12
36
+     */
37
+    public function getIcon();
38 38
 }
Please login to merge, or discard this patch.