Completed
Push — master ( 0cff70...dba08f )
by Morris
09:32
created

Google   F

Complexity

Total Complexity 140

Size/Duplication

Total Lines 666
Duplicated Lines 8.26 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 140
lcom 1
cbo 12
dl 55
loc 666
rs 3.6595
c 1
b 1
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 24 8
A getId() 0 3 1
C getDriveFile() 0 61 10
A setDriveFile() 0 13 4
A onDuplicateFileDetected() 0 8 1
B getGoogleDocExtension() 12 14 5
A mkdir() 0 19 4
C rmdir() 0 22 7
C opendir() 0 50 11
B stat() 0 23 4
A filetype() 0 16 4
A isUpdatable() 0 8 2
A file_exists() 0 3 1
A unlink() 0 12 3
C rename() 0 47 11
C fopen() 9 70 24
C writeBack() 10 80 10
C getMimeType() 13 24 7
A free_space() 0 4 1
B touch() 11 31 5
A test() 0 6 2
C hasUpdated() 0 62 14
A checkDependencies() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Google often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Google, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @author Adam Williamson <[email protected]>
4
 * @author Arthur Schiwon <[email protected]>
5
 * @author Bart Visscher <[email protected]>
6
 * @author Christopher Schäpers <[email protected]>
7
 * @author Jörn Friedrich Dreyer <[email protected]>
8
 * @author Lukas Reschke <[email protected]>
9
 * @author Michael Gapczynski <[email protected]>
10
 * @author Morris Jobke <[email protected]>
11
 * @author Philipp Kapfer <[email protected]>
12
 * @author Robin Appelman <[email protected]>
13
 * @author Robin McCorkell <[email protected]>
14
 * @author Thomas Müller <[email protected]>
15
 * @author Vincent Petry <[email protected]>
16
 *
17
 * @copyright Copyright (c) 2016, ownCloud, Inc.
18
 * @license AGPL-3.0
19
 *
20
 * This code is free software: you can redistribute it and/or modify
21
 * it under the terms of the GNU Affero General Public License, version 3,
22
 * as published by the Free Software Foundation.
23
 *
24
 * This program is distributed in the hope that it will be useful,
25
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
 * GNU Affero General Public License for more details.
28
 *
29
 * You should have received a copy of the GNU Affero General Public License, version 3,
30
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
31
 *
32
 */
33
34
namespace OCA\Files_External\Lib\Storage;
35
36
use GuzzleHttp\Exception\RequestException;
37
use Icewind\Streams\IteratorDirectory;
38
use Icewind\Streams\RetryWrapper;
39
40
set_include_path(get_include_path().PATH_SEPARATOR.
41
	\OC_App::getAppPath('files_external').'/3rdparty/google-api-php-client/src');
