Test Failed
Push — master ( bcee4e...785041 )
by
unknown
20:42 queued 04:46
created

FilesBrowserModule::delete()   B

Complexity

Conditions 8
Paths 10

Size

Total Lines 79
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 45
nc 10
nop 2
dl 0
loc 79
rs 7.9555
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
require_once "plugins/files/php/modules/class.fileslistmodule.php";
4
5
require_once __DIR__ . "/Files/Core/class.exception.php";
6
require_once __DIR__ . "/Files/Backend/class.exception.php";
7
8
require_once __DIR__ . "/Files/Core/class.accountstore.php";
9
require_once __DIR__ . "/Files/Backend/class.backendstore.php";
10
11
require_once __DIR__ . "/Files/Core/Util/class.arrayutil.php";
12
require_once __DIR__ . "/Files/Core/Util/class.logger.php";
13
require_once __DIR__ . "/Files/Core/Util/class.stringutil.php";
14
require_once __DIR__ . "/Files/Core/Util/class.pathutil.php";
15
16
require_once __DIR__ . "/lib/phpfastcache/lib/Phpfastcache/Autoload/Autoload.php";
17
18
use Files\Backend\Exception as BackendException;
19
use Files\Core\Exception as AccountException;
20
use Files\Core\Util\ArrayUtil;
21
use Files\Core\Util\Logger;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Logger. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
22
use Files\Core\Util\PathUtil;
23
use Files\Core\Util\StringUtil;
24
25
/**
26
 * This module handles all list and change requests for the files browser.
27
 *
28
 * @class FilesBrowserModule
29
 * @extends ListModule
30
 */
31
class FilesBrowserModule extends FilesListModule {
32
	public const LOG_CONTEXT = "FilesBrowserModule"; // Context for the Logger
33
34
	/**
35
	 * Creates the notifiers for this module,
36
	 * and register them to the Bus.
37
	 */
38
	public function createNotifiers() {
39
		$GLOBALS["bus"]->registerNotifier('fileshierarchynotifier', REQUEST_ENTRYID);
40
	}
41
42
	/**
43
	 * Executes all the actions in the $data variable.
44
	 * Exception part is used for authentication errors also.
45
	 *
46
	 * @return bool true on success or false on failure
47
	 */
48
	public function execute() {
49
		$result = false;
50
51
		foreach ($this->data as $actionType => $actionData) {
52
			if (isset($actionType)) {
53
				try {
54
					switch ($actionType) {
55
						case "checkifexists":
56
							$records = $actionData["records"];
57
							$destination = isset($actionData["destination"]) ? $actionData["destination"] : false;
58
							$result = $this->checkIfExists($records, $destination);
0 ignored issues
show
Bug introduced by
It seems like $destination can also be of type false; however, parameter $destination of FilesBrowserModule::checkIfExists() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

58
							$result = $this->checkIfExists($records, /** @scrutinizer ignore-type */ $destination);
Loading history...
59
							$response = [];
60
							$response['status'] = true;
61
							$response['duplicate'] = $result;
62
							$this->addActionData($actionType, $response);
63
							$GLOBALS["bus"]->addData($this->getResponseData());
64
							break;
65
66
						case "downloadtotmp":
67
							$result = $this->downloadSelectedFilesToTmp($actionType, $actionData);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $this->downloadSelectedF...ctionType, $actionData) targeting FilesBrowserModule::downloadSelectedFilesToTmp() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
68
							break;
69
70
						case "createdir":
71
							$this->save($actionData);
72
							$result = true;
73
							break;
74
75
						case "rename":
76
							$result = $this->rename($actionType, $actionData);
77
							break;
78
79
						case "uploadtobackend":
80
							$result = $this->uploadToBackend($actionType, $actionData);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $this->uploadToBackend($actionType, $actionData) targeting FilesBrowserModule::uploadToBackend() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
81
							break;
82
83
						case "save":
84
							if ((isset($actionData["props"]["sharedid"]) || isset($actionData["props"]["isshared"])) && (!isset($actionData["props"]["deleted"]) || !isset($actionData["props"]["message_size"]))) {
85
								// JUST IGNORE THIS REQUEST - we don't need to interact with the backend if a share was changed
86
								$response['status'] = true;
87
								$folder = [];
88
								$folder[$actionData['entryid']] = [
89
									'props' => $actionData["props"],
90
									'entryid' => $actionData['entryid'],
91
									'store_entryid' => 'files',
92
									'parent_entryid' => $actionData['parent_entryid'],
93
								];
94
95
								$response['item'] = array_values($folder);
96
								$this->addActionData("update", $response);
97
								$GLOBALS["bus"]->addData($this->getResponseData());
98
99
								break;
100
							}
101
102
							/*
103
							 * The "message_action" object has been set, check the action_type field for
104
							 * the exact action which must be taken.
105
							 * Supported actions:
106
							 *   - move: move record to new folder
107
							 */
108
							if (isset($actionData["message_action"], $actionData["message_action"]["action_type"])) {
109
								switch ($actionData["message_action"]["action_type"]) {
110
									case "move" :
111
										$result = $this->move($actionType, $actionData);
112
										break;
113
114
									default:
115
										// check if we should create something new or edit an existing file/folder
116
										if (isset($actionData["entryid"])) {
117
											$result = $this->rename($actionType, $actionData);
118
										}
119
										else {
120
											$result = $this->save($actionData);
121
										}
122
										break;
123
								}
124
							}
125
							else {
126
								// check if we should create something new or edit an existing file/folder
127
								if (isset($actionData["entryid"])) {
128
									$result = $this->rename($actionType, $actionData);
129
								}
130
								else {
131
									$result = $this->save($actionData);
132
								}
133
							}
134
							break;
135
136
						case "delete":
137
							$result = $this->delete($actionType, $actionData);
138
							break;
139
140
						case "list":
141
							$result = $this->loadFiles($actionType, $actionData);
142
							break;
143
144
						case "loadsharingdetails":
145
							$result = $this->getSharingInformation($actionType, $actionData);
146
							break;
147
148
						case "createnewshare":
149
							$result = $this->createNewShare($actionType, $actionData);
150
							break;
151
152
						case "updateexistingshare":
153
							$result = $this->updateExistingShare($actionType, $actionData);
154
							break;
155
156
						case "deleteexistingshare":
157
							$result = $this->deleteExistingShare($actionType, $actionData);
158
							break;
159
160
						case "updatecache":
161
							$result = $this->updateCache($actionType, $actionData);
162
							break;
163
164
						default:
165
							$this->handleUnknownActionType($actionType);
166
					}
167
				}
168
				catch (MAPIException $e) {
169
					$this->sendFeedback(false, $this->errorDetailsFromException($e));
170
				}
171
				catch (AccountException $e) {
172
					$this->sendFeedback(false, [
173
						'type' => ERROR_GENERAL,
174
						'info' => [
175
							'title' => $e->getTitle(),
176
							'original_message' => $e->getMessage(),
177
							'display_message' => $e->getMessage(),
178
						],
179
					]);
180
				}
181
				catch (BackendException $e) {
182
					$this->sendFeedback(false, [
183
						'type' => ERROR_GENERAL,
184
						'info' => [
185
							'title' => $e->getTitle(),
186
							'original_message' => $e->getMessage(),
187
							'display_message' => $e->getMessage(),
188
							'code' => $e->getCode(),
189
						],
190
					]);
191
				}
192
			}
193
		}
194
195
		return $result;
196
	}