42
require_once 'Google/Client.php';
43
require_once 'Google/Service/Drive.php';
44
45
class Google extends \OC\Files\Storage\Common {
46
47
	private $client;
48
	private $id;
49
	private $service;
50
	private $driveFiles;
51
52
	private static $tempFiles = array();
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
61
	public function __construct($params) {
62
		if (isset($params['configured']) && $params['configured'] === 'true'
63
			&& isset($params['client_id']) && isset($params['client_secret'])
64
			&& isset($params['token'])
65
		) {
66
			$this->client = new \Google_Client();
67
			$this->client->setClientId($params['client_id']);
68
			$this->client->setClientSecret($params['client_secret']);
69
			$this->client->setScopes(array('https://www.googleapis.com/auth/drive'));
70
			$this->client->setAccessToken($params['token']);
71
			// if curl isn't available we're likely to run into
72
			// https://github.com/google/google-api-php-client/issues/59
73
			// - disable gzip to avoid it.
74
			if (!function_exists('curl_version') || !function_exists('curl_exec')) {
75
				$this->client->setClassConfig("Google_Http_Request", "disable_gzip", true);
76
			}
77
			// note: API connection is lazy
78
			$this->service = new \Google_Service_Drive($this->client);
79
			$token = json_decode($params['token'], true);
80
			$this->id = 'google::'.substr($params['client_id'], 0, 30).$token['created'];
81
		} else {
82
			throw new \Exception('Creating Google storage failed');
83
		}
84
	}
85
86
	public function getId() {
87
		return $this->id;
88
	}
89
90
	/**
91
	 * Get the Google_Service_Drive_DriveFile object for the specified path.
92
	 * Returns false on failure.
93
	 * @param string $path
94
	 * @return \Google_Service_Drive_DriveFile|false
95
	 */
96
	private function getDriveFile($path) {
97
		// Remove leading and trailing slashes
98
		$path = trim($path, '/');
99
		if (isset($this->driveFiles[$path])) {
100
			return $this->driveFiles[$path];
101
		} else if ($path === '') {
102
			$root = $this->service->files->get('root');
103
			$this->driveFiles[$path] = $root;
104
			return $root;
105
		} else {
106
			// Google Drive SDK does not have methods for retrieving files by path
107
			// Instead we must find the id of the parent folder of the file
108
			$parentId = $this->getDriveFile('')->getId();
109
			$folderNames = explode('/', $path);
110
			$path = '';
111
			// Loop through each folder of this path to get to the file
112
			foreach ($folderNames as $name) {
113
				// Reconstruct path from beginning
114
				if ($path === '') {
115
					$path .= $name;
116
				} else {
117
					$path .= '/'.$name;
118
				}
119
				if (isset($this->driveFiles[$path])) {
120
					$parentId = $this->driveFiles[$path]->getId();
121
				} else {
122
					$q = "title='" . str_replace("'","\\'", $name) . "' and '" . str_replace("'","\\'", $parentId) . "' in parents and trashed = false";
123
					$result = $this->service->files->listFiles(array('q' => $q))->getItems();
124
					if (!empty($result)) {
125
						// Google Drive allows files with the same name, ownCloud doesn't
126
						if (count($result) > 1) {
127
							$this->onDuplicateFileDetected($path);
128
							return false;
129
						} else {
130
							$file = current($result);
131
							$this->driveFiles[$path] = $file;
132
							$parentId = $file->getId();
133
						}
134
					} else {
135
						// Google Docs have no extension in their title, so try without extension
136
						$pos = strrpos($path, '.');
137
						if ($pos !== false) {
138
							$pathWithoutExt = substr($path, 0, $pos);
139
							$file = $this->getDriveFile($pathWithoutExt);
140
							if ($file) {
141
								// Switch cached Google_Service_Drive_DriveFile to the correct index
142
								unset($this->driveFiles[$pathWithoutExt]);
143
								$this->driveFiles[$path] = $file;
144
								$parentId = $file->getId();
145
							} else {
146
								return false;
147
							}
148
						} else {
149
							return false;
150
						}
151
					}
152
				}
153
			}
154
			return $this->driveFiles[$path];
155
		}
156
	}
157
158
	/**
159
	 * Set the Google_Service_Drive_DriveFile object in the cache
160
	 * @param string $path
161
	 * @param Google_Service_Drive_DriveFile|false $file
162
	 */
163
	private function setDriveFile($path, $file) {
164
		$path = trim($path, '/');
165
		$this->driveFiles[$path] = $file;
166
		if ($file === false) {
167
			// Set all child paths as false
168
			$len = strlen($path);
169
			foreach ($this->driveFiles as $key => $file) {
170
				if (substr($key, 0, $len) === $path) {
171
					$this->driveFiles[$key] = false;
172
				}
173
			}
174
		}
175
	}
176
177
	/**
178
	 * Write a log message to inform about duplicate file names
179
	 * @param string $path
180
	 */
181
	private function onDuplicateFileDetected($path) {
182
		$about = $this->service->about->get();
183
		$user = $about->getName();
184
		\OCP\Util::writeLog('files_external',
185
			'Ignoring duplicate file name: '.$path.' on Google Drive for Google user: '.$user,
186
			\OCP\Util::INFO
187
		);
188
	}
189
190
	/**
191
	 * Generate file extension for a Google Doc, choosing Open Document formats for download
192
	 * @param string $mimetype
193
	 * @return string
194
	 */
195
	private function getGoogleDocExtension($mimetype) {
196 View Code Duplication
		if ($mimetype === self::DOCUMENT) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197
			return 'odt';
198
		} else if ($mimetype === self::SPREADSHEET) {
199
			return 'ods';
200
		} else if ($mimetype === self::DRAWING) {
201
			return 'jpg';
202
		} else if ($mimetype === self::PRESENTATION) {
203
			// Download as .odp is not available
204
			return 'pdf';
205
		} else {
206
			return '';
207
		}
208
	}
209
210
	public function mkdir($path) {
211
		if (!$this->is_dir($path)) {
212
			$parentFolder = $this->getDriveFile(dirname($path));
213
			if ($parentFolder) {
214
				$folder = new \Google_Service_Drive_DriveFile();
215
				$folder->setTitle(basename($path));
216
				$folder->setMimeType(self::FOLDER);
217
				$parent = new \Google_Service_Drive_ParentReference();
218
				$parent->setId($parentFolder->getId());
219
				$folder->setParents(array($parent));
220
				$result = $this->service->files->insert($folder);
221
				if ($result) {
222
					$this->setDriveFile($path, $result);
223
				}
224
				return (bool)$result;
225
			}
226
		}
227
		return false;
228
	}
229
230
	public function rmdir($path) {
231
		if (!$this->isDeletable($path)) {
232
			return false;
233
		}
234
		if (trim($path, '/') === '') {
235
			$dir = $this->opendir($path);
236
			if(is_resource($dir)) {
237
				while (($file = readdir($dir)) !== false) {
238
					if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
239
						if (!$this->unlink($path.'/'.$file)) {
240
							return false;
241
						}
242
					}
243
				}
244
				closedir($dir);
245
			}
246
			$this->driveFiles = array();
247
			return true;
248
		} else {
249
			return $this->unlink($path);
250
		}
251
	}
252
253
	public function opendir($path) {
254
		$folder = $this->getDriveFile($path);
255
		if ($folder) {
256
			$files = array();
257
			$duplicates = array();
258
			$pageToken = true;
259
			while ($pageToken) {
260
				$params = array();
261
				if ($pageToken !== true) {
262
					$params['pageToken'] = $pageToken;
263
				}
264
				$params['q'] = "'" . str_replace("'","\\'", $folder->getId()) . "' in parents and trashed = false";
265
				$children = $this->service->files->listFiles($params);
266
				foreach ($children->getItems() as $child) {
267
					$name = $child->getTitle();
268
					// Check if this is a Google Doc i.e. no extension in name
269
					$extension = $child->getFileExtension();
270
					if (empty($extension)
271
						&& $child->getMimeType() !== self::FOLDER
272
					) {
273
						$name .= '.'.$this->getGoogleDocExtension($child->getMimeType());
274
					}
275
					if ($path === '') {
276
						$filepath = $name;
277
					} else {
278
						$filepath = $path.'/'.$name;
279
					}
280
					// Google Drive allows files with the same name, ownCloud doesn't
281
					// Prevent opendir() from returning any duplicate files
282
					$key = array_search($name, $files);
283
					if ($key !== false || isset($duplicates[$filepath])) {
284
						if (!isset($duplicates[$filepath])) {
285
							$duplicates[$filepath] = true;
286
							$this->setDriveFile($filepath, false);
287
							unset($files[$key]);
288
							$this->onDuplicateFileDetected($filepath);
289
						}
290
					} else {
291
						// Cache the Google_Service_Drive_DriveFile for future use
292
						$this->setDriveFile($filepath, $child);
293
						$files[] = $name;
294
					}
295
				}
296
				$pageToken = $children->getNextPageToken();
297
			}
298
			return IteratorDirectory::wrap($files);
299
		} else {
300
			return false;
301
		}
302
	}
303
304
	public function stat($path) {
305
		$file = $this->getDriveFile($path);
306
		if ($file) {
307
			$stat = array();
308
			if ($this->filetype($path) === 'dir') {
309
				$stat['size'] = 0;
310
			} else {
311
				// Check if this is a Google Doc
312
				if ($this->getMimeType($path) !== $file->getMimeType()) {
313
					// Return unknown file size
314
					$stat['size'] = \OCP\Files\FileInfo::SPACE_UNKNOWN;
315
				} else {
316
					$stat['size'] = $file->getFileSize();
317
				}
318
			}
319
			$stat['atime'] = strtotime($file->getLastViewedByMeDate());
320
			$stat['mtime'] = strtotime($file->getModifiedDate());
321
			$stat['ctime'] = strtotime($file->getCreatedDate());
322
			return $stat;
323
		} else {
324
			return false;
325
		}
326
	}
327
328
	public function filetype($path) {
329
		if ($path === '') {
330
			return 'dir';
331
		} else {
332
			$file = $this->getDriveFile($path);
333
			if ($file) {
334
				if ($file->getMimeType() === self::FOLDER) {
335
					return 'dir';
336
				} else {
337
					return 'file';
338
				}
339
			} else {
340
				return false;
341
			}
342
		}
343
	}