197
198
	/**
199
	 * loads content of current folder - list of folders and files from Files.
200
	 *
201
	 * @param string $actionType name of the current action
202
	 * @param array  $actionData all parameters contained in this request
203
	 *
204
	 * @throws BackendException if the backend request fails
205
	 *
206
	 * @return bool
207
	 */
208
	public function loadFiles($actionType, $actionData) {
209
		$nodeId = $actionData['id'];
210
		$onlyFiles = isset($actionData['only_files']) ? $actionData['only_files'] : false;
211
		$response = [];
212
		$nodes = [];
213
214
		$accountID = $this->accountIDFromNode($nodeId);
215
216
		// check if we are in the ROOT (#R#). If so, display some kind of device/account view.
217
		if (empty($accountID) || !$this->accountStore->getAccount($accountID)) {
218
			$accounts = $this->accountStore->getAllAccounts();
219
			foreach ($accounts as $account) { // we have to load all accounts and their folders
220
				// skip accounts that are not valid
221
				if ($account->getStatus() != \Files\Core\Account::STATUS_OK) {
222
					continue;
223
				}
224
				// build the real node id for this folder
225
				$realNodeId = $nodeId . $account->getId() . "/";
226
227
				$nodes[$realNodeId] = ['props' => [
228
					'id' => rawurldecode($realNodeId),
229
					'folder_id' => rawurldecode($realNodeId),
230
					'path' => $realNodeId,
231
					'filename' => $account->getName(),
232
					'message_size' => -1,
233
					'lastmodified' => -1,
234
					'message_class' => "IPM.Files",
235
					'type' => 0,
236
				],
237
					'entryid' => $this->createId($realNodeId),
238
					'store_entryid' => $this->createId($realNodeId),
239
					'parent_entryid' => $this->createId($realNodeId),
240
				];
241
			}
242
		}
243
		else {
244
			$account = $this->accountStore->getAccount($accountID);
245
246
			// initialize the backend
247
			$initializedBackend = $this->initializeBackend($account, true);
248
249
			$starttime = microtime(true);
250
			$nodes = $this->getFolderContent($nodeId, $initializedBackend, $onlyFiles);
251
			Logger::debug(self::LOG_CONTEXT, "[loadfiles]: getFolderContent took: " . (microtime(true) - $starttime) . " seconds");
252
253
			$nodes = $this->sortFolderContent($nodes, $actionData, false);
254
		}
255
256
		$response["item"] = array_values($nodes);
257
258
		$response['page'] = ["start" => 0, "rowcount" => 50, "totalrowcount" => count($response["item"])];
259
		$response['folder'] = ["content_count" => count($response["item"]), "content_unread" => 0];
260
261
		$this->addActionData($actionType, $response);
262
		$GLOBALS["bus"]->addData($this->getResponseData());
263
264
		return true;
265
	}
266
267
	/**
268
	 * Forms the structure needed for frontend
269
	 * for the list of folders and files.
270
	 *
271
	 * @param string                        $nodeId          the name of the current root directory
272
	 * @param Files\Backend\AbstractBackend $backendInstance
273
	 * @param bool                          $onlyFiles       if true, get only files
274
	 *
275
	 * @throws BackendException if the backend request fails
276
	 *
277
	 * @return array of nodes for current path folder
278
	 */
279
	public function getFolderContent($nodeId, $backendInstance, $onlyFiles = false) {
280
		$nodes = [];
281
282
		// relative node ID. We need to trim off the #R# and account ID
283
		$relNodeId = substr($nodeId, strpos($nodeId, '/'));
284
		$nodeIdPrefix = substr($nodeId, 0, strpos($nodeId, '/'));
285
286
		$accountID = $backendInstance->getAccountID();
287
288
		// remove the trailing slash for the cache key
289
		$cachePath = rtrim($relNodeId, '/');
290
		if ($cachePath === "") {
291
			$cachePath = "/";
292
		}
293
294
		$dir = $this->getCache($accountID, $cachePath);
295
		if (is_null($dir)) {
296
			$dir = $backendInstance->ls($relNodeId);
297
			$this->setCache($accountID, $cachePath, $dir);
298
		}
299
300
		// FIXME: There is something issue with getting sharing information from owncloud.
301
		// check if backend supports sharing and load the information
302
		if ($backendInstance->supports(\Files\Backend\BackendStore::FEATURE_SHARING)) {
303
			Logger::debug(self::LOG_CONTEXT, "Checking for shared folders! ({$relNodeId})");
304
305
			$time_start = microtime(true);
306
307
			/** @var \Files\Backend\iFeatureSharing $backendInstance */
308
			$sharingInfo = $backendInstance->getShares($relNodeId);
309
			$time_end = microtime(true);
310
			$time = $time_end - $time_start;
311
312
			Logger::debug(self::LOG_CONTEXT, "Checking for shared took {$time} s!");
313
		}
314
315
		if ($dir) {
316
			$updateCache = false;
317
			foreach ($dir as $id => $node) {
318
				$type = FILES_FILE;
319
320
				if (strcmp($node['resourcetype'], "collection") == 0) { // we have a folder
321
					$type = FILES_FOLDER;
322
				}
323
324
				if ($type === FILES_FOLDER && $onlyFiles) {
325
					continue;
326
				}
327
328
				// Check if foldernames have a trailing slash, if not, add one!
329
				if ($type === FILES_FOLDER && !StringUtil::endsWith($id, "/")) {
330
					$id .= "/";
331
				}
332
333
				$realID = $nodeIdPrefix . $id;
334
335
				Logger::debug(self::LOG_CONTEXT, "parsing: " . $id . " in base: " . $nodeId);
336
337
				$filename = stringToUTF8Encode(basename($id));
338
339
				$size = $node['getcontentlength'] === null ? -1 : intval($node['getcontentlength']);
340
				$size = $type == FILES_FOLDER ? -1 : $size; // folder's dont have a size
341
342
				$fileid = $node['fileid'] === "-1" ? -1 : intval($node['fileid']);
343
344
				$shared = false;
345
				$sharedid = [];
346
				if (isset($sharingInfo) && count($sharingInfo[$relNodeId]) > 0) {
347
					foreach ($sharingInfo[$relNodeId] as $sid => $sdetails) {
348
						if ($sdetails["path"] == rtrim($id, "/")) {
349
							$shared = true;
350
							$sharedid[] = $sid;
351
						}
352
					}
353
				}
354
355
				$nodeId = stringToUTF8Encode($id);
356
				$dirName = dirname($nodeId, 1);
357
				if ($dirName === '/') {
358
					$path = stringToUTF8Encode($nodeIdPrefix . $dirName);
359
				}
360
				else {
361
					$path = stringToUTF8Encode($nodeIdPrefix . $dirName . '/');
362
				}
363
364
				if (!isset($node['entryid']) || !isset($node['parent_entryid']) || !isset($node['store_entryid'])) {
365
					$entryid = $this->createId($realID);
366
					$parentEntryid = $this->createId($path);
367
					$storeEntryid = $this->createId($nodeIdPrefix . '/');
368
369
					$dir[$id]['entryid'] = $entryid;
370
					$dir[$id]['parent_entryid'] = $parentEntryid;
371
					$dir[$id]['store_entryid'] = $storeEntryid;
372
373
					$updateCache = true;
374
				}
375
				else {
376
					$entryid = $node['entryid'];
377
					$parentEntryid = $node['parent_entryid'];
378
					$storeEntryid = $node['store_entryid'];
379
				}
380
381
				$nodes[$nodeId] = ['props' => [
382
					'folder_id' => stringToUTF8Encode($realID),
383
					'fileid' => $fileid,
384
					'path' => $path,
385
					'filename' => $filename,
386
					'message_size' => $size,
387
					'lastmodified' => strtotime($node['getlastmodified']) * 1000,
388
					'message_class' => "IPM.Files",
389
					'isshared' => $shared,
390
					'sharedid' => $sharedid,
391
					'object_type' => $type,
392
					'type' => $type,
393
				],
394
					'entryid' => $entryid,
395
					'parent_entryid' => $parentEntryid,
396
					'store_entryid' => $storeEntryid,
397
				];
398
			}
399
400
			// Update the cache.
401
			if ($updateCache) {
402
				$this->setCache($accountID, $cachePath, $dir);
403
			}
404
		}
405
		else {
406
			Logger::debug(self::LOG_CONTEXT, "dir was empty");
407
		}
408
409
		return $nodes;
410
	}