344
345
	public function isUpdatable($path) {
346
		$file = $this->getDriveFile($path);
347
		if ($file) {
348
			return $file->getEditable();
349
		} else {
350
			return false;
351
		}
352
	}
353
354
	public function file_exists($path) {
355
		return (bool)$this->getDriveFile($path);
356
	}
357
358
	public function unlink($path) {
359
		$file = $this->getDriveFile($path);
360
		if ($file) {
361
			$result = $this->service->files->trash($file->getId());
362
			if ($result) {
363
				$this->setDriveFile($path, false);
364
			}
365
			return (bool)$result;
366
		} else {
367
			return false;
368
		}
369
	}
370
371
	public function rename($path1, $path2) {
372
		$file = $this->getDriveFile($path1);
373
		if ($file) {
374
			$newFile = $this->getDriveFile($path2);
375
			if (dirname($path1) === dirname($path2)) {
376
				if ($newFile) {
377
					// rename to the name of the target file, could be an office file without extension
378
					$file->setTitle($newFile->getTitle());
379
				} else {
380
					$file->setTitle(basename(($path2)));
381
				}
382
			} else {
383
				// Change file parent
384
				$parentFolder2 = $this->getDriveFile(dirname($path2));
385
				if ($parentFolder2) {
386
					$parent = new \Google_Service_Drive_ParentReference();
387
					$parent->setId($parentFolder2->getId());
388
					$file->setParents(array($parent));
389
				} else {
390
					return false;
391
				}
392
			}
393
			// We need to get the object for the existing file with the same
394
			// name (if there is one) before we do the patch. If oldfile
395
			// exists and is a directory we have to delete it before we
396
			// do the rename too.
397
			$oldfile = $this->getDriveFile($path2);
398
			if ($oldfile && $this->is_dir($path2)) {
399
				$this->rmdir($path2);
400
				$oldfile = false;
401
			}
402
			$result = $this->service->files->patch($file->getId(), $file);
403
			if ($result) {
404
				$this->setDriveFile($path1, false);
405
				$this->setDriveFile($path2, $result);
406
				if ($oldfile && $newFile) {
407
					// only delete if they have a different id (same id can happen for part files)
408
					if ($newFile->getId() !== $oldfile->getId()) {
409
						$this->service->files->delete($oldfile->getId());
410
					}
411
				}
412
			}
413
			return (bool)$result;
414
		} else {
415
			return false;
416
		}
417
	}
418
419
	public function fopen($path, $mode) {
420
		$pos = strrpos($path, '.');
421
		if ($pos !== false) {
422
			$ext = substr($path, $pos);
423
		} else {
424
			$ext = '';
425
		}
426
		switch ($mode) {
427
			case 'r':
428
			case 'rb':
429
				$file = $this->getDriveFile($path);
430
				if ($file) {
431
					$exportLinks = $file->getExportLinks();
432
					$mimetype = $this->getMimeType($path);
433
					$downloadUrl = null;
0 ignored issues
show
Unused Code introduced by
$downloadUrl is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
434
					if ($exportLinks && isset($exportLinks[$mimetype])) {
435
						$downloadUrl = $exportLinks[$mimetype];
436
					} else {
437
						$downloadUrl = $file->getDownloadUrl();
438
					}
439
					if (isset($downloadUrl)) {
440
						$request = new \Google_Http_Request($downloadUrl, 'GET', null, null);
441
						$httpRequest = $this->client->getAuth()->sign($request);
442
						// the library's service doesn't support streaming, so we use Guzzle instead
443
						$client = \OC::$server->getHTTPClientService()->newClient();
444
						try {
445
							$response = $client->get($downloadUrl, [
446
								'headers' => $httpRequest->getRequestHeaders(),
447
								'stream' => true,
448
								'verify' => __DIR__ . '/../3rdparty/google-api-php-client/src/Google/IO/cacerts.pem',
449
							]);
450
						} catch (RequestException $e) {
0 ignored issues
show
Bug introduced by
The class GuzzleHttp\Exception\RequestException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
451 View Code Duplication
							if(!is_null($e->getResponse())) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
452
								if ($e->getResponse()->getStatusCode() === 404) {
453
									return false;
454
								} else {
455
									throw $e;
456
								}
457
							} else {
458
								throw $e;
459
							}
460
						}
461
462
						$handle = $response->getBody();
463
						return RetryWrapper::wrap($handle);
464
					}
465
				}
466
				return false;
467
			case 'w':
468
			case 'wb':
469
			case 'a':
470
			case 'ab':
471
			case 'r+':
472
			case 'w+':
473
			case 'wb+':
474
			case 'a+':
475
			case 'x':
476
			case 'x+':
477
			case 'c':
478
			case 'c+':
479
				$tmpFile = \OCP\Files::tmpFile($ext);
480
				\OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
481
				if ($this->file_exists($path)) {
482
					$source = $this->fopen($path, 'rb');
483
					file_put_contents($tmpFile, $source);
484
				}
485
				self::$tempFiles[$tmpFile] = $path;
486
				return fopen('close://'.$tmpFile, $mode);
487
		}
488
	}
489
490
	public function writeBack($tmpFile) {
491
		if (isset(self::$tempFiles[$tmpFile])) {
492
			$path = self::$tempFiles[$tmpFile];
493
			$parentFolder = $this->getDriveFile(dirname($path));
494
			if ($parentFolder) {
495
				$mimetype = \OC::$server->getMimeTypeDetector()->detect($tmpFile);
496
				$params = array(
497
					'mimeType' => $mimetype,
498
					'uploadType' => 'media'
499
				);
500
				$result = false;
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
501
502
				$chunkSizeBytes = 10 * 1024 * 1024;
503
504
				$useChunking = false;
505
				$size = filesize($tmpFile);
506
				if ($size > $chunkSizeBytes) {
507
					$useChunking = true;
508
				} else {
509
					$params['data'] = file_get_contents($tmpFile);
510
				}
511
512
				if ($this->file_exists($path)) {
513
					$file = $this->getDriveFile($path);
514
					$this->client->setDefer($useChunking);
515
					$request = $this->service->files->update($file->getId(), $file, $params);
516 View Code Duplication
				} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
517
					$file = new \Google_Service_Drive_DriveFile();
518
					$file->setTitle(basename($path));
519
					$file->setMimeType($mimetype);
520
					$parent = new \Google_Service_Drive_ParentReference();
521
					$parent->setId($parentFolder->getId());
522
					$file->setParents(array($parent));
523
					$this->client->setDefer($useChunking);
524
					$request = $this->service->files->insert($file, $params);
525
				}
526
527
				if ($useChunking) {
528
					// Create a media file upload to represent our upload process.
529
					$media = new \Google_Http_MediaFileUpload(
530
						$this->client,
531
						$request,
532
						'text/plain',
533
						null,
534
						true,
535
						$chunkSizeBytes
536
					);
537
					$media->setFileSize($size);
538
539
					// Upload the various chunks. $status will be false until the process is
540
					// complete.
541
					$status = false;
542
					$handle = fopen($tmpFile, 'rb');
543
					while (!$status && !feof($handle)) {
544
						$chunk = fread($handle, $chunkSizeBytes);
545
						$status = $media->nextChunk($chunk);
546
					}
547
548
					// The final value of $status will be the data from the API for the object
549
					// that has been uploaded.
550
					$result = false;
551
					if ($status !== false) {
552
						$result = $status;
553
					}
554
555
					fclose($handle);
556
				} else {
557
					$result = $request;
558
				}
559
560
				// Reset to the client to execute requests immediately in the future.
561
				$this->client->setDefer(false);
562
563
				if ($result) {
564
					$this->setDriveFile($path, $result);
565
				}
566
			}
567
			unlink($tmpFile);
568
		}
569
	}
570
571
	public function getMimeType($path) {
572
		$file = $this->getDriveFile($path);
573
		if ($file) {
574
			$mimetype = $file->getMimeType();
575
			// Convert Google Doc mimetypes, choosing Open Document formats for download
576
			if ($mimetype === self::FOLDER) {
577
				return 'httpd/unix-directory';
578 View Code Duplication
			} else if ($mimetype === self::DOCUMENT) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
579
				return 'application/vnd.oasis.opendocument.text';
580
			} else if ($mimetype === self::SPREADSHEET) {
581
				return 'application/x-vnd.oasis.opendocument.spreadsheet';
582
			} else if ($mimetype === self::DRAWING) {
583
				return 'image/jpeg';
584
			} else if ($mimetype === self::PRESENTATION) {
585
				// Download as .odp is not available
586
				return 'application/pdf';
587
			} else {
588
				// use extension-based detection, could be an encrypted file
589
				return parent::getMimeType($path);
590
			}
591
		} else {
592
			return false;
593
		}
594
	}