411
412
	/**
413
	 * This functions sorts an array of nodes.
414
	 *
415
	 * @param array $nodes   array of nodes to sort
416
	 * @param array $data    all parameters contained in the request
417
	 * @param bool  $navtree parse for navtree or browser
418
	 *
419
	 * @return array of sorted nodes
420
	 */
421
	public function sortFolderContent($nodes, $data, $navtree = false) {
422
		$sortednodes = [];
423
424
		$sortkey = "filename";
425
		$sortdir = "ASC";
426
427
		if (isset($data['sort'])) {
428
			$sortkey = $data['sort'][0]['field'];
429
			$sortdir = $data['sort'][0]['direction'];
430
		}
431
432
		Logger::debug(self::LOG_CONTEXT, "sorting by " . $sortkey . " in direction: " . $sortdir);
433
434
		if ($navtree) {
435
			$sortednodes = ArrayUtil::sort_by_key($nodes, $sortkey, $sortdir);
436
		}
437
		else {
438
			$sortednodes = ArrayUtil::sort_props_by_key($nodes, $sortkey, $sortdir);
439
		}
440
441
		return $sortednodes;
442
	}
443
444
	/**
445
	 * Deletes the selected files on the backend server.
446
	 *
447
	 * @param string $actionType name of the current action
448
	 * @param array  $actionData all parameters contained in this request
449
	 *
450
	 * @throws BackendException if the backend request fails
451
	 *
452
	 * @return bool
453
	 */
454
	private function delete($actionType, $actionData) {
455
		// TODO: function is duplicate of class.hierarchylistmodule.php of delete function.
456
		$result = false;
457
		if (isset($actionData['records']) && is_array($actionData['records'])) {
458
			foreach ($actionData['records'] as $record) {
459
				$nodeId = $record['folder_id'];
460
				$relNodeId = substr($nodeId, strpos($nodeId, '/'));
461
462
				$account = $this->accountFromNode($nodeId);
463
464
				// initialize the backend
465
				$initializedBackend = $this->initializeBackend($account);
466
467
				$result = $initializedBackend->delete($relNodeId);
468
				Logger::debug(self::LOG_CONTEXT, "deleted: " . $nodeId . ", worked: " . $result);
469
470
				// clear the cache
471
				$this->deleteCache($account->getId(), dirname($relNodeId));
472
				$GLOBALS["bus"]->notify(REQUEST_ENTRYID, OBJECT_DELETE, [
473
					"id" => $nodeId,
474
					"folder_id" => $nodeId,
475
					"entryid" => $record['entryid'],
476
					"parent_entryid" => $record["parent_entryid"],
477
					"store_entryid" => $record["store_entryid"],
478
				]);
479
			}
480
481
			$response['status'] = true;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$response was never initialized. Although not strictly required by PHP, it is generally a good practice to add $response = array(); before regardless.
Loading history...
482
			$this->addActionData($actionType, $response);
483
			$GLOBALS["bus"]->addData($this->getResponseData());
484
		}
485
		else {
486
			$nodeId = $actionData['folder_id'];
487
488
			$relNodeId = substr($nodeId, strpos($nodeId, '/'));
489
			$response = [];
490
491
			$account = $this->accountFromNode($nodeId);
492
			$accountId = $account->getId();
493
			// initialize the backend
494
			$initializedBackend = $this->initializeBackend($account);
495
496
			try {
497
				$result = $initializedBackend->delete($relNodeId);
498
			}
499
			catch (\Files\Backend\Exception $e) {
500
				// TODO: this might fails because the file was already deleted.
501
				// fire error message if any other error occurred.
502
				Logger::debug(self::LOG_CONTEXT, "deleted a directory that was no longer available");
503
			}
504
			Logger::debug(self::LOG_CONTEXT, "deleted: " . $nodeId . ", worked: " . $result);
505
506
			// Get old cached data.
507
			$cachedDir = $this->getCache($accountId, dirname($relNodeId));
508
			if (isset($cachedDir[$relNodeId]) && !empty($cachedDir[$relNodeId])) {
509
				// Delete the folder from cached data.
510
				unset($cachedDir[$relNodeId]);
511
			}
512
513
			// clear the cache of parent directory.
514
			$this->deleteCache($accountId, dirname($relNodeId));
515
			// clear the cache of selected directory.
516
			$this->deleteCache($accountId, rtrim($relNodeId, '/'));
517
518
			// Set data in cache.
519
			$this->setCache($accountId, dirname($relNodeId), $cachedDir);
520
521
			$response['status'] = $result ? true : false;
522
			$this->addActionData($actionType, $response);
523
			$GLOBALS["bus"]->addData($this->getResponseData());
524
525
			$GLOBALS["bus"]->notify(REQUEST_ENTRYID, OBJECT_DELETE, [
526
				"entryid" => $actionData["entryid"],
527
				"parent_entryid" => $actionData["parent_entryid"],
528
				"store_entryid" => $actionData["store_entryid"],
529
			]);
530
		}
531
532
		return true;
533
	}
534
535
	/**
536
	 * Moves the selected files on the backend server.
537
	 *
538
	 * @param string $actionType name of the current action
539
	 * @param array  $actionData all parameters contained in this request
540
	 *
541
	 * @return bool if the backend request failed
542
	 */
543
	private function move($actionType, $actionData) {
544
		$dst = rtrim($actionData['message_action']["destination_folder_id"], '/');
545
546
		$overwrite = isset($actionData['message_action']["overwrite"]) ? $actionData['message_action']["overwrite"] : true;
547
		$isFolder = isset($actionData['message_action']["isFolder"]) ? $actionData['message_action']["isFolder"] : false;
548
549
		$pathPostfix = "";
550
		if (substr($actionData['folder_id'], -1) == '/') {
551
			$pathPostfix = "/"; // we have a folder...
552
		}
553
554
		$source = rtrim($actionData['folder_id'], '/');
555
		$fileName = basename($source);
556
		$destination = $dst . '/' . basename($source);
557
558
		// get dst and source account ids
559
		// currently only moving within one account is supported
560
		$srcAccountID = substr($actionData['folder_id'], 3, (strpos($actionData['folder_id'], '/') - 3)); // parse account id from node id
561
		$dstAccountID = substr($actionData['message_action']["destination_folder_id"], 3, (strpos($actionData['message_action']["destination_folder_id"], '/') - 3)); // parse account id from node id
562
563
		if ($srcAccountID !== $dstAccountID) {
564
			$this->sendFeedback(false, [
565
				'type' => ERROR_GENERAL,
566
				'info' => [
567
					'title' => _("Files Plugin"),
568
					'original_message' => _("Moving between accounts is not implemented"),
569
					'display_message' => _("Moving between accounts is not implemented"),
570
				],
571
			]);
572
573
			return false;
574
		}
575
		$relDst = substr($destination, strpos($destination, '/'));
576
		$relSrc = substr($source, strpos($source, '/'));
577
578
		$account = $this->accountFromNode($source);
579
580
		// initialize the backend
581
		$initializedBackend = $this->initializeBackend($account);
582
583
		$result = $initializedBackend->move($relSrc, $relDst, $overwrite);
584
585
		$actionId = $account->getId();
586
		// clear the cache
587
		$this->deleteCache($actionId, dirname($relDst));
588
589
		$cachedFolderName = $relSrc . $pathPostfix;
590
		$this->deleteCache($actionId, $cachedFolderName);
591
592
		$cached = $this->getCache($actionId, dirname($relSrc));
593
		$this->deleteCache($actionId, dirname($relSrc));
594
595
		if (isset($cached[$cachedFolderName]) && !empty($cached[$cachedFolderName])) {
596
			unset($cached[$cachedFolderName]);
597
			$this->setCache($actionId, dirname($relSrc), $cached);
598
		}
599
600
		$response['status'] = !$result ? false : true;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$response was never initialized. Although not strictly required by PHP, it is generally a good practice to add $response = array(); before regardless.
Loading history...
601
602
		/* create the response object */
603
		$folder = [
604
			'props' => [
605
				'folder_id' => ($destination . $pathPostfix),
606
				'path' => $actionData['message_action']["destination_folder_id"],
607
				'filename' => $fileName,
608
				'display_name' => $fileName,
609
				'object_type' => $isFolder ? FILES_FOLDER : FILES_FILE,
610
				'deleted' => !$result ? false : true,
611
			],
612
			'entryid' => $this->createId($destination . $pathPostfix),
613
			'store_entryid' => $actionData['store_entryid'],
614
			'parent_entryid' => $actionData['message_action']['parent_entryid'],
615
		];
616
617
		$response['item'] = $folder;
618
619
		$this->addActionData("update", $response);
620
		$GLOBALS["bus"]->addData($this->getResponseData());
621
622
		// Notify hierarchy only when folder was moved.
623
		if ($isFolder) {
624
			// Send notification to delete folder node in hierarchy.
625
			$GLOBALS["bus"]->notify(REQUEST_ENTRYID, OBJECT_DELETE, [
626
				"entryid" => $actionData["entryid"],
627
				"parent_entryid" => $actionData["parent_entryid"],
628
				"store_entryid" => $actionData["store_entryid"],
629
			]);
630
631
			// Send notification to create new folder node in hierarchy.
632
			$GLOBALS["bus"]->notify(REQUEST_ENTRYID, OBJECT_SAVE, $folder);
633
		}
634
635
		return true;
636
	}
637
638
	/**
639
	 * Renames the selected file on the backend server.
640
	 *
641
	 * @param string $actionType name of the current action
642
	 * @param array  $actionData all parameters contained in this request
643
	 *
644
	 * @throws BackendException if the backend request fails
645
	 *
646
	 * @return bool
647
	 */
648
	public function rename($actionType, $actionData) {
649
		$messageProps = $this->save($actionData);
650
		$notifySubFolders = isset($actionData['message_action']['isFolder']) ? $actionData['message_action']['isFolder'] : true;
651
		if (!empty($messageProps)) {
652
			$GLOBALS["bus"]->notify(REQUEST_ENTRYID, OBJECT_SAVE, $messageProps);
653
			if ($notifySubFolders) {
654
				$this->notifySubFolders($messageProps["props"]["folder_id"]);
655
			}
656
		}
657
	}
658
659
	/**
660
	 * Check if given filename or folder already exists on server.
661
	 *
662
	 * @param array $records     which needs to be check for existence
663
	 * @param array $destination where the given records needs to be moved, uploaded, or renamed
664
	 *
665
	 * @throws BackendException if the backend request fails
666
	 *
667
	 * @return bool True if duplicate found, false otherwise
668
	 */