595
596
	public function free_space($path) {
597
		$about = $this->service->about->get();
598
		return $about->getQuotaBytesTotal() - $about->getQuotaBytesUsed();
0 ignored issues
show
Bug Compatibility introduced by
The expression $about->getQuotaBytesTot...t->getQuotaBytesUsed(); of type integer|double adds the type double to the return on line 598 which is incompatible with the return type declared by the interface OCP\Files\Storage::free_space of type integer|false.
Loading history...
599
	}
600
601
	public function touch($path, $mtime = null) {
602
		$file = $this->getDriveFile($path);
603
		$result = false;
604
		if ($file) {
605
			if (isset($mtime)) {
606
				// This is just RFC3339, but frustratingly, GDrive's API *requires*
607
				// the fractions portion be present, while no handy PHP constant
608
				// for RFC3339 or ISO8601 includes it. So we do it ourselves.
609
				$file->setModifiedDate(date('Y-m-d\TH:i:s.uP', $mtime));
610
				$result = $this->service->files->patch($file->getId(), $file, array(
611
					'setModifiedDate' => true,
612
				));
613
			} else {
614
				$result = $this->service->files->touch($file->getId());
615
			}
616 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
617
			$parentFolder = $this->getDriveFile(dirname($path));
618
			if ($parentFolder) {
619
				$file = new \Google_Service_Drive_DriveFile();
620
				$file->setTitle(basename($path));
621
				$parent = new \Google_Service_Drive_ParentReference();
622
				$parent->setId($parentFolder->getId());
623
				$file->setParents(array($parent));
624
				$result = $this->service->files->insert($file);
625
			}
626
		}
627
		if ($result) {
628
			$this->setDriveFile($path, $result);
629
		}
630
		return (bool)$result;
631
	}
632
633
	public function test() {
634
		if ($this->free_space('')) {
635
			return true;
636
		}
637
		return false;
638
	}
639
640
	public function hasUpdated($path, $time) {
641
		$appConfig = \OC::$server->getAppConfig();
642
		if ($this->is_file($path)) {
643
			return parent::hasUpdated($path, $time);
644
		} else {
645
			// Google Drive doesn't change modified times of folders when files inside are updated
646
			// Instead we use the Changes API to see if folders have been updated, and it's a pain
647
			$folder = $this->getDriveFile($path);
648
			if ($folder) {
649
				$result = false;
650
				$folderId = $folder->getId();
651
				$startChangeId = $appConfig->getValue('files_external', $this->getId().'cId');
652
				$params = array(
653
					'includeDeleted' => true,
654
					'includeSubscribed' => true,
655
				);
656
				if (isset($startChangeId)) {
657
					$startChangeId = (int)$startChangeId;
658
					$largestChangeId = $startChangeId;
659
					$params['startChangeId'] = $startChangeId + 1;
660
				} else {
661
					$largestChangeId = 0;
662
				}
663
				$pageToken = true;
664
				while ($pageToken) {
665
					if ($pageToken !== true) {
666
						$params['pageToken'] = $pageToken;
667
					}
668
					$changes = $this->service->changes->listChanges($params);
669
					if ($largestChangeId === 0 || $largestChangeId === $startChangeId) {
670
						$largestChangeId = $changes->getLargestChangeId();
671
					}
672
					if (isset($startChangeId)) {
673
						// Check if a file in this folder has been updated
674
						// There is no way to filter by folder at the API level...
675
						foreach ($changes->getItems() as $change) {
676
							$file = $change->getFile();
677
							if ($file) {
678
								foreach ($file->getParents() as $parent) {
679
									if ($parent->getId() === $folderId) {
680
										$result = true;
681
									// Check if there are changes in different folders
682
									} else if ($change->getId() <= $largestChangeId) {
683
										// Decrement id so this change is fetched when called again
684
										$largestChangeId = $change->getId();
685
										$largestChangeId--;
686
									}
687
								}
688
							}
689
						}
690
						$pageToken = $changes->getNextPageToken();
691
					} else {
692
						// Assuming the initial scan just occurred and changes are negligible
693
						break;
694
					}
695
				}
696
				$appConfig->setValue('files_external', $this->getId().'cId', $largestChangeId);
697
				return $result;
698
			}
699
		}
700
		return false;
701
	}
702
703
	/**
704
	 * check if curl is installed
705
	 */
706
	public static function checkDependencies() {
707
		return true;
708
	}
709
710
}
711