669
	private function checkIfExists($records, $destination) {
670
		$duplicate = false;
671
672
		if (isset($records) && is_array($records)) {
673
			if (!isset($destination) || $destination == false) {
674
				$destination = reset($records);
675
				$destination = $destination["id"]; // we can only check files in the same folder, so one request will be enough
676
				Logger::debug(self::LOG_CONTEXT, "Resetting destination to check.");
677
			}
678
			Logger::debug(self::LOG_CONTEXT, "Checking: " . $destination);
679
			$account = $this->accountFromNode($destination);
680
681
			// initialize the backend
682
			$initializedBackend = $this->initializeBackend($account);
683
684
			$relDirname = substr($destination, strpos($destination, '/'));
685
			Logger::debug(self::LOG_CONTEXT, "Getting content for: " . $relDirname);
686
687
			try {
688
				$lsdata = $initializedBackend->ls($relDirname); // we can only check files in the same folder, so one request will be enough
689
			}
690
			catch (Exception $e) {
691
				// ignore - if file not found -> does not exist :)
692
			}
693
			if (isset($lsdata) && is_array($lsdata)) {
694
				foreach ($records as $record) {
695
					$relRecId = substr($record["id"], strpos($record["id"], '/'));
696
					Logger::debug(self::LOG_CONTEXT, "Checking rec: " . $relRecId, "Core");
0 ignored issues
show
Unused Code introduced by
The call to Files\Core\Util\Logger::debug() has too many arguments starting with 'Core'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

696
					Logger::/** @scrutinizer ignore-call */ 
697
             debug(self::LOG_CONTEXT, "Checking rec: " . $relRecId, "Core");

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
697
					foreach ($lsdata as $argsid => $args) {
698
						if (strcmp($args['resourcetype'], "collection") == 0 && $record["isFolder"] && strcmp(basename($argsid), basename($relRecId)) == 0) { // we have a folder
699
							Logger::debug(self::LOG_CONTEXT, "Duplicate folder found: " . $argsid, "Core");
700
							$duplicate = true;
701
							break;
702
						}
703
						if (strcmp($args['resourcetype'], "collection") != 0 && !$record["isFolder"] && strcmp(basename($argsid), basename($relRecId)) == 0) {
704
							Logger::debug(self::LOG_CONTEXT, "Duplicate file found: " . $argsid, "Core");
705
							$duplicate = true;
706
							break;
707
						}
708
						$duplicate = false;
709
					}
710
711
					if ($duplicate) {
712
						Logger::debug(self::LOG_CONTEXT, "Duplicate entry: " . $relRecId, "Core");
713
						break;
714
					}
715
				}
716
			}
717
		}
718
719
		return $duplicate;
720
	}
721
722
	/**
723
	 * Downloads file from the Files service and saves it in tmp
724
	 * folder with unique name.
725
	 *
726
	 * @param array $actionData
727
	 * @param mixed $actionType
728
	 *
729
	 * @throws BackendException if the backend request fails
730
	 */
731
	private function downloadSelectedFilesToTmp($actionType, $actionData) {
732
		$ids = $actionData['ids'];
733
		$dialogAttachmentId = $actionData['dialog_attachments'];
734
		$response = [];
735
736
		$attachment_state = new AttachmentState();
737
		$attachment_state->open();
738
739
		$account = $this->accountFromNode($ids[0]);
740
741
		// initialize the backend
742
		$initializedBackend = $this->initializeBackend($account);
743
744
		foreach ($ids as $file) {
745
			$filename = basename($file);
746
			$tmpname = $attachment_state->getAttachmentTmpPath($filename);
747
748
			// download file from the backend
749
			$relRecId = substr($file, strpos($file, '/'));
750
			$http_status = $initializedBackend->get_file($relRecId, $tmpname);
0 ignored issues
show
Unused Code introduced by
The assignment to $http_status is dead and can be removed.
Loading history...
751
752
			$filesize = filesize($tmpname);
753
754
			Logger::debug(self::LOG_CONTEXT, "Downloading: " . $filename . " to: " . $tmpname);
755
756
			$attach_id = uniqid();
757
			$response['items'][] = [
758
				'name' => $filename,
759
				'size' => $filesize,
760
				"attach_id" => $attach_id,
761
				'tmpname' => PathUtil::getFilenameFromPath($tmpname),
762
			];
763
764
			$attachment_state->addAttachmentFile($dialogAttachmentId, PathUtil::getFilenameFromPath($tmpname), [
765
				"name" => $filename,
766
				"size" => $filesize,
767
				"type" => PathUtil::get_mime($tmpname),
768
				"attach_id" => $attach_id,
769
				"sourcetype" => 'default',
770
			]);
771
772
			Logger::debug(self::LOG_CONTEXT, "filesize: " . $filesize);
773
		}
774
775
		$attachment_state->close();
776
		$response['status'] = true;
777
		$this->addActionData($actionType, $response);
778
		$GLOBALS["bus"]->addData($this->getResponseData());
779
	}
780
781
	/**
782
	 * upload the tempfile to files.
783
	 *
784
	 * @param array $actionData
785
	 * @param mixed $actionType
786
	 *
787
	 * @throws BackendException if the backend request fails
788
	 */
789
	private function uploadToBackend($actionType, $actionData) {
790
		Logger::debug(self::LOG_CONTEXT, "preparing attachment");
791
792
		$account = $this->accountFromNode($actionData["destdir"]);
793
794
		// initialize the backend
795
		$initializedBackend = $this->initializeBackend($account);
796
797
		$result = true;
798
799
		if ($actionData["type"] === "attachment") {
800
			foreach ($actionData["items"] as $item) {
801
				list($tmpname, $filename) = $this->prepareAttachmentForUpload($item);
802
803
				$dirName = substr($actionData["destdir"], strpos($actionData["destdir"], '/'));
804
				$filePath = $dirName . $filename;
805
806
				Logger::debug(self::LOG_CONTEXT, "Uploading to: " . $filePath . " tmpfile: " . $tmpname);
807
808
				$result = $result && $initializedBackend->put_file($filePath, $tmpname);
809
				unlink($tmpname);
810
811
				$this->updateDirCache($initializedBackend, $dirName, $filePath, $actionData);
812
			}
813
		}
814
		elseif ($actionData["type"] === "mail") {
815
			foreach ($actionData["items"] as $item) {
816
				list($tmpname, $filename) = $this->prepareEmailForUpload($item);
817
818
				$dirName = substr($actionData["destdir"], strpos($actionData["destdir"], '/'));
819
				$filePath = $dirName . $filename;
820
821
				Logger::debug(self::LOG_CONTEXT, "Uploading to: " . $filePath . " tmpfile: " . $tmpname);
822
823
				$result = $result && $initializedBackend->put_file($filePath, $tmpname);
824
				unlink($tmpname);
825
826
				$this->updateDirCache($initializedBackend, $dirName, $filePath, $actionData);
827
			}
828
		}
829
		else {
830
			$this->sendFeedback(false, [
831
				'type' => ERROR_GENERAL,
832
				'info' => [
833
					'title' => _("Files plugin"),
834
					'original_message' => _("Unknown type - cannot save this file to the Files backend!"),
835
					'display_message' => _("Unknown type - cannot save this file to the Files backend!"),
836
				],
837
			]);
838
		}
839
840
		$response = [];
841
		$response['status'] = $result;
842
		$this->addActionData($actionType, $response);
843
		$GLOBALS["bus"]->addData($this->getResponseData());
844
	}
845
846
	/**
847
	 * Update the cache of selected directory.
848
	 *
849
	 * @param Files\Backend\AbstractBackend $backendInstance
850
	 * @param string                        $dirName         The directory name
851
	 * @param $filePath The file path
852
	 * @param $actionData The action data
853
	 *
854
	 * @throws BackendException
855
	 */
856
	public function updateDirCache($backendInstance, $dirName, $filePath, $actionData) {
857
		$cachePath = rtrim($dirName, '/');
858
		if ($cachePath === "") {
859
			$cachePath = "/";
860
		}
861
862
		$dir = $backendInstance->ls($cachePath);
863
		$accountID = $this->accountIDFromNode($actionData["destdir"]);
864
		$cacheDir = $this->getCache($accountID, $cachePath);
865
		$cacheDir[$filePath] = $dir[$filePath];
866
		$this->setCache($accountID, $cachePath, $cacheDir);
867
	}
868
869
	/**
870
	 * This function will prepare an attachment for the upload to the backend.
871
	 * It will store the attachment to the TMP folder and return its temporary
872
	 * path and filename as array.
873
	 *
874
	 * @param $items
875
	 * @param mixed $item
876
	 *
877
	 * @return array (tmpname, filename) or false on error
878
	 */
879
	private function prepareAttachmentForUpload($item) {
880
		// Check which type isset
881
		$openType = "attachment";
0 ignored issues
show
Unused Code introduced by
The assignment to $openType is dead and can be removed.
Loading history...
882
883
		// Get store id
884
		$storeid = false;
885
		if (isset($item["store"])) {
886
			$storeid = $item["store"];
887
		}
888
889
		// Get message entryid
890
		$entryid = false;
891
		if (isset($item["entryid"])) {
892
			$entryid = $item["entryid"];
893
		}
894
895
		// Get number of attachment which should be opened.
896
		$attachNum = false;
897
		if (isset($item["attachNum"])) {
898
			$attachNum = $item["attachNum"];
899
		}
900
901
		$tmpname = "";
0 ignored issues
show
Unused Code introduced by
The assignment to $tmpname is dead and can be removed.
Loading history...
902
		$filename = "";
0 ignored issues
show
Unused Code introduced by
The assignment to $filename is dead and can be removed.
Loading history...
903
904
		// Check if storeid and entryid isset
905
		if ($storeid && $entryid) {
906
			// Open the store
907
			$store = $GLOBALS["mapisession"]->openMessageStore(hex2bin($storeid));
908
909
			if ($store) {
910
				// Open the message
911
				$message = mapi_msgstore_openentry($store, hex2bin($entryid));
912
913
				if ($message) {
914
					$attachment = false;
915
916
					// Check if attachNum isset
917
					if ($attachNum) {
918
						// Loop through the attachNums, message in message in message ...
919
						for ($i = 0; $i < (count($attachNum) - 1); ++$i) {
920
							// Open the attachment
921
							$tempattach = mapi_message_openattach($message, (int) $attachNum[$i]);
922
							if ($tempattach) {
923
								// Open the object in the attachment
924
								$message = mapi_attach_openobj($tempattach);
925
							}
926
						}
927
928
						// Open the attachment
929
						$attachment = mapi_message_openattach($message, (int) $attachNum[(count($attachNum) - 1)]);
930
					}
931
932
					// Check if the attachment is opened
933
					if ($attachment) {
934
						// Get the props of the attachment
935
						$props = mapi_attach_getprops($attachment, [PR_ATTACH_LONG_FILENAME, PR_ATTACH_MIME_TAG, PR_DISPLAY_NAME, PR_ATTACH_METHOD]);
936
						// Content Type
937
						$contentType = "application/octet-stream";
938
						// Filename
939
						$filename = "ERROR";
940
941
						// Set filename
942
						if (isset($props[PR_ATTACH_LONG_FILENAME])) {
943
							$filename = PathUtil::sanitizeFilename($props[PR_ATTACH_LONG_FILENAME]);
944
						}
945
						else {
946
							if (isset($props[PR_ATTACH_FILENAME])) {
947
								$filename = PathUtil::sanitizeFilename($props[PR_ATTACH_FILENAME]);
948
							}
949
							else {
950
								if (isset($props[PR_DISPLAY_NAME])) {
951
									$filename = PathUtil::sanitizeFilename($props[PR_DISPLAY_NAME]);
952
								}
953
							}
954
						}
955
956
						// Set content type
957
						if (isset($props[PR_ATTACH_MIME_TAG])) {
958
							$contentType = $props[PR_ATTACH_MIME_TAG];
0 ignored issues
show
Unused Code introduced by
The assignment to $contentType is dead and can be removed.
Loading history...
959
						}
960
						else {
961
							// Parse the extension of the filename to get the content type
962
							if (strrpos($filename, ".") !== false) {
963
								$extension = strtolower(substr($filename, strrpos($filename, ".")));
964
								$contentType = "application/octet-stream";
965
								if (is_readable("mimetypes.dat")) {
966
									$fh = fopen("mimetypes.dat", "r");
967
									$ext_found = false;
968
									while (!feof($fh) && !$ext_found) {
969
										$line = fgets($fh);
970
										preg_match("/(\\.[a-z0-9]+)[ \t]+([^ \t\n\r]*)/i", $line, $result);
971
										if ($extension == $result[1]) {
972
											$ext_found = true;
973
											$contentType = $result[2];
974
										}
975
									}
976
									fclose($fh);
977
								}
978
							}
979
						}
980
981
						$tmpname = tempnam(TMP_PATH, stripslashes($filename));
982
983
						// Open a stream to get the attachment data
984
						$stream = mapi_openproperty($attachment, PR_ATTACH_DATA_BIN, IID_IStream, 0, 0);
985
						$stat = mapi_stream_stat($stream);
986
						// File length =  $stat["cb"]
987
988
						Logger::debug(self::LOG_CONTEXT, "filesize: " . $stat["cb"]);
989
990
						$fhandle = fopen($tmpname, 'w');
991
						$buffer = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $buffer is dead and can be removed.
Loading history...
992
						for ($i = 0; $i < $stat["cb"]; $i += BLOCK_SIZE) {
993
							// Write stream
994
							$buffer = mapi_stream_read($stream, BLOCK_SIZE);
995
							fwrite($fhandle, $buffer, strlen($buffer));
996
						}
997
						fclose($fhandle);
998
999
						Logger::debug(self::LOG_CONTEXT, "temp attachment written to " . $tmpname);
1000
1001
						return [$tmpname, $filename];
1002
					}
1003
				}
1004
			}
1005
			else {
1006
				Logger::error(self::LOG_CONTEXT, "store could not be opened");
1007
			}
1008
		}
1009
		else {
1010
			Logger::error(self::LOG_CONTEXT, "wrong call, store and entryid have to be set");
1011
		}
1012
1013
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
1014
	}
1015
1016
	/**
1017
	 * Store the email as eml to a temporary directory and return its temporary filename.
1018
	 *
1019
	 * @param {string} $actionType
0 ignored issues
show
Documentation Bug introduced by
The doc comment {string} at position 0 could not be parsed: Unknown type name '{' at position 0 in {string}.
Loading history...
1020
	 * @param {array} $actionData
1021
	 * @param mixed $item
1022
	 *
1023
	 * @return array (tmpname, filename) or false on error
1024
	 */
1025
	private function prepareEmailForUpload($item) {
1026
		// Get store id
1027
		$storeid = false;
1028
		if (isset($item["store"])) {
1029
			$storeid = $item["store"];
1030
		}
1031
1032
		// Get message entryid
1033
		$entryid = false;
1034
		if (isset($item["entryid"])) {
1035
			$entryid = $item["entryid"];
1036
		}
1037
1038
		$tmpname = "";
0 ignored issues
show
Unused Code introduced by
The assignment to $tmpname is dead and can be removed.
Loading history...
1039
		$filename = "";
1040
1041
		$store = $GLOBALS['mapisession']->openMessageStore(hex2bin($storeid));
1042
		$message = mapi_msgstore_openentry($store, hex2bin($entryid));
1043
1044
		// Decode smime signed messages on this message
1045
		parse_smime($store, $message);
1046
1047
		if ($message && $store) {
1048
			// get message properties.
1049
			$messageProps = mapi_getprops($message, [PR_SUBJECT, PR_MESSAGE_CLASS]);
1050
1051
			$isSupportedMessage = (
1052
				(stripos($messageProps[PR_MESSAGE_CLASS], 'IPM.Note') === 0) ||
1053
				(stripos($messageProps[PR_MESSAGE_CLASS], 'Report.IPM.Note') === 0) ||
1054
				(stripos($messageProps[PR_MESSAGE_CLASS], 'IPM.Schedule') === 0)
1055
			);
1056
1057
			if ($isSupportedMessage) {
1058
				// Get addressbook for current session
1059
				$addrBook = $GLOBALS['mapisession']->getAddressbook();
1060
1061
				// Read the message as RFC822-formatted e-mail stream.
1062
				$stream = mapi_inetmapi_imtoinet($GLOBALS['mapisession']->getSession(), $addrBook, $message, []);
1063
1064
				if (!empty($messageProps[PR_SUBJECT])) {
1065
					$filename = PathUtil::sanitizeFilename($messageProps[PR_SUBJECT]) . '.eml';
1066
				}
1067
				else {
1068
					$filename = _('Untitled') . '.eml';
1069
				}
1070
1071
				$tmpname = tempnam(TMP_PATH, "email2filez");
1072
1073
				// Set the file length
1074
				$stat = mapi_stream_stat($stream);
1075
1076
				$fhandle = fopen($tmpname, 'w');
1077
				$buffer = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $buffer is dead and can be removed.
Loading history...
1078
				for ($i = 0; $i < $stat["cb"]; $i += BLOCK_SIZE) {
1079
					// Write stream
1080
					$buffer = mapi_stream_read($stream, BLOCK_SIZE);
1081
					fwrite($fhandle, $buffer, strlen($buffer));
1082
				}
1083
				fclose($fhandle);
1084
1085
				return [$tmpname, $filename];
1086
			}
1087
		}
1088
1089
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
1090
	}
1091
1092
	/**
1093
	 * Get sharing information from the backend.
1094
	 *
1095
	 * @param $actionType
1096
	 * @param $actionData
1097
	 *
1098
	 * @return bool
1099
	 */
1100
	private function getSharingInformation($actionType, $actionData) {
1101
		$response = [];
1102
		$records = $actionData["records"];
1103
1104
		if (count($records) < 1) {
1105
			$this->sendFeedback(false, [
1106
				'type' => ERROR_GENERAL,
1107
				'info' => [
1108
					'title' => _("Files Plugin"),
1109
					'original_message' => _("No record given!"),
1110
					'display_message' => _("No record given!"),
1111
				],
1112
			]);
1113
		}
1114
1115
		$account = $this->accountFromNode($records[0]);
1116
1117
		// initialize the backend
1118
		$initializedBackend = $this->initializeBackend($account);
1119
1120
		$relRecords = [];
1121
		foreach ($records as $record) {
1122
			$relRecords[] = substr($record, strpos($record, '/')); // remove account id
1123
		}
1124
1125
		try {
1126
			$sInfo = $initializedBackend->sharingDetails($relRecords);
0 ignored issues
show
Bug introduced by
The method sharingDetails() does not exist on Files\Backend\AbstractBackend. It seems like you code against a sub-type of Files\Backend\AbstractBackend such as Files\Backend\Owncloud\Backend. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1126
			/** @scrutinizer ignore-call */ 
1127
   $sInfo = $initializedBackend->sharingDetails($relRecords);
Loading history...
1127
		}
1128
		catch (Exception $e) {
1129
			$response['status'] = false;
1130
			$response['header'] = _('Fetching sharing information failed');
1131
			$response['message'] = $e->getMessage();
1132
			$this->addActionData("error", $response);
1133
			$GLOBALS["bus"]->addData($this->getResponseData());
1134
1135
			return false;
1136
		}
1137
1138
		$sharingInfo = [];
1139
		foreach ($sInfo as $path => $details) {
1140
			$realPath = "#R#" . $account->getId() . $path;
1141
			$sharingInfo[$realPath] = $details; // add account id again
1142
		}
1143
1144
		$response['status'] = true;
1145
		$response['shares'] = $sharingInfo;
1146
		$this->addActionData($actionType, $response);
1147
		$GLOBALS["bus"]->addData($this->getResponseData());
1148
1149
		return true;
1150
	}
1151
1152
	/**
1153
	 * Create a new share.
1154
	 *
1155
	 * @param $actionType
1156
	 * @param $actionData
1157
	 *
1158
	 * @return bool
1159
	 */
1160
	private function createNewShare($actionType, $actionData) {
1161
		$records = $actionData["records"];
1162
		$shareOptions = $actionData["options"];
1163
1164
		if (count($records) < 1) {
1165
			$this->sendFeedback(false, [
1166
				'type' => ERROR_GENERAL,
1167
				'info' => [
1168
					'title' => _("Files Plugin"),
1169
					'original_message' => _("No record given!"),
1170
					'display_message' => _("No record given!"),
1171
				],
1172
			]);
1173
		}
1174
1175
		$account = $this->accountFromNode($records[0]);
1176
1177
		// initialize the backend
1178
		$initializedBackend = $this->initializeBackend($account);
1179
1180
		$sharingRecords = [];
1181
		foreach ($records as $record) {
1182
			$path = substr($record, strpos($record, '/')); // remove account id
1183
			$sharingRecords[$path] = $shareOptions; // add options
1184
		}
1185
1186
		try {
1187
			$sInfo = $initializedBackend->share($sharingRecords);
0 ignored issues
show
Bug introduced by
The method share() does not exist on Files\Backend\AbstractBackend. It seems like you code against a sub-type of Files\Backend\AbstractBackend such as Files\Backend\Owncloud\Backend. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1187
			/** @scrutinizer ignore-call */ 
1188
   $sInfo = $initializedBackend->share($sharingRecords);
Loading history...
1188
		}
1189
		catch (Exception $e) {
1190
			$response['status'] = false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$response was never initialized. Although not strictly required by PHP, it is generally a good practice to add $response = array(); before regardless.
Loading history...
1191
			$response['header'] = _('Sharing failed');
1192
			$response['message'] = $e->getMessage();
1193
			$this->addActionData("error", $response);
1194
			$GLOBALS["bus"]->addData($this->getResponseData());
1195
1196
			return false;
1197
		}
1198
1199
		$sharingInfo = [];
1200
		foreach ($sInfo as $path => $details) {
1201
			$realPath = "#R#" . $account->getId() . $path;
1202
			$sharingInfo[$realPath] = $details; // add account id again
1203
		}
1204
1205
		$response = [];
1206
		$response['status'] = true;
1207
		$response['shares'] = $sharingInfo;
1208
		$this->addActionData($actionType, $response);
1209
		$GLOBALS["bus"]->addData($this->getResponseData());
1210
1211
		return true;
1212
	}
1213
1214
	/**
1215
	 * Update a existing share.
1216
	 *
1217
	 * @param $actionType
1218
	 * @param $actionData
1219
	 *
1220
	 * @return bool
1221
	 */
1222
	private function updateExistingShare($actionType, $actionData) {
1223
		$records = $actionData["records"];
1224
		$accountID = $actionData["accountid"];
1225
		$shareOptions = $actionData["options"];
1226
1227
		if (count($records) < 1) {
1228
			$this->sendFeedback(false, [
1229
				'type' => ERROR_GENERAL,
1230
				'info' => [
1231
					'title' => _("Files Plugin"),
1232
					'original_message' => _("No record given!"),
1233
					'display_message' => _("No record given!"),
1234
				],
1235
			]);
1236
		}
1237
1238
		$account = $this->accountStore->getAccount($accountID);
1239
1240
		// initialize the backend
1241
		$initializedBackend = $this->initializeBackend($account);
1242
1243
		$sharingRecords = [];
1244
		foreach ($records as $record) {
1245
			$sharingRecords[$record] = $shareOptions; // add options
1246
		}
1247
1248
		try {
1249
			$sInfo = $initializedBackend->share($sharingRecords, true);
1250
		}
1251
		catch (Exception $e) {
1252
			$response['status'] = false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$response was never initialized. Although not strictly required by PHP, it is generally a good practice to add $response = array(); before regardless.
Loading history...
1253
			$response['header'] = _('Updating share failed');
1254
			$response['message'] = $e->getMessage();
1255
			$this->addActionData("error", $response);
1256
			$GLOBALS["bus"]->addData($this->getResponseData());
1257
1258
			return false;
1259
		}
1260
1261
		$response = [];
1262
		$response['status'] = true;
1263
		$response['shares'] = $sInfo;
1264
		$this->addActionData($actionType, $response);
1265
		$GLOBALS["bus"]->addData($this->getResponseData());
1266
1267
		return true;
1268
	}
1269
1270
	/**
1271
	 * Delete one or more shares.
1272
	 *
1273
	 * @param $actionType
1274
	 * @param $actionData
1275
	 *
1276
	 * @return bool
1277
	 */
1278
	private function deleteExistingShare($actionType, $actionData) {
1279
		$records = $actionData["records"];
1280
		$accountID = $actionData["accountid"];
1281
1282
		if (count($records) < 1) {
1283
			$this->sendFeedback(false, [
1284
				'type' => ERROR_GENERAL,
1285
				'info' => [
1286
					'title' => _("Files Plugin"),
1287
					'original_message' => _("No record given!"),
1288
					'display_message' => _("No record given!"),
1289
				],
1290
			]);
1291
		}
1292
1293
		$account = $this->accountStore->getAccount($accountID);
1294
1295
		// initialize the backend
1296
		$initializedBackend = $this->initializeBackend($account);
1297
1298
		try {
1299
			$sInfo = $initializedBackend->unshare($records);
0 ignored issues
show
Bug introduced by
The method unshare() does not exist on Files\Backend\AbstractBackend. It seems like you code against a sub-type of Files\Backend\AbstractBackend such as Files\Backend\Owncloud\Backend. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1299
			/** @scrutinizer ignore-call */ 
1300
   $sInfo = $initializedBackend->unshare($records);
Loading history...
Unused Code introduced by
The assignment to $sInfo is dead and can be removed.
Loading history...
1300
		}
1301
		catch (Exception $e) {
1302
			$response['status'] = false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$response was never initialized. Although not strictly required by PHP, it is generally a good practice to add $response = array(); before regardless.
Loading history...
1303
			$response['header'] = _('Deleting share failed');
1304
			$response['message'] = $e->getMessage();
1305
			$this->addActionData("error", $response);
1306
			$GLOBALS["bus"]->addData($this->getResponseData());
1307
1308
			return false;
1309
		}
1310
1311
		$response = [];
1312
		$response['status'] = true;
1313
		$this->addActionData($actionType, $response);
1314
		$GLOBALS["bus"]->addData($this->getResponseData());
1315
1316
		return true;
1317
	}
1318
1319
	/**
1320
	 * Function will use to update the cache.
1321
	 *
1322
	 * @param string $actionType name of the current action
1323
	 * @param array  $actionData all parameters contained in this request
1324
	 *
1325
	 * @return bool true on success or false on failure
1326
	 */
1327
	public function updateCache($actionType, $actionData) {
1328
		$nodeId = $actionData['id'];
1329
		$accountID = $this->accountIDFromNode($nodeId);
1330
		$account = $this->accountStore->getAccount($accountID);
1331
		// initialize the backend
1332
		$initializedBackend = $this->initializeBackend($account, true);
1333
		$relNodeId = substr($nodeId, strpos($nodeId, '/'));
1334
1335
		// remove the trailing slash for the cache key
1336
		$cachePath = rtrim($relNodeId, '/');
1337
		if ($cachePath === "") {
1338
			$cachePath = "/";
1339
		}
1340
		$dir = $initializedBackend->ls($relNodeId);
1341
		$this->setCache($accountID, $cachePath, $dir);
1342
1343
		$response = [];
1344
		$response['status'] = true;
1345
		$this->addActionData($actionType, $response);
1346
		$GLOBALS["bus"]->addData($this->getResponseData());
1347
1348
		return true;
1349
	}
1350
}
1351