Test Failed
Push — master ( cd42b5...841446 )
by
unknown
16:44 queued 06:09
created
plugins/files/php/cssloader.php 2 patches
Indentation   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -9,14 +9,14 @@  discard block
 block discarded – undo
9 9
 
10 10
 // in source mode load the non minifyed css
11 11
 if (isset($_GET['source'])) {
12
-	$content .= file_get_contents("../resources/css/files-main.css");
13
-	$content .= file_get_contents("../resources/css/icons.css");
14
-	$content .= file_get_contents("../resources/css/navbar.css");
15
-	$content .= file_get_contents("../resources/css/pdfjspanel.css");
16
-	$content .= file_get_contents("../resources/css/webodfpanel.css");
12
+    $content .= file_get_contents("../resources/css/files-main.css");
13
+    $content .= file_get_contents("../resources/css/icons.css");
14
+    $content .= file_get_contents("../resources/css/navbar.css");
15
+    $content .= file_get_contents("../resources/css/pdfjspanel.css");
16
+    $content .= file_get_contents("../resources/css/webodfpanel.css");
17 17
 }
18 18
 else {
19
-	$content .= file_get_contents("../resources/css/files" . $debug . ".css");
19
+    $content .= file_get_contents("../resources/css/files" . $debug . ".css");
20 20
 }
21 21
 
22 22
 // try to load all backend form config javascript files
@@ -25,17 +25,17 @@  discard block
 block discarded – undo
25 25
 
26 26
 // Populate the list of directories to check against
27 27
 if (($directoryHandle = opendir($BACKEND_PATH)) !== false) {
28
-	while (($backend = readdir($directoryHandle)) !== false) {
29
-		// Make sure we're not dealing with a file or a link to the parent directory
30
-		if (is_dir($BACKEND_PATH . $backend) && ($backend == '.' || $backend == '..') !== true) {
31
-			if (is_file($BACKEND_PATH . $backend . $BACKEND_CSS_LOADER)) {
32
-				include $BACKEND_PATH . $backend . $BACKEND_CSS_LOADER;
33
-				$class = "\\Files\\Backend\\{$backend}\\BackendCSSLoader";
34
-				$cssloader = new $class();
35
-				$content .= $cssloader->get_combined_css($_GET['debug']);
36
-			}
37
-		}
38
-	}
28
+    while (($backend = readdir($directoryHandle)) !== false) {
29
+        // Make sure we're not dealing with a file or a link to the parent directory
30
+        if (is_dir($BACKEND_PATH . $backend) && ($backend == '.' || $backend == '..') !== true) {
31
+            if (is_file($BACKEND_PATH . $backend . $BACKEND_CSS_LOADER)) {
32
+                include $BACKEND_PATH . $backend . $BACKEND_CSS_LOADER;
33
+                $class = "\\Files\\Backend\\{$backend}\\BackendCSSLoader";
34
+                $cssloader = new $class();
35
+                $content .= $cssloader->get_combined_css($_GET['debug']);
36
+            }
37
+        }
38
+    }
39 39
 }
40 40
 
41 41
 echo $content;
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -14,8 +14,7 @@
 block discarded – undo
14 14
 	$content .= file_get_contents("../resources/css/navbar.css");
15 15
 	$content .= file_get_contents("../resources/css/pdfjspanel.css");
16 16
 	$content .= file_get_contents("../resources/css/webodfpanel.css");
17
-}
18
-else {
17
+} else {
19 18
 	$content .= file_get_contents("../resources/css/files" . $debug . ".css");
20 19
 }
21 20
 
Please login to merge, or discard this patch.
plugins/files/php/class.filesbrowsermodule.php 2 patches
Indentation   +1321 added lines, -1321 removed lines patch added patch discarded remove patch
@@ -29,1330 +29,1330 @@
 block discarded – undo
29 29
  * @extends ListModule
30 30
  */
31 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);
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);
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);
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
-							/*
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);
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);
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);
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 103
 							 * The "message_action" object has been set, check the action_type field for
104 104
 							 * the exact action which must be taken.
105 105
 							 * Supported actions:
106 106
 							 *   - move: move record to new folder
107 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;
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;
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");
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);
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";
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 = "";
902
-		$filename = "";
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];
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;
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;
1014
-	}
1015
-
1016
-	/**
1017
-	 * Store the email as eml to a temporary directory and return its temporary filename.
1018
-	 *
1019
-	 * @param {string} $actionType
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 = "";
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_EC_IMAP_EMAIL, 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
-				// If RFC822-formatted stream is already available in PR_EC_IMAP_EMAIL property
1059
-				// than directly use it, generate otherwise.
1060
-				if (isset($messageProps[PR_EC_IMAP_EMAIL]) || propIsError(PR_EC_IMAP_EMAIL, $messageProps) == MAPI_E_NOT_ENOUGH_MEMORY) {
1061
-					// Stream the message to properly get the PR_EC_IMAP_EMAIL property
1062
-					$stream = mapi_openproperty($message, PR_EC_IMAP_EMAIL, IID_IStream, 0, 0);
1063
-				}
1064
-				else {
1065
-					// Get addressbook for current session
1066
-					$addrBook = $GLOBALS['mapisession']->getAddressbook();
1067
-
1068
-					// Read the message as RFC822-formatted e-mail stream.
1069
-					$stream = mapi_inetmapi_imtoinet($GLOBALS['mapisession']->getSession(), $addrBook, $message, []);
1070
-				}
1071
-
1072
-				if (!empty($messageProps[PR_SUBJECT])) {
1073
-					$filename = PathUtil::sanitizeFilename($messageProps[PR_SUBJECT]) . '.eml';
1074
-				}
1075
-				else {
1076
-					$filename = _('Untitled') . '.eml';
1077
-				}
1078
-
1079
-				$tmpname = tempnam(TMP_PATH, "email2filez");
1080
-
1081
-				// Set the file length
1082
-				$stat = mapi_stream_stat($stream);
1083
-
1084
-				$fhandle = fopen($tmpname, 'w');
1085
-				$buffer = null;
1086
-				for ($i = 0; $i < $stat["cb"]; $i += BLOCK_SIZE) {
1087
-					// Write stream
1088
-					$buffer = mapi_stream_read($stream, BLOCK_SIZE);
1089
-					fwrite($fhandle, $buffer, strlen($buffer));
1090
-				}
1091
-				fclose($fhandle);
1092
-
1093
-				return [$tmpname, $filename];
1094
-			}
1095
-		}
1096
-
1097
-		return false;
1098
-	}
1099
-
1100
-	/**
1101
-	 * Get sharing information from the backend.
1102
-	 *
1103
-	 * @param $actionType
1104
-	 * @param $actionData
1105
-	 *
1106
-	 * @return bool
1107
-	 */
1108
-	private function getSharingInformation($actionType, $actionData) {
1109
-		$response = [];
1110
-		$records = $actionData["records"];
1111
-
1112
-		if (count($records) < 1) {
1113
-			$this->sendFeedback(false, [
1114
-				'type' => ERROR_GENERAL,
1115
-				'info' => [
1116
-					'title' => _("Files Plugin"),
1117
-					'original_message' => _("No record given!"),
1118
-					'display_message' => _("No record given!"),
1119
-				],
1120
-			]);
1121
-		}
1122
-
1123
-		$account = $this->accountFromNode($records[0]);
1124
-
1125
-		// initialize the backend
1126
-		$initializedBackend = $this->initializeBackend($account);
1127
-
1128
-		$relRecords = [];
1129
-		foreach ($records as $record) {
1130
-			$relRecords[] = substr($record, strpos($record, '/')); // remove account id
1131
-		}
1132
-
1133
-		try {
1134
-			$sInfo = $initializedBackend->sharingDetails($relRecords);
1135
-		}
1136
-		catch (Exception $e) {
1137
-			$response['status'] = false;
1138
-			$response['header'] = _('Fetching sharing information failed');
1139
-			$response['message'] = $e->getMessage();
1140
-			$this->addActionData("error", $response);
1141
-			$GLOBALS["bus"]->addData($this->getResponseData());
1142
-
1143
-			return false;
1144
-		}
1145
-
1146
-		$sharingInfo = [];
1147
-		foreach ($sInfo as $path => $details) {
1148
-			$realPath = "#R#" . $account->getId() . $path;
1149
-			$sharingInfo[$realPath] = $details; // add account id again
1150
-		}
1151
-
1152
-		$response['status'] = true;
1153
-		$response['shares'] = $sharingInfo;
1154
-		$this->addActionData($actionType, $response);
1155
-		$GLOBALS["bus"]->addData($this->getResponseData());
1156
-
1157
-		return true;
1158
-	}
1159
-
1160
-	/**
1161
-	 * Create a new share.
1162
-	 *
1163
-	 * @param $actionType
1164
-	 * @param $actionData
1165
-	 *
1166
-	 * @return bool
1167
-	 */
1168
-	private function createNewShare($actionType, $actionData) {
1169
-		$records = $actionData["records"];
1170
-		$shareOptions = $actionData["options"];
1171
-
1172
-		if (count($records) < 1) {
1173
-			$this->sendFeedback(false, [
1174
-				'type' => ERROR_GENERAL,
1175
-				'info' => [
1176
-					'title' => _("Files Plugin"),
1177
-					'original_message' => _("No record given!"),
1178
-					'display_message' => _("No record given!"),
1179
-				],
1180
-			]);
1181
-		}
1182
-
1183
-		$account = $this->accountFromNode($records[0]);
1184
-
1185
-		// initialize the backend
1186
-		$initializedBackend = $this->initializeBackend($account);
1187
-
1188
-		$sharingRecords = [];
1189
-		foreach ($records as $record) {
1190
-			$path = substr($record, strpos($record, '/')); // remove account id
1191
-			$sharingRecords[$path] = $shareOptions; // add options
1192
-		}
1193
-
1194
-		try {
1195
-			$sInfo = $initializedBackend->share($sharingRecords);
1196
-		}
1197
-		catch (Exception $e) {
1198
-			$response['status'] = false;
1199
-			$response['header'] = _('Sharing failed');
1200
-			$response['message'] = $e->getMessage();
1201
-			$this->addActionData("error", $response);
1202
-			$GLOBALS["bus"]->addData($this->getResponseData());
1203
-
1204
-			return false;
1205
-		}
1206
-
1207
-		$sharingInfo = [];
1208
-		foreach ($sInfo as $path => $details) {
1209
-			$realPath = "#R#" . $account->getId() . $path;
1210
-			$sharingInfo[$realPath] = $details; // add account id again
1211
-		}
1212
-
1213
-		$response = [];
1214
-		$response['status'] = true;
1215
-		$response['shares'] = $sharingInfo;
1216
-		$this->addActionData($actionType, $response);
1217
-		$GLOBALS["bus"]->addData($this->getResponseData());
1218
-
1219
-		return true;
1220
-	}
1221
-
1222
-	/**
1223
-	 * Update a existing share.
1224
-	 *
1225
-	 * @param $actionType
1226
-	 * @param $actionData
1227
-	 *
1228
-	 * @return bool
1229
-	 */
1230
-	private function updateExistingShare($actionType, $actionData) {
1231
-		$records = $actionData["records"];
1232
-		$accountID = $actionData["accountid"];
1233
-		$shareOptions = $actionData["options"];
1234
-
1235
-		if (count($records) < 1) {
1236
-			$this->sendFeedback(false, [
1237
-				'type' => ERROR_GENERAL,
1238
-				'info' => [
1239
-					'title' => _("Files Plugin"),
1240
-					'original_message' => _("No record given!"),
1241
-					'display_message' => _("No record given!"),
1242
-				],
1243
-			]);
1244
-		}
1245
-
1246
-		$account = $this->accountStore->getAccount($accountID);
1247
-
1248
-		// initialize the backend
1249
-		$initializedBackend = $this->initializeBackend($account);
1250
-
1251
-		$sharingRecords = [];
1252
-		foreach ($records as $record) {
1253
-			$sharingRecords[$record] = $shareOptions; // add options
1254
-		}
1255
-
1256
-		try {
1257
-			$sInfo = $initializedBackend->share($sharingRecords, true);
1258
-		}
1259
-		catch (Exception $e) {
1260
-			$response['status'] = false;
1261
-			$response['header'] = _('Updating share failed');
1262
-			$response['message'] = $e->getMessage();
1263
-			$this->addActionData("error", $response);
1264
-			$GLOBALS["bus"]->addData($this->getResponseData());
1265
-
1266
-			return false;
1267
-		}
1268
-
1269
-		$response = [];
1270
-		$response['status'] = true;
1271
-		$response['shares'] = $sInfo;
1272
-		$this->addActionData($actionType, $response);
1273
-		$GLOBALS["bus"]->addData($this->getResponseData());
1274
-
1275
-		return true;
1276
-	}
1277
-
1278
-	/**
1279
-	 * Delete one or more shares.
1280
-	 *
1281
-	 * @param $actionType
1282
-	 * @param $actionData
1283
-	 *
1284
-	 * @return bool
1285
-	 */
1286
-	private function deleteExistingShare($actionType, $actionData) {
1287
-		$records = $actionData["records"];
1288
-		$accountID = $actionData["accountid"];
1289
-
1290
-		if (count($records) < 1) {
1291
-			$this->sendFeedback(false, [
1292
-				'type' => ERROR_GENERAL,
1293
-				'info' => [
1294
-					'title' => _("Files Plugin"),
1295
-					'original_message' => _("No record given!"),
1296
-					'display_message' => _("No record given!"),
1297
-				],
1298
-			]);
1299
-		}
1300
-
1301
-		$account = $this->accountStore->getAccount($accountID);
1302
-
1303
-		// initialize the backend
1304
-		$initializedBackend = $this->initializeBackend($account);
1305
-
1306
-		try {
1307
-			$sInfo = $initializedBackend->unshare($records);
1308
-		}
1309
-		catch (Exception $e) {
1310
-			$response['status'] = false;
1311
-			$response['header'] = _('Deleting share failed');
1312
-			$response['message'] = $e->getMessage();
1313
-			$this->addActionData("error", $response);
1314
-			$GLOBALS["bus"]->addData($this->getResponseData());
1315
-
1316
-			return false;
1317
-		}
1318
-
1319
-		$response = [];
1320
-		$response['status'] = true;
1321
-		$this->addActionData($actionType, $response);
1322
-		$GLOBALS["bus"]->addData($this->getResponseData());
1323
-
1324
-		return true;
1325
-	}
1326
-
1327
-	/**
1328
-	 * Function will use to update the cache.
1329
-	 *
1330
-	 * @param string $actionType name of the current action
1331
-	 * @param array  $actionData all parameters contained in this request
1332
-	 *
1333
-	 * @return bool true on success or false on failure
1334
-	 */
1335
-	public function updateCache($actionType, $actionData) {
1336
-		$nodeId = $actionData['id'];
1337
-		$accountID = $this->accountIDFromNode($nodeId);
1338
-		$account = $this->accountStore->getAccount($accountID);
1339
-		// initialize the backend
1340
-		$initializedBackend = $this->initializeBackend($account, true);
1341
-		$relNodeId = substr($nodeId, strpos($nodeId, '/'));
1342
-
1343
-		// remove the trailing slash for the cache key
1344
-		$cachePath = rtrim($relNodeId, '/');
1345
-		if ($cachePath === "") {
1346
-			$cachePath = "/";
1347
-		}
1348
-		$dir = $initializedBackend->ls($relNodeId);
1349
-		$this->setCache($accountID, $cachePath, $dir);
1350
-
1351
-		$response = [];
1352
-		$response['status'] = true;
1353
-		$this->addActionData($actionType, $response);
1354
-		$GLOBALS["bus"]->addData($this->getResponseData());
1355
-
1356
-		return true;
1357
-	}
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;
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;
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");
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);
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";
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 = "";
902
+        $filename = "";
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];
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;
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;
1014
+    }
1015
+
1016
+    /**
1017
+     * Store the email as eml to a temporary directory and return its temporary filename.
1018
+     *
1019
+     * @param {string} $actionType
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 = "";
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_EC_IMAP_EMAIL, 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
+                // If RFC822-formatted stream is already available in PR_EC_IMAP_EMAIL property
1059
+                // than directly use it, generate otherwise.
1060
+                if (isset($messageProps[PR_EC_IMAP_EMAIL]) || propIsError(PR_EC_IMAP_EMAIL, $messageProps) == MAPI_E_NOT_ENOUGH_MEMORY) {
1061
+                    // Stream the message to properly get the PR_EC_IMAP_EMAIL property
1062
+                    $stream = mapi_openproperty($message, PR_EC_IMAP_EMAIL, IID_IStream, 0, 0);
1063
+                }
1064
+                else {
1065
+                    // Get addressbook for current session
1066
+                    $addrBook = $GLOBALS['mapisession']->getAddressbook();
1067
+
1068
+                    // Read the message as RFC822-formatted e-mail stream.
1069
+                    $stream = mapi_inetmapi_imtoinet($GLOBALS['mapisession']->getSession(), $addrBook, $message, []);
1070
+                }
1071
+
1072
+                if (!empty($messageProps[PR_SUBJECT])) {
1073
+                    $filename = PathUtil::sanitizeFilename($messageProps[PR_SUBJECT]) . '.eml';
1074
+                }
1075
+                else {
1076
+                    $filename = _('Untitled') . '.eml';
1077
+                }
1078
+
1079
+                $tmpname = tempnam(TMP_PATH, "email2filez");
1080
+
1081
+                // Set the file length
1082
+                $stat = mapi_stream_stat($stream);
1083
+
1084
+                $fhandle = fopen($tmpname, 'w');
1085
+                $buffer = null;
1086
+                for ($i = 0; $i < $stat["cb"]; $i += BLOCK_SIZE) {
1087
+                    // Write stream
1088
+                    $buffer = mapi_stream_read($stream, BLOCK_SIZE);
1089
+                    fwrite($fhandle, $buffer, strlen($buffer));
1090
+                }
1091
+                fclose($fhandle);
1092
+
1093
+                return [$tmpname, $filename];
1094
+            }
1095
+        }
1096
+
1097
+        return false;
1098
+    }
1099
+
1100
+    /**
1101
+     * Get sharing information from the backend.
1102
+     *
1103
+     * @param $actionType
1104
+     * @param $actionData
1105
+     *
1106
+     * @return bool
1107
+     */
1108
+    private function getSharingInformation($actionType, $actionData) {
1109
+        $response = [];
1110
+        $records = $actionData["records"];
1111
+
1112
+        if (count($records) < 1) {
1113
+            $this->sendFeedback(false, [
1114
+                'type' => ERROR_GENERAL,
1115
+                'info' => [
1116
+                    'title' => _("Files Plugin"),
1117
+                    'original_message' => _("No record given!"),
1118
+                    'display_message' => _("No record given!"),
1119
+                ],
1120
+            ]);
1121
+        }
1122
+
1123
+        $account = $this->accountFromNode($records[0]);
1124
+
1125
+        // initialize the backend
1126
+        $initializedBackend = $this->initializeBackend($account);
1127
+
1128
+        $relRecords = [];
1129
+        foreach ($records as $record) {
1130
+            $relRecords[] = substr($record, strpos($record, '/')); // remove account id
1131
+        }
1132
+
1133
+        try {
1134
+            $sInfo = $initializedBackend->sharingDetails($relRecords);
1135
+        }
1136
+        catch (Exception $e) {
1137
+            $response['status'] = false;
1138
+            $response['header'] = _('Fetching sharing information failed');
1139
+            $response['message'] = $e->getMessage();
1140
+            $this->addActionData("error", $response);
1141
+            $GLOBALS["bus"]->addData($this->getResponseData());
1142
+
1143
+            return false;
1144
+        }
1145
+
1146
+        $sharingInfo = [];
1147
+        foreach ($sInfo as $path => $details) {
1148
+            $realPath = "#R#" . $account->getId() . $path;
1149
+            $sharingInfo[$realPath] = $details; // add account id again
1150
+        }
1151
+
1152
+        $response['status'] = true;
1153
+        $response['shares'] = $sharingInfo;
1154
+        $this->addActionData($actionType, $response);
1155
+        $GLOBALS["bus"]->addData($this->getResponseData());
1156
+
1157
+        return true;
1158
+    }
1159
+
1160
+    /**
1161
+     * Create a new share.
1162
+     *
1163
+     * @param $actionType
1164
+     * @param $actionData
1165
+     *
1166
+     * @return bool
1167
+     */
1168
+    private function createNewShare($actionType, $actionData) {
1169
+        $records = $actionData["records"];
1170
+        $shareOptions = $actionData["options"];
1171
+
1172
+        if (count($records) < 1) {
1173
+            $this->sendFeedback(false, [
1174
+                'type' => ERROR_GENERAL,
1175
+                'info' => [
1176
+                    'title' => _("Files Plugin"),
1177
+                    'original_message' => _("No record given!"),
1178
+                    'display_message' => _("No record given!"),
1179
+                ],
1180
+            ]);
1181
+        }
1182
+
1183
+        $account = $this->accountFromNode($records[0]);
1184
+
1185
+        // initialize the backend
1186
+        $initializedBackend = $this->initializeBackend($account);
1187
+
1188
+        $sharingRecords = [];
1189
+        foreach ($records as $record) {
1190
+            $path = substr($record, strpos($record, '/')); // remove account id
1191
+            $sharingRecords[$path] = $shareOptions; // add options
1192
+        }
1193
+
1194
+        try {
1195
+            $sInfo = $initializedBackend->share($sharingRecords);
1196
+        }
1197
+        catch (Exception $e) {
1198
+            $response['status'] = false;
1199
+            $response['header'] = _('Sharing failed');
1200
+            $response['message'] = $e->getMessage();
1201
+            $this->addActionData("error", $response);
1202
+            $GLOBALS["bus"]->addData($this->getResponseData());
1203
+
1204
+            return false;
1205
+        }
1206
+
1207
+        $sharingInfo = [];
1208
+        foreach ($sInfo as $path => $details) {
1209
+            $realPath = "#R#" . $account->getId() . $path;
1210
+            $sharingInfo[$realPath] = $details; // add account id again
1211
+        }
1212
+
1213
+        $response = [];
1214
+        $response['status'] = true;
1215
+        $response['shares'] = $sharingInfo;
1216
+        $this->addActionData($actionType, $response);
1217
+        $GLOBALS["bus"]->addData($this->getResponseData());
1218
+
1219
+        return true;
1220
+    }
1221
+
1222
+    /**
1223
+     * Update a existing share.
1224
+     *
1225
+     * @param $actionType
1226
+     * @param $actionData
1227
+     *
1228
+     * @return bool
1229
+     */
1230
+    private function updateExistingShare($actionType, $actionData) {
1231
+        $records = $actionData["records"];
1232
+        $accountID = $actionData["accountid"];
1233
+        $shareOptions = $actionData["options"];
1234
+
1235
+        if (count($records) < 1) {
1236
+            $this->sendFeedback(false, [
1237
+                'type' => ERROR_GENERAL,
1238
+                'info' => [
1239
+                    'title' => _("Files Plugin"),
1240
+                    'original_message' => _("No record given!"),
1241
+                    'display_message' => _("No record given!"),
1242
+                ],
1243
+            ]);
1244
+        }
1245
+
1246
+        $account = $this->accountStore->getAccount($accountID);
1247
+
1248
+        // initialize the backend
1249
+        $initializedBackend = $this->initializeBackend($account);
1250
+
1251
+        $sharingRecords = [];
1252
+        foreach ($records as $record) {
1253
+            $sharingRecords[$record] = $shareOptions; // add options
1254
+        }
1255
+
1256
+        try {
1257
+            $sInfo = $initializedBackend->share($sharingRecords, true);
1258
+        }
1259
+        catch (Exception $e) {
1260
+            $response['status'] = false;
1261
+            $response['header'] = _('Updating share failed');
1262
+            $response['message'] = $e->getMessage();
1263
+            $this->addActionData("error", $response);
1264
+            $GLOBALS["bus"]->addData($this->getResponseData());
1265
+
1266
+            return false;
1267
+        }
1268
+
1269
+        $response = [];
1270
+        $response['status'] = true;
1271
+        $response['shares'] = $sInfo;
1272
+        $this->addActionData($actionType, $response);
1273
+        $GLOBALS["bus"]->addData($this->getResponseData());
1274
+
1275
+        return true;
1276
+    }
1277
+
1278
+    /**
1279
+     * Delete one or more shares.
1280
+     *
1281
+     * @param $actionType
1282
+     * @param $actionData
1283
+     *
1284
+     * @return bool
1285
+     */
1286
+    private function deleteExistingShare($actionType, $actionData) {
1287
+        $records = $actionData["records"];
1288
+        $accountID = $actionData["accountid"];
1289
+
1290
+        if (count($records) < 1) {
1291
+            $this->sendFeedback(false, [
1292
+                'type' => ERROR_GENERAL,
1293
+                'info' => [
1294
+                    'title' => _("Files Plugin"),
1295
+                    'original_message' => _("No record given!"),
1296
+                    'display_message' => _("No record given!"),
1297
+                ],
1298
+            ]);
1299
+        }
1300
+
1301
+        $account = $this->accountStore->getAccount($accountID);
1302
+
1303
+        // initialize the backend
1304
+        $initializedBackend = $this->initializeBackend($account);
1305
+
1306
+        try {
1307
+            $sInfo = $initializedBackend->unshare($records);
1308
+        }
1309
+        catch (Exception $e) {
1310
+            $response['status'] = false;
1311
+            $response['header'] = _('Deleting share failed');
1312
+            $response['message'] = $e->getMessage();
1313
+            $this->addActionData("error", $response);
1314
+            $GLOBALS["bus"]->addData($this->getResponseData());
1315
+
1316
+            return false;
1317
+        }
1318
+
1319
+        $response = [];
1320
+        $response['status'] = true;
1321
+        $this->addActionData($actionType, $response);
1322
+        $GLOBALS["bus"]->addData($this->getResponseData());
1323
+
1324
+        return true;
1325
+    }
1326
+
1327
+    /**
1328
+     * Function will use to update the cache.
1329
+     *
1330
+     * @param string $actionType name of the current action
1331
+     * @param array  $actionData all parameters contained in this request
1332
+     *
1333
+     * @return bool true on success or false on failure
1334
+     */
1335
+    public function updateCache($actionType, $actionData) {
1336
+        $nodeId = $actionData['id'];
1337
+        $accountID = $this->accountIDFromNode($nodeId);
1338
+        $account = $this->accountStore->getAccount($accountID);
1339
+        // initialize the backend
1340
+        $initializedBackend = $this->initializeBackend($account, true);
1341
+        $relNodeId = substr($nodeId, strpos($nodeId, '/'));
1342
+
1343
+        // remove the trailing slash for the cache key
1344
+        $cachePath = rtrim($relNodeId, '/');
1345
+        if ($cachePath === "") {
1346
+            $cachePath = "/";
1347
+        }
1348
+        $dir = $initializedBackend->ls($relNodeId);
1349
+        $this->setCache($accountID, $cachePath, $dir);
1350
+
1351
+        $response = [];
1352
+        $response['status'] = true;
1353
+        $this->addActionData($actionType, $response);
1354
+        $GLOBALS["bus"]->addData($this->getResponseData());
1355
+
1356
+        return true;
1357
+    }
1358 1358
 }
Please login to merge, or discard this patch.
Braces   +27 added lines, -54 removed lines patch added patch discarded remove patch
@@ -115,19 +115,16 @@  discard block
 block discarded – undo
115 115
 										// check if we should create something new or edit an existing file/folder
116 116
 										if (isset($actionData["entryid"])) {
117 117
 											$result = $this->rename($actionType, $actionData);
118
-										}
119
-										else {
118
+										} else {
120 119
 											$result = $this->save($actionData);
121 120
 										}
122 121
 										break;
123 122
 								}
124
-							}
125
-							else {
123
+							} else {
126 124
 								// check if we should create something new or edit an existing file/folder
127 125
 								if (isset($actionData["entryid"])) {
128 126
 									$result = $this->rename($actionType, $actionData);
129
-								}
130
-								else {
127
+								} else {
131 128
 									$result = $this->save($actionData);
132 129
 								}
133 130
 							}
@@ -164,11 +161,9 @@  discard block
 block discarded – undo
164 161
 						default:
165 162
 							$this->handleUnknownActionType($actionType);
166 163
 					}
167
-				}
168
-				catch (MAPIException $e) {
164
+				} catch (MAPIException $e) {
169 165
 					$this->sendFeedback(false, $this->errorDetailsFromException($e));
170
-				}
171
-				catch (AccountException $e) {
166
+				} catch (AccountException $e) {
172 167
 					$this->sendFeedback(false, [
173 168
 						'type' => ERROR_GENERAL,
174 169
 						'info' => [
@@ -177,8 +172,7 @@  discard block
 block discarded – undo
177 172
 							'display_message' => $e->getMessage(),
178 173
 						],
179 174
 					]);
180
-				}
181
-				catch (BackendException $e) {
175
+				} catch (BackendException $e) {
182 176
 					$this->sendFeedback(false, [
183 177
 						'type' => ERROR_GENERAL,
184 178
 						'info' => [
@@ -239,8 +233,7 @@  discard block
 block discarded – undo
239 233
 					'parent_entryid' => $this->createId($realNodeId),
240 234
 				];
241 235
 			}
242
-		}
243
-		else {
236
+		} else {
244 237
 			$account = $this->accountStore->getAccount($accountID);
245 238
 
246 239
 			// initialize the backend
@@ -356,8 +349,7 @@  discard block
 block discarded – undo
356 349
 				$dirName = dirname($nodeId, 1);
357 350
 				if ($dirName === '/') {
358 351
 					$path = stringToUTF8Encode($nodeIdPrefix . $dirName);
359
-				}
360
-				else {
352
+				} else {
361 353
 					$path = stringToUTF8Encode($nodeIdPrefix . $dirName . '/');
362 354
 				}
363 355
 
@@ -371,8 +363,7 @@  discard block
 block discarded – undo
371 363
 					$dir[$id]['store_entryid'] = $storeEntryid;
372 364
 
373 365
 					$updateCache = true;
374
-				}
375
-				else {
366
+				} else {
376 367
 					$entryid = $node['entryid'];
377 368
 					$parentEntryid = $node['parent_entryid'];
378 369
 					$storeEntryid = $node['store_entryid'];
@@ -401,8 +392,7 @@  discard block
 block discarded – undo
401 392
 			if ($updateCache) {
402 393
 				$this->setCache($accountID, $cachePath, $dir);
403 394
 			}
404
-		}
405
-		else {
395
+		} else {
406 396
 			Logger::debug(self::LOG_CONTEXT, "dir was empty");
407 397
 		}
408 398
 
@@ -433,8 +423,7 @@  discard block
 block discarded – undo
433 423
 
434 424
 		if ($navtree) {
435 425
 			$sortednodes = ArrayUtil::sort_by_key($nodes, $sortkey, $sortdir);
436
-		}
437
-		else {
426
+		} else {
438 427
 			$sortednodes = ArrayUtil::sort_props_by_key($nodes, $sortkey, $sortdir);
439 428
 		}
440 429
 
@@ -481,8 +470,7 @@  discard block
 block discarded – undo
481 470
 			$response['status'] = true;
482 471
 			$this->addActionData($actionType, $response);
483 472
 			$GLOBALS["bus"]->addData($this->getResponseData());
484
-		}
485
-		else {
473
+		} else {
486 474
 			$nodeId = $actionData['folder_id'];
487 475
 
488 476
 			$relNodeId = substr($nodeId, strpos($nodeId, '/'));
@@ -495,8 +483,7 @@  discard block
 block discarded – undo
495 483
 
496 484
 			try {
497 485
 				$result = $initializedBackend->delete($relNodeId);
498
-			}
499
-			catch (\Files\Backend\Exception $e) {
486
+			} catch (\Files\Backend\Exception $e) {
500 487
 				// TODO: this might fails because the file was already deleted.
501 488
 				// fire error message if any other error occurred.
502 489
 				Logger::debug(self::LOG_CONTEXT, "deleted a directory that was no longer available");
@@ -686,8 +673,7 @@  discard block
 block discarded – undo
686 673
 
687 674
 			try {
688 675
 				$lsdata = $initializedBackend->ls($relDirname); // we can only check files in the same folder, so one request will be enough
689
-			}
690
-			catch (Exception $e) {
676
+			} catch (Exception $e) {
691 677
 				// ignore - if file not found -> does not exist :)
692 678
 			}
693 679
 			if (isset($lsdata) && is_array($lsdata)) {
@@ -810,8 +796,7 @@  discard block
 block discarded – undo
810 796
 
811 797
 				$this->updateDirCache($initializedBackend, $dirName, $filePath, $actionData);
812 798
 			}
813
-		}
814
-		elseif ($actionData["type"] === "mail") {
799
+		} elseif ($actionData["type"] === "mail") {
815 800
 			foreach ($actionData["items"] as $item) {
816 801
 				list($tmpname, $filename) = $this->prepareEmailForUpload($item);
817 802
 
@@ -825,8 +810,7 @@  discard block
 block discarded – undo
825 810
 
826 811
 				$this->updateDirCache($initializedBackend, $dirName, $filePath, $actionData);
827 812
 			}
828
-		}
829
-		else {
813
+		} else {
830 814
 			$this->sendFeedback(false, [
831 815
 				'type' => ERROR_GENERAL,
832 816
 				'info' => [
@@ -941,12 +925,10 @@  discard block
 block discarded – undo
941 925
 						// Set filename
942 926
 						if (isset($props[PR_ATTACH_LONG_FILENAME])) {
943 927
 							$filename = PathUtil::sanitizeFilename($props[PR_ATTACH_LONG_FILENAME]);
944
-						}
945
-						else {
928
+						} else {
946 929
 							if (isset($props[PR_ATTACH_FILENAME])) {
947 930
 								$filename = PathUtil::sanitizeFilename($props[PR_ATTACH_FILENAME]);
948
-							}
949
-							else {
931
+							} else {
950 932
 								if (isset($props[PR_DISPLAY_NAME])) {
951 933
 									$filename = PathUtil::sanitizeFilename($props[PR_DISPLAY_NAME]);
952 934
 								}
@@ -956,8 +938,7 @@  discard block
 block discarded – undo
956 938
 						// Set content type
957 939
 						if (isset($props[PR_ATTACH_MIME_TAG])) {
958 940
 							$contentType = $props[PR_ATTACH_MIME_TAG];
959
-						}
960
-						else {
941
+						} else {
961 942
 							// Parse the extension of the filename to get the content type
962 943
 							if (strrpos($filename, ".") !== false) {
963 944
 								$extension = strtolower(substr($filename, strrpos($filename, ".")));
@@ -1001,12 +982,10 @@  discard block
 block discarded – undo
1001 982
 						return [$tmpname, $filename];
1002 983
 					}
1003 984
 				}
1004
-			}
1005
-			else {
985
+			} else {
1006 986
 				Logger::error(self::LOG_CONTEXT, "store could not be opened");
1007 987
 			}
1008
-		}
1009
-		else {
988
+		} else {
1010 989
 			Logger::error(self::LOG_CONTEXT, "wrong call, store and entryid have to be set");
1011 990
 		}
1012 991
 
@@ -1060,8 +1039,7 @@  discard block
 block discarded – undo
1060 1039
 				if (isset($messageProps[PR_EC_IMAP_EMAIL]) || propIsError(PR_EC_IMAP_EMAIL, $messageProps) == MAPI_E_NOT_ENOUGH_MEMORY) {
1061 1040
 					// Stream the message to properly get the PR_EC_IMAP_EMAIL property
1062 1041
 					$stream = mapi_openproperty($message, PR_EC_IMAP_EMAIL, IID_IStream, 0, 0);
1063
-				}
1064
-				else {
1042
+				} else {
1065 1043
 					// Get addressbook for current session
1066 1044
 					$addrBook = $GLOBALS['mapisession']->getAddressbook();
1067 1045
 
@@ -1071,8 +1049,7 @@  discard block
 block discarded – undo
1071 1049
 
1072 1050
 				if (!empty($messageProps[PR_SUBJECT])) {
1073 1051
 					$filename = PathUtil::sanitizeFilename($messageProps[PR_SUBJECT]) . '.eml';
1074
-				}
1075
-				else {
1052
+				} else {
1076 1053
 					$filename = _('Untitled') . '.eml';
1077 1054
 				}
1078 1055
 
@@ -1132,8 +1109,7 @@  discard block
 block discarded – undo
1132 1109
 
1133 1110
 		try {
1134 1111
 			$sInfo = $initializedBackend->sharingDetails($relRecords);
1135
-		}
1136
-		catch (Exception $e) {
1112
+		} catch (Exception $e) {
1137 1113
 			$response['status'] = false;
1138 1114
 			$response['header'] = _('Fetching sharing information failed');
1139 1115
 			$response['message'] = $e->getMessage();
@@ -1193,8 +1169,7 @@  discard block
 block discarded – undo
1193 1169
 
1194 1170
 		try {
1195 1171
 			$sInfo = $initializedBackend->share($sharingRecords);
1196
-		}
1197
-		catch (Exception $e) {
1172
+		} catch (Exception $e) {
1198 1173
 			$response['status'] = false;
1199 1174
 			$response['header'] = _('Sharing failed');
1200 1175
 			$response['message'] = $e->getMessage();
@@ -1255,8 +1230,7 @@  discard block
 block discarded – undo
1255 1230
 
1256 1231
 		try {
1257 1232
 			$sInfo = $initializedBackend->share($sharingRecords, true);
1258
-		}
1259
-		catch (Exception $e) {
1233
+		} catch (Exception $e) {
1260 1234
 			$response['status'] = false;
1261 1235
 			$response['header'] = _('Updating share failed');
1262 1236
 			$response['message'] = $e->getMessage();
@@ -1305,8 +1279,7 @@  discard block
 block discarded – undo
1305 1279
 
1306 1280
 		try {
1307 1281
 			$sInfo = $initializedBackend->unshare($records);
1308
-		}
1309
-		catch (Exception $e) {
1282
+		} catch (Exception $e) {
1310 1283
 			$response['status'] = false;
1311 1284
 			$response['header'] = _('Deleting share failed');
1312 1285
 			$response['message'] = $e->getMessage();
Please login to merge, or discard this patch.
plugins/passwd/php/plugin.passwd.php 1 patch
Indentation   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -5,48 +5,48 @@
 block discarded – undo
5 5
  * Allows to change user password from grommunio Web.
6 6
  */
7 7
 class Pluginpasswd extends Plugin {
8
-	public function __construct() {
9
-	}
8
+    public function __construct() {
9
+    }
10 10
 
11
-	/**
12
-	 * Function initializes the Plugin and registers all hooks.
13
-	 */
14
-	public function init() {
15
-		$this->registerHook('server.core.settings.init.before');
16
-	}
11
+    /**
12
+     * Function initializes the Plugin and registers all hooks.
13
+     */
14
+    public function init() {
15
+        $this->registerHook('server.core.settings.init.before');
16
+    }
17 17
 
18
-	/**
19
-	 * Function is executed when a hook is triggered by the PluginManager.
20
-	 *
21
-	 * @param string $eventID the id of the triggered hook
22
-	 * @param mixed  $data    object(s) related to the hook
23
-	 */
24
-	public function execute($eventID, &$data) {
25
-		switch ($eventID) {
26
-			case 'server.core.settings.init.before':
27
-				$this->injectPluginSettings($data);
28
-				break;
29
-		}
30
-	}
18
+    /**
19
+     * Function is executed when a hook is triggered by the PluginManager.
20
+     *
21
+     * @param string $eventID the id of the triggered hook
22
+     * @param mixed  $data    object(s) related to the hook
23
+     */
24
+    public function execute($eventID, &$data) {
25
+        switch ($eventID) {
26
+            case 'server.core.settings.init.before':
27
+                $this->injectPluginSettings($data);
28
+                break;
29
+        }
30
+    }
31 31
 
32
-	/**
33
-	 * Called when the core Settings class is initialized and ready to accept sysadmin default
34
-	 * settings. Registers the sysadmin defaults for the SugarCRM plugin.
35
-	 *
36
-	 * @param array $data Reference to the data of the triggered hook
37
-	 */
38
-	public function injectPluginSettings(&$data) {
39
-		$data['settingsObj']->addSysAdminDefaults([
40
-			'zarafa' => [
41
-				'v1' => [
42
-					'plugins' => [
43
-						'passwd' => [
44
-							'enable' => PLUGIN_PASSWD_USER_DEFAULT_ENABLE,
45
-							'enable_strict_check' => PLUGIN_PASSWD_STRICT_CHECK_ENABLE,
46
-						],
47
-					],
48
-				],
49
-			],
50
-		]);
51
-	}
32
+    /**
33
+     * Called when the core Settings class is initialized and ready to accept sysadmin default
34
+     * settings. Registers the sysadmin defaults for the SugarCRM plugin.
35
+     *
36
+     * @param array $data Reference to the data of the triggered hook
37
+     */
38
+    public function injectPluginSettings(&$data) {
39
+        $data['settingsObj']->addSysAdminDefaults([
40
+            'zarafa' => [
41
+                'v1' => [
42
+                    'plugins' => [
43
+                        'passwd' => [
44
+                            'enable' => PLUGIN_PASSWD_USER_DEFAULT_ENABLE,
45
+                            'enable_strict_check' => PLUGIN_PASSWD_STRICT_CHECK_ENABLE,
46
+                        ],
47
+                    ],
48
+                ],
49
+            ],
50
+        ]);
51
+    }
52 52
 }
Please login to merge, or discard this patch.
plugins/passwd/php/class.passwdmodule.php 2 patches
Indentation   +151 added lines, -151 removed lines patch added patch discarded remove patch
@@ -4,170 +4,170 @@
 block discarded – undo
4 4
  * Module that will be used to change passwords of the user.
5 5
  */
6 6
 class PasswdModule extends Module {
7
-	/**
8
-	 * Process the incoming events that were fire by the client.
9
-	 */
10
-	public function execute() {
11
-		foreach ($this->data as $actionType => $actionData) {
12
-			if (isset($actionType)) {
13
-				try {
14
-					switch ($actionType) {
15
-						case 'save':
16
-							$this->save($actionData);
17
-							break;
7
+    /**
8
+     * Process the incoming events that were fire by the client.
9
+     */
10
+    public function execute() {
11
+        foreach ($this->data as $actionType => $actionData) {
12
+            if (isset($actionType)) {
13
+                try {
14
+                    switch ($actionType) {
15
+                        case 'save':
16
+                            $this->save($actionData);
17
+                            break;
18 18
 
19
-						default:
20
-							$this->handleUnknownActionType($actionType);
21
-					}
22
-				}
23
-				catch (MAPIException $e) {
24
-					$this->sendFeedback(false, $this->errorDetailsFromException($e));
25
-				}
26
-			}
27
-		}
28
-	}
19
+                        default:
20
+                            $this->handleUnknownActionType($actionType);
21
+                    }
22
+                }
23
+                catch (MAPIException $e) {
24
+                    $this->sendFeedback(false, $this->errorDetailsFromException($e));
25
+                }
26
+            }
27
+        }
28
+    }
29 29
 
30
-	/**
31
-	 * Change the password of user. Do some calidation and call proper methods based on
32
-	 * zarafa setup.
33
-	 *
34
-	 * @param {Array} $data data sent by client
35
-	 */
36
-	public function save($data) {
37
-		$errorMessage = '';
30
+    /**
31
+     * Change the password of user. Do some calidation and call proper methods based on
32
+     * zarafa setup.
33
+     *
34
+     * @param {Array} $data data sent by client
35
+     */
36
+    public function save($data) {
37
+        $errorMessage = '';
38 38
 
39
-		// some sanity checks
40
-		if (empty($data)) {
41
-			$errorMessage = _('No data received.');
42
-		}
39
+        // some sanity checks
40
+        if (empty($data)) {
41
+            $errorMessage = _('No data received.');
42
+        }
43 43
 
44
-		if (empty($data['username'])) {
45
-			$errorMessage = _('Account is empty.');
46
-		}
44
+        if (empty($data['username'])) {
45
+            $errorMessage = _('Account is empty.');
46
+        }
47 47
 
48
-		if (empty($data['current_password'])) {
49
-			$errorMessage = _('Current password is empty.');
50
-		}
48
+        if (empty($data['current_password'])) {
49
+            $errorMessage = _('Current password is empty.');
50
+        }
51 51
 
52
-		if (empty($data['new_password']) || empty($data['new_password_repeat'])) {
53
-			$errorMessage = _('New password is empty.');
54
-		}
52
+        if (empty($data['new_password']) || empty($data['new_password_repeat'])) {
53
+            $errorMessage = _('New password is empty.');
54
+        }
55 55
 
56
-		if ($data['new_password'] !== $data['new_password_repeat']) {
57
-			$errorMessage = _('New passwords do not match.');
58
-		}
56
+        if ($data['new_password'] !== $data['new_password_repeat']) {
57
+            $errorMessage = _('New passwords do not match.');
58
+        }
59 59
 
60
-		if (empty($errorMessage)) {
61
-			$this->saveInDB($data);
62
-		}
63
-		else {
64
-			$this->sendFeedback(false, [
65
-				'type' => ERROR_ZARAFA,
66
-				'info' => [
67
-					'display_message' => $errorMessage,
68
-				],
69
-			]);
70
-		}
71
-	}
60
+        if (empty($errorMessage)) {
61
+            $this->saveInDB($data);
62
+        }
63
+        else {
64
+            $this->sendFeedback(false, [
65
+                'type' => ERROR_ZARAFA,
66
+                'info' => [
67
+                    'display_message' => $errorMessage,
68
+                ],
69
+            ]);
70
+        }
71
+    }
72 72
 
73
-	/**
74
-	 * Function will try to change user's password via MAPI in SOAP connection.
75
-	 *
76
-	 * @param {Array} $data data sent by client
77
-	 */
78
-	public function saveInDB($data) {
79
-		$errorMessage = '';
80
-		$userName = $data['username'];
81
-		$newPassword = $data['new_password'];
82
-		$sessionPass = '';
73
+    /**
74
+     * Function will try to change user's password via MAPI in SOAP connection.
75
+     *
76
+     * @param {Array} $data data sent by client
77
+     */
78
+    public function saveInDB($data) {
79
+        $errorMessage = '';
80
+        $userName = $data['username'];
81
+        $newPassword = $data['new_password'];
82
+        $sessionPass = '';
83 83
 
84
-		// get current session password
85
-		// if this plugin is used on a webapp version with EncryptionStore,
86
-		// $_SESSION['password'] is no longer available. Uses EncryptionStore
87
-		// in this case.
88
-		if (class_exists("EncryptionStore")) {
89
-			$encryptionStore = EncryptionStore::getInstance();
90
-			$sessionPass = $encryptionStore->get("password");
91
-		}
84
+        // get current session password
85
+        // if this plugin is used on a webapp version with EncryptionStore,
86
+        // $_SESSION['password'] is no longer available. Uses EncryptionStore
87
+        // in this case.
88
+        if (class_exists("EncryptionStore")) {
89
+            $encryptionStore = EncryptionStore::getInstance();
90
+            $sessionPass = $encryptionStore->get("password");
91
+        }
92 92
 
93
-		if ($data['current_password'] !== $sessionPass) {
94
-			$errorMessage = _('Current password does not match.');
95
-		}
96
-		elseif (defined('PLUGIN_PASSWD_USE_ZCORE') && PLUGIN_PASSWD_USE_ZCORE) {
97
-			try {
98
-				$result = nsp_setuserpasswd($userName, $sessionPass, $newPassword);
99
-				// password changed successfully
100
-				if ($result) {
101
-					$this->sendFeedback(true, [
102
-						'info' => [
103
-							'display_message' => _('Password is changed successfully.'),
104
-						],
105
-					]);
106
-					// write new password to session because we don't want user to re-authenticate
107
-					session_start();
108
-					$encryptionStore = EncryptionStore::getInstance();
109
-					$encryptionStore->add('password', $newPassword);
110
-					session_write_close();
93
+        if ($data['current_password'] !== $sessionPass) {
94
+            $errorMessage = _('Current password does not match.');
95
+        }
96
+        elseif (defined('PLUGIN_PASSWD_USE_ZCORE') && PLUGIN_PASSWD_USE_ZCORE) {
97
+            try {
98
+                $result = nsp_setuserpasswd($userName, $sessionPass, $newPassword);
99
+                // password changed successfully
100
+                if ($result) {
101
+                    $this->sendFeedback(true, [
102
+                        'info' => [
103
+                            'display_message' => _('Password is changed successfully.'),
104
+                        ],
105
+                    ]);
106
+                    // write new password to session because we don't want user to re-authenticate
107
+                    session_start();
108
+                    $encryptionStore = EncryptionStore::getInstance();
109
+                    $encryptionStore->add('password', $newPassword);
110
+                    session_write_close();
111 111
 
112
-					return;
113
-				}
114
-			}
115
-			catch (MAPIException $e) {
116
-				if (mapi_last_hresult() == MAPI_E_NO_ACCESS) {
117
-					$errorMessage = _('Your password is wrong or you have insufficient permission to change password');
118
-				}
119
-			}
120
-			if (empty($errorMessage)) {
121
-				$errorMessage = _('Password is not changed.');
122
-			}
123
-		}
124
-		else {
125
-			$url = (defined('PLUGIN_PASSWD_ADMIN_API_ENDPOINT') && PLUGIN_PASSWD_ADMIN_API_ENDPOINT) ?
126
-				PLUGIN_PASSWD_ADMIN_API_ENDPOINT :
127
-				'http://[::1]:8080/api/v1/passwd';
128
-			$result = file_get_contents($url, false, stream_context_create([
129
-				'http' => [
130
-					'header' => [
131
-						'Content-type: application/json',
132
-					],
133
-					'method' => 'PUT',
134
-					'content' => json_encode([
135
-						"user" => $userName,
136
-						"old" => $data['current_password'],
137
-						"new" => $newPassword,
138
-					]),
139
-				],
140
-			]));
112
+                    return;
113
+                }
114
+            }
115
+            catch (MAPIException $e) {
116
+                if (mapi_last_hresult() == MAPI_E_NO_ACCESS) {
117
+                    $errorMessage = _('Your password is wrong or you have insufficient permission to change password');
118
+                }
119
+            }
120
+            if (empty($errorMessage)) {
121
+                $errorMessage = _('Password is not changed.');
122
+            }
123
+        }
124
+        else {
125
+            $url = (defined('PLUGIN_PASSWD_ADMIN_API_ENDPOINT') && PLUGIN_PASSWD_ADMIN_API_ENDPOINT) ?
126
+                PLUGIN_PASSWD_ADMIN_API_ENDPOINT :
127
+                'http://[::1]:8080/api/v1/passwd';
128
+            $result = file_get_contents($url, false, stream_context_create([
129
+                'http' => [
130
+                    'header' => [
131
+                        'Content-type: application/json',
132
+                    ],
133
+                    'method' => 'PUT',
134
+                    'content' => json_encode([
135
+                        "user" => $userName,
136
+                        "old" => $data['current_password'],
137
+                        "new" => $newPassword,
138
+                    ]),
139
+                ],
140
+            ]));
141 141
 
142
-			if ($result === false) {
143
-				$errorMessage = _('Error changing password. Please contact the system administrator.');
144
-			}
145
-			else {
146
-				$this->sendFeedback(true, [
147
-					'info' => [
148
-						'display_message' => _('Password has been changed successfully.'),
149
-					],
150
-				]);
151
-				// write new password to session because we don't want user to re-authenticate
152
-				session_start();
153
-				$encryptionStore = EncryptionStore::getInstance();
154
-				$encryptionStore->add('password', $newPassword);
155
-				session_write_close();
142
+            if ($result === false) {
143
+                $errorMessage = _('Error changing password. Please contact the system administrator.');
144
+            }
145
+            else {
146
+                $this->sendFeedback(true, [
147
+                    'info' => [
148
+                        'display_message' => _('Password has been changed successfully.'),
149
+                    ],
150
+                ]);
151
+                // write new password to session because we don't want user to re-authenticate
152
+                session_start();
153
+                $encryptionStore = EncryptionStore::getInstance();
154
+                $encryptionStore->add('password', $newPassword);
155
+                session_write_close();
156 156
 
157
-				return;
158
-			}
159
-			if (empty($errorMessage)) {
160
-				$errorMessage = _('Password is not changed.');
161
-			}
162
-		}
157
+                return;
158
+            }
159
+            if (empty($errorMessage)) {
160
+                $errorMessage = _('Password is not changed.');
161
+            }
162
+        }
163 163
 
164
-		if (!empty($errorMessage)) {
165
-			$this->sendFeedback(false, [
166
-				'type' => ERROR_ZARAFA,
167
-				'info' => [
168
-					'display_message' => $errorMessage,
169
-				],
170
-			]);
171
-		}
172
-	}
164
+        if (!empty($errorMessage)) {
165
+            $this->sendFeedback(false, [
166
+                'type' => ERROR_ZARAFA,
167
+                'info' => [
168
+                    'display_message' => $errorMessage,
169
+                ],
170
+            ]);
171
+        }
172
+    }
173 173
 }
Please login to merge, or discard this patch.
Braces   +6 added lines, -12 removed lines patch added patch discarded remove patch
@@ -19,8 +19,7 @@  discard block
 block discarded – undo
19 19
 						default:
20 20
 							$this->handleUnknownActionType($actionType);
21 21
 					}
22
-				}
23
-				catch (MAPIException $e) {
22
+				} catch (MAPIException $e) {
24 23
 					$this->sendFeedback(false, $this->errorDetailsFromException($e));
25 24
 				}
26 25
 			}
@@ -59,8 +58,7 @@  discard block
 block discarded – undo
59 58
 
60 59
 		if (empty($errorMessage)) {
61 60
 			$this->saveInDB($data);
62
-		}
63
-		else {
61
+		} else {
64 62
 			$this->sendFeedback(false, [
65 63
 				'type' => ERROR_ZARAFA,
66 64
 				'info' => [
@@ -92,8 +90,7 @@  discard block
 block discarded – undo
92 90
 
93 91
 		if ($data['current_password'] !== $sessionPass) {
94 92
 			$errorMessage = _('Current password does not match.');
95
-		}
96
-		elseif (defined('PLUGIN_PASSWD_USE_ZCORE') && PLUGIN_PASSWD_USE_ZCORE) {
93
+		} elseif (defined('PLUGIN_PASSWD_USE_ZCORE') && PLUGIN_PASSWD_USE_ZCORE) {
97 94
 			try {
98 95
 				$result = nsp_setuserpasswd($userName, $sessionPass, $newPassword);
99 96
 				// password changed successfully
@@ -111,8 +108,7 @@  discard block
 block discarded – undo
111 108
 
112 109
 					return;
113 110
 				}
114
-			}
115
-			catch (MAPIException $e) {
111
+			} catch (MAPIException $e) {
116 112
 				if (mapi_last_hresult() == MAPI_E_NO_ACCESS) {
117 113
 					$errorMessage = _('Your password is wrong or you have insufficient permission to change password');
118 114
 				}
@@ -120,8 +116,7 @@  discard block
 block discarded – undo
120 116
 			if (empty($errorMessage)) {
121 117
 				$errorMessage = _('Password is not changed.');
122 118
 			}
123
-		}
124
-		else {
119
+		} else {
125 120
 			$url = (defined('PLUGIN_PASSWD_ADMIN_API_ENDPOINT') && PLUGIN_PASSWD_ADMIN_API_ENDPOINT) ?
126 121
 				PLUGIN_PASSWD_ADMIN_API_ENDPOINT :
127 122
 				'http://[::1]:8080/api/v1/passwd';
@@ -141,8 +136,7 @@  discard block
 block discarded – undo
141 136
 
142 137
 			if ($result === false) {
143 138
 				$errorMessage = _('Error changing password. Please contact the system administrator.');
144
-			}
145
-			else {
139
+			} else {
146 140
 				$this->sendFeedback(true, [
147 141
 					'info' => [
148 142
 						'display_message' => _('Password has been changed successfully.'),
Please login to merge, or discard this patch.
plugins/mdm/php/class.pluginmdmmodule.php 2 patches
Indentation   +962 added lines, -962 removed lines patch added patch discarded remove patch
@@ -7,331 +7,331 @@  discard block
 block discarded – undo
7 7
  * PluginMDMModule Module.
8 8
  */
9 9
 class PluginMDMModule extends Module {
10
-	// content data
11
-	public const FOLDERUUID = 1;
12
-	public const FOLDERTYPE = 2;
13
-	public const FOLDERBACKENDID = 5;
14
-
15
-	/**
16
-	 * Constructor.
17
-	 *
18
-	 * @param int   $id   unique id
19
-	 * @param array $data list of all actions
20
-	 */
21
-	public function __construct($id, $data) {
22
-		parent::__construct($id, $data);
23
-		$this->stateFolder = null;
24
-		$this->deviceStates = [];
25
-		$this->devices = [];
26
-		$this->setupDevices();
27
-	}
28
-
29
-	/**
30
-	 * Function sets up the array with the user's devices.
31
-	 */
32
-	public function setupDevices() {
33
-		$devices = [];
34
-		$stateFolder = $this->getStoreStateFolder();
35
-		if ($stateFolder) {
36
-			$store = $GLOBALS["mapisession"]->getDefaultMessageStore();
37
-			$username = $GLOBALS["mapisession"]->getUserName();
38
-			$hierarchyTable = mapi_folder_gethierarchytable($stateFolder, CONVENIENT_DEPTH | MAPI_DEFERRED_ERRORS);
39
-			$rows = mapi_table_queryallrows($hierarchyTable, [PR_ENTRYID, PR_DISPLAY_NAME]);
40
-			foreach ($rows as $row) {
41
-				$deviceStateFolder = mapi_msgstore_openentry($store, $row[PR_ENTRYID]);
42
-				if (mapi_last_hresult() == 0) {
43
-					$this->deviceStates[$row[PR_DISPLAY_NAME]] = $deviceStateFolder;
44
-
45
-					$deviceStateFolderContents = mapi_folder_getcontentstable($deviceStateFolder, MAPI_ASSOCIATED);
46
-					$restriction = $this->getStateMessageRestriction("devicedata");
47
-					mapi_table_restrict($deviceStateFolderContents, $restriction);
48
-					if (mapi_table_getrowcount($deviceStateFolderContents) == 1) {
49
-						$rows = mapi_table_queryrows($deviceStateFolderContents, [PR_ENTRYID], 0, 1);
50
-						$message = mapi_msgstore_openentry($store, $rows[0][PR_ENTRYID]);
51
-						$state = base64_decode(streamProperty($message, PR_BODY));
52
-						$unserializedState = json_decode($state);
53
-						// fallback for "old-style" states
54
-						if (isset($unserializedState->data->devices)) {
55
-							$devices[$unserializedState->data->devices->{$username}->data->deviceid] = $unserializedState->data->devices->{$username}->data;
56
-						}
57
-						else {
58
-							$devices[$unserializedState->data->deviceid] = $unserializedState->data;
59
-						}
60
-					}
61
-				}
62
-			}
63
-		}
64
-		$this->devices = $devices;
65
-	}
66
-
67
-	/**
68
-	 * Function which triggers full resync of a device.
69
-	 *
70
-	 * @param string $deviceid of phone which has to be resynced
71
-	 *
72
-	 * @return json $response object contains the response of the soap request from grommunio-sync
73
-	 */
74
-	public function resyncDevice($deviceid) {
75
-		$deviceStateFolder = $this->deviceStates[$deviceid];
76
-		if ($deviceStateFolder) {
77
-			try {
78
-				// get the device data message and empty the folder contents
79
-				// create a new message in the folder and copy the device data message to it
80
-				// preventing the devicedata removal for resync
81
-				$deviceStateFolderContents = mapi_folder_getcontentstable($deviceStateFolder, MAPI_ASSOCIATED);
82
-				$restriction = $this->getStateMessageRestriction("devicedata");
83
-				mapi_table_restrict($deviceStateFolderContents, $restriction);
84
-				if (mapi_table_getrowcount($deviceStateFolderContents) == 1) {
85
-					$rows = mapi_table_queryrows($deviceStateFolderContents, [PR_ENTRYID], 0, 1);
86
-					$store = $GLOBALS["mapisession"]->getDefaultMessageStore();
87
-					$message = mapi_msgstore_openentry($store, $rows[0][PR_ENTRYID]);
88
-					mapi_folder_emptyfolder($deviceStateFolder, DEL_ASSOCIATED);
89
-
90
-					$devicedata = mapi_folder_createmessage($deviceStateFolder, MAPI_ASSOCIATED);
91
-					mapi_copyto($message, [], [], $devicedata);
92
-					mapi_setprops($devicedata, [PR_MESSAGE_CLASS => 'IPM.Note.GrommunioState']);
93
-					mapi_savechanges($devicedata);
94
-
95
-					return true;
96
-				}
97
-			}
98
-			catch (Exception $e) {
99
-				error_log(sprintf("mdm plugin resyncDevice Exception: %s", $e));
100
-
101
-				return false;
102
-			}
103
-		}
104
-		error_log(sprintf("mdm plugin resyncDevice device state folder %s", $deviceStateFolder));
105
-
106
-		return false;
107
-	}
108
-
109
-	/**
110
-	 * Function which triggers remote wipe of a device.
111
-	 *
112
-	 * @param string $deviceid of phone which has to be wiped
113
-	 * @param string $password user password
114
-	 *
115
-	 * @return json $response object contains the response of the soap request from grommunio-sync
116
-	 */
117
-	public function wipeDevice($deviceid, $password) {
118
-		$opts = ['http' => [
119
-			'method' => 'POST',
120
-			'header' => 'Content-Type: application/json',
121
-			'ignore_errors' => true,
122
-			'content' => json_encode(
123
-				[
124
-					'password' => $password,
125
-					'remoteIP' => '[::1]',
126
-					'status' => SYNC_PROVISION_RWSTATUS_PENDING,
127
-					'time' => time(),
128
-				]
129
-			),
130
-		],
131
-		];
132
-		$ret = file_get_contents(PLUGIN_MDM_ADMIN_API_WIPE_ENDPOINT . $GLOBALS["mapisession"]->getUserName() . "?devices=" . $deviceid, false, stream_context_create($opts));
133
-
134
-		return $ret;
135
-	}
136
-
137
-	/**
138
-	 * Function which triggers removal of a device.
139
-	 *
140
-	 * @param string $deviceid of phone which has to be wiped
141
-	 * @param string $password user password
142
-	 *
143
-	 * @return json $response object contains the response of the soap request from grommunio-sync
144
-	 */
145
-	public function removeDevice($deviceid, $password) {
146
-		// TODO remove the device from device / user list
147
-		$deviceStateFolder = $this->deviceStates[$deviceid];
148
-		$stateFolder = $this->getStoreStateFolder();
149
-		if ($stateFolder && $deviceStateFolder) {
150
-			$props = mapi_getprops($deviceStateFolder, [PR_ENTRYID]);
151
-
152
-			try {
153
-				mapi_folder_deletefolder($stateFolder, $props[PR_ENTRYID], DEL_MESSAGES);
154
-				$opts = ['http' => [
155
-					'method' => 'POST',
156
-					'header' => 'Content-Type: application/json',
157
-					'ignore_errors' => true,
158
-					'content' => json_encode(
159
-						[
160
-							'password' => $password,
161
-							'remoteIP' => '[::1]',
162
-							'status' => SYNC_PROVISION_RWSTATUS_NA,
163
-							'time' => time(),
164
-						]
165
-					),
166
-				],
167
-				];
168
-				$ret = file_get_contents(PLUGIN_MDM_ADMIN_API_WIPE_ENDPOINT . $GLOBALS["mapisession"]->getUserName() . "?devices=" . $deviceid, false, stream_context_create($opts));
169
-
170
-				return $ret;
171
-			}
172
-			catch (Exception $e) {
173
-				error_log(sprintf("mdm plugin removeDevice Exception: %s", $e));
174
-
175
-				return false;
176
-			}
177
-		}
178
-		error_log(sprintf(
179
-			"mdm plugin removeDevice state folder %s device state folder %s",
180
-			$stateFolder,
181
-			$deviceStateFolder
182
-		));
183
-
184
-		return false;
185
-	}
186
-
187
-	/**
188
-	 * Function to get details of the given device.
189
-	 *
190
-	 * @param string $deviceid id of device
191
-	 *
192
-	 * @return array contains device props
193
-	 */
194
-	public function getDeviceDetails($deviceid) {
195
-		$device = [];
196
-		$device['props'] = $this->getDeviceProps($this->devices[$deviceid]);
197
-		$device['sharedfolders'] = ['item' => $this->getAdditionalFolderList($deviceid)];
198
-
199
-		return $device;
200
-	}
201
-
202
-	/**
203
-	 * Executes all the actions in the $data variable.
204
-	 *
205
-	 * @return bool true on success of false on fialure
206
-	 */
207
-	public function execute() {
208
-		foreach ($this->data as $actionType => $actionData) {
209
-			if (isset($actionType)) {
210
-				try {
211
-					switch ($actionType) {
212
-						case 'wipe':
213
-							$this->wipeDevice($actionData['deviceid'], $actionData['password']);
214
-							$this->addActionData('wipe', [
215
-								'type' => 3,
216
-								'wipe' => $this->wipeDevice($actionData['deviceid'], $actionData['password']),
217
-							]);
218
-							$GLOBALS['bus']->addData($this->getResponseData());
219
-							break;
220
-
221
-						case 'resync':
222
-							$this->addActionData('resync', [
223
-								'type' => 3,
224
-								'resync' => $this->resyncDevice($actionData['deviceid']),
225
-							]);
226
-							$GLOBALS['bus']->addData($this->getResponseData());
227
-							break;
228
-
229
-						case 'remove':
230
-							$this->addActionData('remove', [
231
-								'type' => 3,
232
-								'remove' => $this->removeDevice($actionData['deviceid'], $actionData['password']),
233
-							]);
234
-							$GLOBALS['bus']->addData($this->getResponseData());
235
-							break;
236
-
237
-						case 'list':
238
-							$items = [];
239
-							$data['page'] = [];
240
-
241
-							foreach ($this->devices as $device) {
242
-								array_push($items, ['props' => $this->getDeviceProps($device)]);
243
-							}
244
-							$data['page']['start'] = 0;
245
-							$data['page']['rowcount'] = count($this->devices);
246
-							$data['page']['totalrowcount'] = $data['page']['rowcount'];
247
-							$data = array_merge($data, ['item' => $items]);
248
-							$this->addActionData('list', $data);
249
-							$GLOBALS['bus']->addData($this->getResponseData());
250
-							break;
251
-
252
-						case 'open':
253
-							$device = $this->getDeviceDetails($actionData["entryid"]);
254
-							$item = ["item" => $device];
255
-							$this->addActionData('item', $item);
256
-							$GLOBALS['bus']->addData($this->getResponseData());
257
-							break;
258
-
259
-						case 'save':
260
-							$this->saveDevice($actionData)
261
-							;
262
-							$device = $this->getDeviceDetails($actionData["entryid"]);
263
-							$item = ["item" => $device];
264
-							$this->addActionData('update', $item);
265
-							$GLOBALS['bus']->addData($this->getResponseData());
266
-							break;
267
-
268
-						default:
269
-							$this->handleUnknownActionType($actionType);
270
-					}
271
-				}
272
-				catch (Exception $e) {
273
-					$title = _('Mobile device management plugin');
274
-					$display_message = sprintf(_('Unexpected error occurred. Please contact your system administrator. Error code: %s'), $e->getMessage());
275
-					$this->sendFeedback(true, ["type" => ERROR_GENERAL, "info" => ['title' => $title, 'display_message' => $display_message]]);
276
-				}
277
-			}
278
-		}
279
-	}
280
-
281
-	/**
282
-	 * Function which is use to get device properties.
283
-	 *
284
-	 * @param object $device array of device properties
285
-	 *
286
-	 * @return array
287
-	 */
288
-	public function getDeviceProps($device) {
289
-		$item = [];
290
-		$propsList = ['devicetype', 'deviceos', 'devicefriendlyname', 'useragent', 'asversion', 'firstsynctime',
291
-			'lastsynctime', 'lastupdatetime', 'policyname', ];
292
-
293
-		$item['entryid'] = $device->deviceid;
294
-		$item['message_class'] = "IPM.MDM";
295
-		foreach ($propsList as $prop) {
296
-			if (isset($device->{$prop})) {
297
-				$item[$prop] = $device->{$prop};
298
-			}
299
-		}
300
-		$item['wipestatus'] = $this->getProvisioningWipeStatus($device->deviceid);
301
-
302
-		return array_merge($item, $this->getSyncFoldersProps($device));
303
-	}
304
-
305
-	/**
306
-	 * Function which is use to gather some statistics about synchronized folders.
307
-	 *
308
-	 * @param array $device array of device props
309
-	 *
310
-	 * @return array $syncFoldersProps has list of properties related to synchronized folders
311
-	 */
312
-	public function getSyncFoldersProps($device) {
313
-		$synchedFolderTypes = [];
314
-		$synchronizedFolders = 0;
315
-
316
-		foreach ($device->contentdata as $folderid => $folderdata) {
317
-			if (isset($folderdata->{self::FOLDERUUID})) {
318
-				$type = $folderdata->{self::FOLDERTYPE};
319
-
320
-				$folderType = $this->getSyncFolderType($type);
321
-				if (isset($synchedFolderTypes[$folderType])) {
322
-					++$synchedFolderTypes[$folderType];
323
-				}
324
-				else {
325
-					$synchedFolderTypes[$folderType] = 1;
326
-				}
327
-			}
328
-		}
329
-		$syncFoldersProps = [];
330
-		foreach ($synchedFolderTypes as $key => $value) {
331
-			$synchronizedFolders += $value;
332
-			$syncFoldersProps[strtolower($key) . 'folder'] = $value;
333
-		}
334
-		/*
10
+    // content data
11
+    public const FOLDERUUID = 1;
12
+    public const FOLDERTYPE = 2;
13
+    public const FOLDERBACKENDID = 5;
14
+
15
+    /**
16
+     * Constructor.
17
+     *
18
+     * @param int   $id   unique id
19
+     * @param array $data list of all actions
20
+     */
21
+    public function __construct($id, $data) {
22
+        parent::__construct($id, $data);
23
+        $this->stateFolder = null;
24
+        $this->deviceStates = [];
25
+        $this->devices = [];
26
+        $this->setupDevices();
27
+    }
28
+
29
+    /**
30
+     * Function sets up the array with the user's devices.
31
+     */
32
+    public function setupDevices() {
33
+        $devices = [];
34
+        $stateFolder = $this->getStoreStateFolder();
35
+        if ($stateFolder) {
36
+            $store = $GLOBALS["mapisession"]->getDefaultMessageStore();
37
+            $username = $GLOBALS["mapisession"]->getUserName();
38
+            $hierarchyTable = mapi_folder_gethierarchytable($stateFolder, CONVENIENT_DEPTH | MAPI_DEFERRED_ERRORS);
39
+            $rows = mapi_table_queryallrows($hierarchyTable, [PR_ENTRYID, PR_DISPLAY_NAME]);
40
+            foreach ($rows as $row) {
41
+                $deviceStateFolder = mapi_msgstore_openentry($store, $row[PR_ENTRYID]);
42
+                if (mapi_last_hresult() == 0) {
43
+                    $this->deviceStates[$row[PR_DISPLAY_NAME]] = $deviceStateFolder;
44
+
45
+                    $deviceStateFolderContents = mapi_folder_getcontentstable($deviceStateFolder, MAPI_ASSOCIATED);
46
+                    $restriction = $this->getStateMessageRestriction("devicedata");
47
+                    mapi_table_restrict($deviceStateFolderContents, $restriction);
48
+                    if (mapi_table_getrowcount($deviceStateFolderContents) == 1) {
49
+                        $rows = mapi_table_queryrows($deviceStateFolderContents, [PR_ENTRYID], 0, 1);
50
+                        $message = mapi_msgstore_openentry($store, $rows[0][PR_ENTRYID]);
51
+                        $state = base64_decode(streamProperty($message, PR_BODY));
52
+                        $unserializedState = json_decode($state);
53
+                        // fallback for "old-style" states
54
+                        if (isset($unserializedState->data->devices)) {
55
+                            $devices[$unserializedState->data->devices->{$username}->data->deviceid] = $unserializedState->data->devices->{$username}->data;
56
+                        }
57
+                        else {
58
+                            $devices[$unserializedState->data->deviceid] = $unserializedState->data;
59
+                        }
60
+                    }
61
+                }
62
+            }
63
+        }
64
+        $this->devices = $devices;
65
+    }
66
+
67
+    /**
68
+     * Function which triggers full resync of a device.
69
+     *
70
+     * @param string $deviceid of phone which has to be resynced
71
+     *
72
+     * @return json $response object contains the response of the soap request from grommunio-sync
73
+     */
74
+    public function resyncDevice($deviceid) {
75
+        $deviceStateFolder = $this->deviceStates[$deviceid];
76
+        if ($deviceStateFolder) {
77
+            try {
78
+                // get the device data message and empty the folder contents
79
+                // create a new message in the folder and copy the device data message to it
80
+                // preventing the devicedata removal for resync
81
+                $deviceStateFolderContents = mapi_folder_getcontentstable($deviceStateFolder, MAPI_ASSOCIATED);
82
+                $restriction = $this->getStateMessageRestriction("devicedata");
83
+                mapi_table_restrict($deviceStateFolderContents, $restriction);
84
+                if (mapi_table_getrowcount($deviceStateFolderContents) == 1) {
85
+                    $rows = mapi_table_queryrows($deviceStateFolderContents, [PR_ENTRYID], 0, 1);
86
+                    $store = $GLOBALS["mapisession"]->getDefaultMessageStore();
87
+                    $message = mapi_msgstore_openentry($store, $rows[0][PR_ENTRYID]);
88
+                    mapi_folder_emptyfolder($deviceStateFolder, DEL_ASSOCIATED);
89
+
90
+                    $devicedata = mapi_folder_createmessage($deviceStateFolder, MAPI_ASSOCIATED);
91
+                    mapi_copyto($message, [], [], $devicedata);
92
+                    mapi_setprops($devicedata, [PR_MESSAGE_CLASS => 'IPM.Note.GrommunioState']);
93
+                    mapi_savechanges($devicedata);
94
+
95
+                    return true;
96
+                }
97
+            }
98
+            catch (Exception $e) {
99
+                error_log(sprintf("mdm plugin resyncDevice Exception: %s", $e));
100
+
101
+                return false;
102
+            }
103
+        }
104
+        error_log(sprintf("mdm plugin resyncDevice device state folder %s", $deviceStateFolder));
105
+
106
+        return false;
107
+    }
108
+
109
+    /**
110
+     * Function which triggers remote wipe of a device.
111
+     *
112
+     * @param string $deviceid of phone which has to be wiped
113
+     * @param string $password user password
114
+     *
115
+     * @return json $response object contains the response of the soap request from grommunio-sync
116
+     */
117
+    public function wipeDevice($deviceid, $password) {
118
+        $opts = ['http' => [
119
+            'method' => 'POST',
120
+            'header' => 'Content-Type: application/json',
121
+            'ignore_errors' => true,
122
+            'content' => json_encode(
123
+                [
124
+                    'password' => $password,
125
+                    'remoteIP' => '[::1]',
126
+                    'status' => SYNC_PROVISION_RWSTATUS_PENDING,
127
+                    'time' => time(),
128
+                ]
129
+            ),
130
+        ],
131
+        ];
132
+        $ret = file_get_contents(PLUGIN_MDM_ADMIN_API_WIPE_ENDPOINT . $GLOBALS["mapisession"]->getUserName() . "?devices=" . $deviceid, false, stream_context_create($opts));
133
+
134
+        return $ret;
135
+    }
136
+
137
+    /**
138
+     * Function which triggers removal of a device.
139
+     *
140
+     * @param string $deviceid of phone which has to be wiped
141
+     * @param string $password user password
142
+     *
143
+     * @return json $response object contains the response of the soap request from grommunio-sync
144
+     */
145
+    public function removeDevice($deviceid, $password) {
146
+        // TODO remove the device from device / user list
147
+        $deviceStateFolder = $this->deviceStates[$deviceid];
148
+        $stateFolder = $this->getStoreStateFolder();
149
+        if ($stateFolder && $deviceStateFolder) {
150
+            $props = mapi_getprops($deviceStateFolder, [PR_ENTRYID]);
151
+
152
+            try {
153
+                mapi_folder_deletefolder($stateFolder, $props[PR_ENTRYID], DEL_MESSAGES);
154
+                $opts = ['http' => [
155
+                    'method' => 'POST',
156
+                    'header' => 'Content-Type: application/json',
157
+                    'ignore_errors' => true,
158
+                    'content' => json_encode(
159
+                        [
160
+                            'password' => $password,
161
+                            'remoteIP' => '[::1]',
162
+                            'status' => SYNC_PROVISION_RWSTATUS_NA,
163
+                            'time' => time(),
164
+                        ]
165
+                    ),
166
+                ],
167
+                ];
168
+                $ret = file_get_contents(PLUGIN_MDM_ADMIN_API_WIPE_ENDPOINT . $GLOBALS["mapisession"]->getUserName() . "?devices=" . $deviceid, false, stream_context_create($opts));
169
+
170
+                return $ret;
171
+            }
172
+            catch (Exception $e) {
173
+                error_log(sprintf("mdm plugin removeDevice Exception: %s", $e));
174
+
175
+                return false;
176
+            }
177
+        }
178
+        error_log(sprintf(
179
+            "mdm plugin removeDevice state folder %s device state folder %s",
180
+            $stateFolder,
181
+            $deviceStateFolder
182
+        ));
183
+
184
+        return false;
185
+    }
186
+
187
+    /**
188
+     * Function to get details of the given device.
189
+     *
190
+     * @param string $deviceid id of device
191
+     *
192
+     * @return array contains device props
193
+     */
194
+    public function getDeviceDetails($deviceid) {
195
+        $device = [];
196
+        $device['props'] = $this->getDeviceProps($this->devices[$deviceid]);
197
+        $device['sharedfolders'] = ['item' => $this->getAdditionalFolderList($deviceid)];
198
+
199
+        return $device;
200
+    }
201
+
202
+    /**
203
+     * Executes all the actions in the $data variable.
204
+     *
205
+     * @return bool true on success of false on fialure
206
+     */
207
+    public function execute() {
208
+        foreach ($this->data as $actionType => $actionData) {
209
+            if (isset($actionType)) {
210
+                try {
211
+                    switch ($actionType) {
212
+                        case 'wipe':
213
+                            $this->wipeDevice($actionData['deviceid'], $actionData['password']);
214
+                            $this->addActionData('wipe', [
215
+                                'type' => 3,
216
+                                'wipe' => $this->wipeDevice($actionData['deviceid'], $actionData['password']),
217
+                            ]);
218
+                            $GLOBALS['bus']->addData($this->getResponseData());
219
+                            break;
220
+
221
+                        case 'resync':
222
+                            $this->addActionData('resync', [
223
+                                'type' => 3,
224
+                                'resync' => $this->resyncDevice($actionData['deviceid']),
225
+                            ]);
226
+                            $GLOBALS['bus']->addData($this->getResponseData());
227
+                            break;
228
+
229
+                        case 'remove':
230
+                            $this->addActionData('remove', [
231
+                                'type' => 3,
232
+                                'remove' => $this->removeDevice($actionData['deviceid'], $actionData['password']),
233
+                            ]);
234
+                            $GLOBALS['bus']->addData($this->getResponseData());
235
+                            break;
236
+
237
+                        case 'list':
238
+                            $items = [];
239
+                            $data['page'] = [];
240
+
241
+                            foreach ($this->devices as $device) {
242
+                                array_push($items, ['props' => $this->getDeviceProps($device)]);
243
+                            }
244
+                            $data['page']['start'] = 0;
245
+                            $data['page']['rowcount'] = count($this->devices);
246
+                            $data['page']['totalrowcount'] = $data['page']['rowcount'];
247
+                            $data = array_merge($data, ['item' => $items]);
248
+                            $this->addActionData('list', $data);
249
+                            $GLOBALS['bus']->addData($this->getResponseData());
250
+                            break;
251
+
252
+                        case 'open':
253
+                            $device = $this->getDeviceDetails($actionData["entryid"]);
254
+                            $item = ["item" => $device];
255
+                            $this->addActionData('item', $item);
256
+                            $GLOBALS['bus']->addData($this->getResponseData());
257
+                            break;
258
+
259
+                        case 'save':
260
+                            $this->saveDevice($actionData)
261
+                            ;
262
+                            $device = $this->getDeviceDetails($actionData["entryid"]);
263
+                            $item = ["item" => $device];
264
+                            $this->addActionData('update', $item);
265
+                            $GLOBALS['bus']->addData($this->getResponseData());
266
+                            break;
267
+
268
+                        default:
269
+                            $this->handleUnknownActionType($actionType);
270
+                    }
271
+                }
272
+                catch (Exception $e) {
273
+                    $title = _('Mobile device management plugin');
274
+                    $display_message = sprintf(_('Unexpected error occurred. Please contact your system administrator. Error code: %s'), $e->getMessage());
275
+                    $this->sendFeedback(true, ["type" => ERROR_GENERAL, "info" => ['title' => $title, 'display_message' => $display_message]]);
276
+                }
277
+            }
278
+        }
279
+    }
280
+
281
+    /**
282
+     * Function which is use to get device properties.
283
+     *
284
+     * @param object $device array of device properties
285
+     *
286
+     * @return array
287
+     */
288
+    public function getDeviceProps($device) {
289
+        $item = [];
290
+        $propsList = ['devicetype', 'deviceos', 'devicefriendlyname', 'useragent', 'asversion', 'firstsynctime',
291
+            'lastsynctime', 'lastupdatetime', 'policyname', ];
292
+
293
+        $item['entryid'] = $device->deviceid;
294
+        $item['message_class'] = "IPM.MDM";
295
+        foreach ($propsList as $prop) {
296
+            if (isset($device->{$prop})) {
297
+                $item[$prop] = $device->{$prop};
298
+            }
299
+        }
300
+        $item['wipestatus'] = $this->getProvisioningWipeStatus($device->deviceid);
301
+
302
+        return array_merge($item, $this->getSyncFoldersProps($device));
303
+    }
304
+
305
+    /**
306
+     * Function which is use to gather some statistics about synchronized folders.
307
+     *
308
+     * @param array $device array of device props
309
+     *
310
+     * @return array $syncFoldersProps has list of properties related to synchronized folders
311
+     */
312
+    public function getSyncFoldersProps($device) {
313
+        $synchedFolderTypes = [];
314
+        $synchronizedFolders = 0;
315
+
316
+        foreach ($device->contentdata as $folderid => $folderdata) {
317
+            if (isset($folderdata->{self::FOLDERUUID})) {
318
+                $type = $folderdata->{self::FOLDERTYPE};
319
+
320
+                $folderType = $this->getSyncFolderType($type);
321
+                if (isset($synchedFolderTypes[$folderType])) {
322
+                    ++$synchedFolderTypes[$folderType];
323
+                }
324
+                else {
325
+                    $synchedFolderTypes[$folderType] = 1;
326
+                }
327
+            }
328
+        }
329
+        $syncFoldersProps = [];
330
+        foreach ($synchedFolderTypes as $key => $value) {
331
+            $synchronizedFolders += $value;
332
+            $syncFoldersProps[strtolower($key) . 'folder'] = $value;
333
+        }
334
+        /*
335 335
 		TODO getAdditionalFolderList
336 336
 		$client = $this->getSoapClient();
337 337
 		$items = $client->AdditionalFolderList($device['deviceid']);
@@ -339,641 +339,641 @@  discard block
 block discarded – undo
339 339
 		$syncFoldersProps["shortfolderids"] = $device['hasfolderidmapping'] ? _("Yes") : _("No");
340 340
 		$syncFoldersProps['synchronizedfolders'] = $synchronizedFolders + count($items);
341 341
 		*/
342
-		$syncFoldersProps['synchronizedfolders'] = $synchronizedFolders;
343
-
344
-		return $syncFoldersProps;
345
-	}
346
-
347
-	/**
348
-	 * Function which is use to get general type like Mail,Calendar,Contacts,etc. from folder type.
349
-	 *
350
-	 * @param int    $type foldertype for a folder already known to the mobile
351
-	 * @param string $name folder name
352
-	 *
353
-	 * @return string general folder type
354
-	 */
355
-	public function getSyncFolderType($type) {
356
-		switch ($type) {
357
-			case SYNC_FOLDER_TYPE_APPOINTMENT:
358
-			case SYNC_FOLDER_TYPE_USER_APPOINTMENT:
359
-					$folderType = "Calendars";
360
-				break;
361
-
362
-			case SYNC_FOLDER_TYPE_CONTACT:
363
-			case SYNC_FOLDER_TYPE_USER_CONTACT:
364
-				$folderType = "Contacts";
365
-				break;
366
-
367
-			case SYNC_FOLDER_TYPE_TASK:
368
-			case SYNC_FOLDER_TYPE_USER_TASK:
369
-				$folderType = "Tasks";
370
-				break;
371
-
372
-			case SYNC_FOLDER_TYPE_NOTE:
373
-			case SYNC_FOLDER_TYPE_USER_NOTE:
374
-				$folderType = "Notes";
375
-				break;
376
-
377
-			default:
378
-				$folderType = "Emails";
379
-				break;
380
-		}
381
-
382
-		return $folderType;
383
-	}
384
-
385
-	/**
386
-	 * Function which is use to get list of additional folders which was shared with given device.
387
-	 *
388
-	 * @param string $devid device id
389
-	 *
390
-	 * @return array has list of properties related to shared folders
391
-	 */
392
-	public function getAdditionalFolderList($devid) {
393
-		return [];
394
-		// TODO implement
395
-		$stores = $GLOBALS["mapisession"]->getAllMessageStores();
396
-		$client = $this->getSoapClient();
397
-		$items = $client->AdditionalFolderList($devid);
398
-		$data = [];
399
-		foreach ($items as $item) {
400
-			foreach ($stores as $store) {
401
-				try {
402
-					$entryid = mapi_msgstore_entryidfromsourcekey($store, hex2bin($item->folderid));
403
-				}
404
-				catch (MAPIException $me) {
405
-					continue;
406
-				}
407
-			}
408
-			if (isset($entryid)) {
409
-				$item->entryid = bin2hex($entryid);
410
-			}
411
-			array_push($data, ["props" => $item]);
412
-		}
413
-
414
-		return $data;
415
-	}
416
-
417
-	/**
418
-	 * Function which is use to remove additional folder which was shared with given device.
419
-	 *
420
-	 * @param string $entryId  id of device
421
-	 * @param string $folderid id of folder which will remove from device
422
-	 */
423
-	public function additionalFolderRemove($entryId, $folderid) {
424
-		$client = $this->getSoapClient();
425
-		$client->AdditionalFolderRemove($entryId, $folderid);
426
-	}
427
-
428
-	/**
429
-	 * Function which is use to add additional folder which will share with given device.
430
-	 *
431
-	 * @param string $entryId id of device
432
-	 * @param array  $folder  folder which will share with device
433
-	 */
434
-	public function additionalFolderAdd($entryId, $folder) {
435
-		$client = $this->getSoapClient();
436
-		$containerClass = isset($folder[PR_CONTAINER_CLASS]) ? $folder[PR_CONTAINER_CLASS] : "IPF.Note";
437
-		$folderId = bin2hex($folder[PR_SOURCE_KEY]);
438
-		$userName = $folder["user"];
439
-		$folderName = $userName === "SYSTEM" ? $folder[PR_DISPLAY_NAME] : $folder[PR_DISPLAY_NAME] . " - " . $userName;
440
-		$folderType = $this->getFolderTypeFromContainerClass($containerClass);
441
-		$client->AdditionalFolderAdd($entryId, $userName, $folderId, $folderName, $folderType, FLD_FLAGS_REPLYASUSER);
442
-	}
443
-
444
-	/**
445
-	 * Function which use to save the device.
446
-	 * It will use to add or remove folders in the device.
447
-	 *
448
-	 * @param array $data array of added and removed folders
449
-	 */
450
-	public function saveDevice($data) {
451
-		$entryid = $data["entryid"];
452
-		if (isset($data['sharedfolders'])) {
453
-			if (isset($data['sharedfolders']['remove'])) {
454
-				$deletedFolders = $data['sharedfolders']['remove'];
455
-				foreach ($deletedFolders as $folder) {
456
-					$this->additionalFolderRemove($entryid, $folder["folderid"]);
457
-				}
458
-			}
459
-			if (isset($data['sharedfolders']['add'])) {
460
-				$addFolders = $data['sharedfolders']['add'];
461
-				$hierarchyFolders = $this->getHierarchyList();
462
-				foreach ($addFolders as $folder) {
463
-					foreach ($hierarchyFolders as $hierarchyFolder) {
464
-						$folderEntryid = bin2hex($hierarchyFolder[PR_ENTRYID]);
465
-						if ($folderEntryid === $folder["entryid"]) {
466
-							$this->additionalFolderAdd($entryid, $hierarchyFolder);
467
-
468
-							continue 2;
469
-						}
470
-					}
471
-				}
472
-			}
473
-		}
474
-	}
475
-
476
-	/**
477
-	 * Gets the hierarchy list of all required stores.
478
-	 * Function which is use to get the hierarchy list with PR_SOURCE_KEY.
479
-	 *
480
-	 * @return array the array of all hierarchy folders
481
-	 */
482
-	public function getHierarchyList() {
483
-		$storeList = $GLOBALS["mapisession"]->getAllMessageStores();
484
-		$properties = $GLOBALS["properties"]->getFolderListProperties();
485
-		$otherUsers = $GLOBALS["mapisession"]->retrieveOtherUsersFromSettings();
486
-		$properties["source_key"] = PR_SOURCE_KEY;
487
-		$openWholeStore = true;
488
-		$storeData = [];
489
-
490
-		foreach ($storeList as $store) {
491
-			$msgstore_props = mapi_getprops($store, [PR_MDB_PROVIDER, PR_ENTRYID, PR_IPM_SUBTREE_ENTRYID, PR_USER_NAME]);
492
-			$storeType = $msgstore_props[PR_MDB_PROVIDER];
493
-
494
-			if ($storeType == ZARAFA_SERVICE_GUID) {
495
-				continue;
496
-			}
497
-			if ($storeType == ZARAFA_STORE_DELEGATE_GUID) {
498
-				$storeUserName = $GLOBALS["mapisession"]->getUserNameOfStore($msgstore_props[PR_ENTRYID]);
499
-			}
500
-			elseif ($storeType == ZARAFA_STORE_PUBLIC_GUID) {
501
-				$storeUserName = "SYSTEM";
502
-			}
503
-			else {
504
-				$storeUserName = $msgstore_props[PR_USER_NAME];
505
-			}
506
-
507
-			if (is_array($otherUsers)) {
508
-				if (isset($otherUsers[$storeUserName])) {
509
-					$sharedFolders = $otherUsers[$storeUserName];
510
-					if (!isset($otherUsers[$storeUserName]['all'])) {
511
-						$openWholeStore = false;
512
-						$a = $this->getSharedFolderList($store, $sharedFolders, $properties, $storeUserName);
513
-						$storeData = array_merge($storeData, $a);
514
-					}
515
-				}
516
-			}
517
-
518
-			if ($openWholeStore) {
519
-				if (isset($msgstore_props[PR_IPM_SUBTREE_ENTRYID])) {
520
-					$subtreeFolderEntryID = $msgstore_props[PR_IPM_SUBTREE_ENTRYID];
521
-
522
-					try {
523
-						$subtreeFolder = mapi_msgstore_openentry($store, $subtreeFolderEntryID);
524
-					}
525
-					catch (MAPIException $e) {
526
-						// We've handled the event
527
-						$e->setHandled();
528
-					}
529
-
530
-					if ($storeType != ZARAFA_STORE_PUBLIC_GUID) {
531
-						$this->getSubFolders($subtreeFolder, $store, $properties, $storeData, $storeUserName);
532
-					}
533
-					else {
534
-						$this->getSubFoldersPublic($subtreeFolder, $store, $properties, $storeData, $storeUserName);
535
-					}
536
-				}
537
-			}
538
-		}
539
-
540
-		return $storeData;
541
-	}
542
-
543
-	/**
544
-	 * Helper function to get the shared folder list.
545
-	 *
546
-	 * @param object $store         message Store Object
547
-	 * @param object $sharedFolders mapi Folder Object
548
-	 * @param array  $properties    MAPI property mappings for folders
549
-	 * @param string $storeUserName owner name of store
550
-	 *
551
-	 * @return array shared folders list
552
-	 */
553
-	public function getSharedFolderList($store, $sharedFolders, $properties, $storeUserName) {
554
-		$msgstore_props = mapi_getprops($store, [PR_ENTRYID, PR_DISPLAY_NAME, PR_IPM_SUBTREE_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID, PR_MDB_PROVIDER, PR_IPM_PUBLIC_FOLDERS_ENTRYID, PR_IPM_FAVORITES_ENTRYID, PR_OBJECT_TYPE, PR_STORE_SUPPORT_MASK, PR_MAILBOX_OWNER_ENTRYID, PR_MAILBOX_OWNER_NAME, PR_USER_ENTRYID, PR_USER_NAME, PR_QUOTA_WARNING_THRESHOLD, PR_QUOTA_SEND_THRESHOLD, PR_QUOTA_RECEIVE_THRESHOLD, PR_MESSAGE_SIZE_EXTENDED, PR_MAPPING_SIGNATURE, PR_COMMON_VIEWS_ENTRYID, PR_FINDER_ENTRYID]);
555
-		$storeData = [];
556
-		$folders = [];
557
-
558
-		try {
559
-			$inbox = mapi_msgstore_getreceivefolder($store);
560
-			$inboxProps = mapi_getprops($inbox, [PR_ENTRYID]);
561
-		}
562
-		catch (MAPIException $e) {
563
-			// don't propagate this error to parent handlers, if store doesn't support it
564
-			if ($e->getCode() === MAPI_E_NO_SUPPORT) {
565
-				$e->setHandled();
566
-			}
567
-		}
568
-
569
-		$root = mapi_msgstore_openentry($store, null);
570
-		$rootProps = mapi_getprops($root, [PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_JOURNAL_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_TASK_ENTRYID, PR_ADDITIONAL_REN_ENTRYIDS]);
571
-
572
-		$additional_ren_entryids = [];
573
-		if (isset($rootProps[PR_ADDITIONAL_REN_ENTRYIDS])) {
574
-			$additional_ren_entryids = $rootProps[PR_ADDITIONAL_REN_ENTRYIDS];
575
-		}
576
-
577
-		$defaultfolders = [
578
-			"default_folder_inbox" => ["inbox" => PR_ENTRYID],
579
-			"default_folder_outbox" => ["store" => PR_IPM_OUTBOX_ENTRYID],
580
-			"default_folder_sent" => ["store" => PR_IPM_SENTMAIL_ENTRYID],
581
-			"default_folder_wastebasket" => ["store" => PR_IPM_WASTEBASKET_ENTRYID],
582
-			"default_folder_favorites" => ["store" => PR_IPM_FAVORITES_ENTRYID],
583
-			"default_folder_publicfolders" => ["store" => PR_IPM_PUBLIC_FOLDERS_ENTRYID],
584
-			"default_folder_calendar" => ["root" => PR_IPM_APPOINTMENT_ENTRYID],
585
-			"default_folder_contact" => ["root" => PR_IPM_CONTACT_ENTRYID],
586
-			"default_folder_drafts" => ["root" => PR_IPM_DRAFTS_ENTRYID],
587
-			"default_folder_journal" => ["root" => PR_IPM_JOURNAL_ENTRYID],
588
-			"default_folder_note" => ["root" => PR_IPM_NOTE_ENTRYID],
589
-			"default_folder_task" => ["root" => PR_IPM_TASK_ENTRYID],
590
-			"default_folder_junk" => ["additional" => 4],
591
-			"default_folder_syncissues" => ["additional" => 1],
592
-			"default_folder_conflicts" => ["additional" => 0],
593
-			"default_folder_localfailures" => ["additional" => 2],
594
-			"default_folder_serverfailures" => ["additional" => 3],
595
-		];
596
-
597
-		foreach ($defaultfolders as $key => $prop) {
598
-			$tag = reset($prop);
599
-			$from = key($prop);
600
-
601
-			switch ($from) {
602
-				case "inbox":
603
-					if (isset($inboxProps[$tag])) {
604
-						$storeData["props"][$key] = bin2hex($inboxProps[$tag]);
605
-					}
606
-					break;
607
-
608
-				case "store":
609
-					if (isset($msgstore_props[$tag])) {
610
-						$storeData["props"][$key] = bin2hex($msgstore_props[$tag]);
611
-					}
612
-					break;
613
-
614
-				case "root":
615
-					if (isset($rootProps[$tag])) {
616
-						$storeData["props"][$key] = bin2hex($rootProps[$tag]);
617
-					}
618
-					break;
619
-
620
-				case "additional":
621
-					if (isset($additional_ren_entryids[$tag])) {
622
-						$storeData["props"][$key] = bin2hex($additional_ren_entryids[$tag]);
623
-					}
624
-					break;
625
-			}
626
-		}
627
-
628
-		$store_access = true;
629
-		$openSubFolders = false;
630
-		foreach ($sharedFolders as $type => $sharedFolder) {
631
-			$openSubFolders = ($sharedFolder["show_subfolders"] == true);
632
-			$folderEntryID = hex2bin($storeData["props"]["default_folder_" . $sharedFolder["folder_type"]]);
633
-
634
-			try {
635
-				// load folder props
636
-				$folder = mapi_msgstore_openentry($store, $folderEntryID);
637
-			}
638
-			catch (MAPIException $e) {
639
-				// Indicate that we don't have access to the store,
640
-				// so no more attempts to read properties or open entries.
641
-				$store_access = false;
642
-
643
-				// We've handled the event
644
-				$e->setHandled();
645
-			}
646
-		}
647
-
648
-		if ($store_access === true) {
649
-			$folderProps = mapi_getprops($folder, $properties);
650
-			$folderProps["user"] = $storeUserName;
651
-			array_push($folders, $folderProps);
652
-
653
-			// If folder has sub folders then add its.
654
-			if ($openSubFolders === true) {
655
-				if ($folderProps[PR_SUBFOLDERS] != false) {
656
-					$subFoldersData = [];
657
-					$this->getSubFolders($folder, $store, $properties, $subFoldersData, $storeUserName);
658
-					$folders = array_merge($folders, $subFoldersData);
659
-				}
660
-			}
661
-		}
662
-
663
-		return $folders;
664
-	}
665
-
666
-	/**
667
-	 * Helper function to get the sub folders of a given folder.
668
-	 *
669
-	 * @param object $folder        mapi Folder Object
670
-	 * @param object $store         Message Store Object
671
-	 * @param array  $properties    MAPI property mappings for folders
672
-	 * @param array  $storeData     Reference to an array. The folder properties are added to this array.
673
-	 * @param string $storeUserName owner name of store
674
-	 */
675
-	public function getSubFolders($folder, $store, $properties, &$storeData, $storeUserName) {
676
-		/**
677
-		 * remove hidden folders, folders with PR_ATTR_HIDDEN property set
678
-		 * should not be shown to the client.
679
-		 */
680
-		$restriction = [RES_OR, [
681
-			[RES_PROPERTY,
682
-				[
683
-					RELOP => RELOP_EQ,
684
-					ULPROPTAG => PR_ATTR_HIDDEN,
685
-					VALUE => [PR_ATTR_HIDDEN => false],
686
-				],
687
-			],
688
-			[RES_NOT,
689
-				[
690
-					[RES_EXIST,
691
-						[
692
-							ULPROPTAG => PR_ATTR_HIDDEN,
693
-						],
694
-					],
695
-				],
696
-			],
697
-		]];
698
-
699
-		$expand = [
700
-			[
701
-				'folder' => $folder,
702
-				'props' => mapi_getprops($folder, [PR_ENTRYID, PR_SUBFOLDERS]),
703
-			],
704
-		];
705
-
706
-		// Start looping through the $expand array, during each loop we grab the first item in
707
-		// the array and obtain the hierarchy table for that particular folder. If one of those
708
-		// subfolders has subfolders of its own, it will be appended to $expand again to ensure
709
-		// it will be expanded later.
710
-		while (!empty($expand)) {
711
-			$item = array_shift($expand);
712
-			$columns = $properties;
713
-
714
-			$hierarchyTable = mapi_folder_gethierarchytable($item['folder'], MAPI_DEFERRED_ERRORS);
715
-			mapi_table_restrict($hierarchyTable, $restriction, TBL_BATCH);
716
-
717
-			mapi_table_setcolumns($hierarchyTable, $columns);
718
-			$columns = null;
719
-
720
-			// Load the hierarchy in small batches
721
-			$batchcount = 100;
722
-			do {
723
-				$rows = mapi_table_queryrows($hierarchyTable, $columns, 0, $batchcount);
724
-
725
-				foreach ($rows as $subfolder) {
726
-					// If the subfolders has subfolders of its own, append the folder
727
-					// to the $expand array, so it can be expanded in the next loop.
728
-					if ($subfolder[PR_SUBFOLDERS]) {
729
-						$folderObject = mapi_msgstore_openentry($store, $subfolder[PR_ENTRYID]);
730
-						array_push($expand, ['folder' => $folderObject, 'props' => $subfolder]);
731
-					}
732
-					$subfolder["user"] = $storeUserName;
733
-					// Add the folder to the return list.
734
-					array_push($storeData, $subfolder);
735
-				}
736
-
737
-				// When the server returned a different number of rows then was requested,
738
-				// we have reached the end of the table and we should exit the loop.
739
-			}
740
-			while (count($rows) === $batchcount);
741
-		}
742
-	}
743
-
744
-	/**
745
-	 * Helper function to get the subfolders of a Public Store.
746
-	 *
747
-	 * @param object $folder        mapi Folder Object
748
-	 * @param object $store         Message Store Object
749
-	 * @param array  $properties    MAPI property mappings for folders
750
-	 * @param array  $storeData     Reference to an array. The folder properties are added to this array.
751
-	 * @param string $storeUserName owner name of store
752
-	 */
753
-	public function getSubFoldersPublic($folder, $store, $properties, &$storeData, $storeUserName) {
754
-		$expand = [
755
-			[
756
-				'folder' => $folder,
757
-				'props' => mapi_getprops($folder, [PR_ENTRYID, PR_SUBFOLDERS]),
758
-			],
759
-		];
760
-
761
-		/**
762
-		 * remove hidden folders, folders with PR_ATTR_HIDDEN property set
763
-		 * should not be shown to the client.
764
-		 */
765
-		$restriction = [RES_OR, [
766
-			[RES_PROPERTY,
767
-				[
768
-					RELOP => RELOP_EQ,
769
-					ULPROPTAG => PR_ATTR_HIDDEN,
770
-					VALUE => [PR_ATTR_HIDDEN => false],
771
-				],
772
-			],
773
-			[RES_NOT,
774
-				[
775
-					[RES_EXIST,
776
-						[
777
-							ULPROPTAG => PR_ATTR_HIDDEN,
778
-						],
779
-					],
780
-				],
781
-			],
782
-		]];
783
-
784
-		// CONVENIENT_DEPTH doesn't work on the IPM_SUBTREE, hence we will be recursively
785
-		// walking through the hierarchy. However, we have some special folders like the
786
-		// "Favorites" and "Public Folders" from where we can switch to using
787
-		// CONVENIENT_DEPTH. Obtain these special cases here.
788
-		$specialEntryids = mapi_getprops($store, [
789
-			PR_IPM_FAVORITES_ENTRYID,
790
-			PR_IPM_PUBLIC_FOLDERS_ENTRYID,
791
-		]);
792
-
793
-		// Start looping through the $expand array, during each loop we grab the first item in
794
-		// the array and obtain the hierarchy table for that particular folder. If one of those
795
-		// subfolders has subfolders of its own, it will be appended to $expand again to ensure
796
-		// it will be expanded later.
797
-		while (!empty($expand)) {
798
-			$item = array_shift($expand);
799
-			$columns = $properties;
800
-			$hierarchyTable = mapi_folder_gethierarchytable($item['folder'], MAPI_DEFERRED_ERRORS);
801
-
802
-			mapi_table_restrict($hierarchyTable, $restriction, TBL_BATCH);
803
-
804
-			mapi_table_setcolumns($hierarchyTable, $columns);
805
-			$columns = null;
806
-
807
-			// Load the hierarchy in small batches
808
-			$batchcount = 100;
809
-			do {
810
-				$rows = mapi_table_queryrows($hierarchyTable, $columns, 0, $batchcount);
811
-
812
-				foreach ($rows as $subfolder) {
813
-					$specialFolder = false;
814
-
815
-					// Check if this folder is special...
816
-					if (!empty($specialEntryids)) {
817
-						foreach ($specialEntryids as $key => $value) {
818
-							// No need to do compareEntryId(), the special folders have static
819
-							// entryids, and can be compared using ===.
820
-							if (bin2hex($subfolder[PR_ENTRYID]) === bin2hex($value)) {
821
-								$specialFolder = mapi_msgstore_openentry($store, $subfolder[PR_ENTRYID]);
822
-								$subfolder = mapi_getprops($specialFolder, $properties);
823
-
824
-								// We found the folder, no need to loop over it next time.
825
-								unset($specialEntryids[$key]);
826
-								break;
827
-							}
828
-						}
829
-					}
830
-
831
-					// If the subfolders has subfolders of its own, append the folder
832
-					// to the $expand array, so it can be expanded in the next loop.
833
-					if ($subfolder[PR_SUBFOLDERS]) {
834
-						if ($specialFolder) {
835
-							// Special folders can be redirected again to getSubFolders(),
836
-							$this->getSubFolders($specialFolder, $store, $properties, $storeData, $storeUserName);
837
-						}
838
-						else {
839
-							$folderObject = mapi_msgstore_openentry($store, $subfolder[PR_ENTRYID]);
840
-							array_push($expand, ['folder' => $folderObject, 'props' => $subfolder]);
841
-						}
842
-					}
843
-
844
-					$subfolder["user"] = $storeUserName;
845
-					// Add the folder to the return list.
846
-					array_push($storeData, $subfolder);
847
-				}
848
-
849
-				// When the server returned a different number of rows then was requested,
850
-				// we have reached the end of the table and we should exit the loop.
851
-			}
852
-			while (count($rows) === $batchcount);
853
-		}
854
-	}
855
-
856
-	/**
857
-	 * Function which is use get folder types from the container class.
858
-	 *
859
-	 * @param string $containerClass container class of folder
860
-	 *
861
-	 * @return int folder type
862
-	 */
863
-	public function getFolderTypeFromContainerClass($containerClass) {
864
-		switch ($containerClass) {
865
-			case "IPF.Note":
866
-				return SYNC_FOLDER_TYPE_USER_MAIL;
867
-
868
-			case "IPF.Appointment":
869
-				return SYNC_FOLDER_TYPE_USER_APPOINTMENT;
870
-
871
-			case "IPF.Contact":
872
-				return SYNC_FOLDER_TYPE_USER_CONTACT;
873
-
874
-			case "IPF.StickyNote":
875
-				return SYNC_FOLDER_TYPE_USER_NOTE;
876
-
877
-			case "IPF.Task":
878
-				return SYNC_FOLDER_TYPE_USER_TASK;
879
-
880
-			case "IPF.Journal":
881
-				return SYNC_FOLDER_TYPE_USER_JOURNAL;
882
-
883
-			default:
884
-				return SYNC_FOLDER_TYPE_UNKNOWN;
885
-		}
886
-	}
887
-
888
-	/**
889
-	 * Returns MAPIFolder object which contains the state information.
890
-	 * Creates this folder if it is not available yet.
891
-	 *
892
-	 * @param string $devid the device id
893
-	 *
894
-	 * @return MAPIFolder
895
-	 */
896
-	public function getStoreStateFolder() {
897
-		if (!$this->stateFolder) {
898
-			$store = $GLOBALS["mapisession"]->getDefaultMessageStore();
899
-			$rootFolder = mapi_msgstore_openentry($store);
900
-			$hierarchy = mapi_folder_gethierarchytable($rootFolder, CONVENIENT_DEPTH | MAPI_DEFERRED_ERRORS);
901
-			$restriction = $this->getStateFolderRestriction(PLUGIN_MDM_STORE_STATE_FOLDER);
902
-			mapi_table_restrict($hierarchy, $restriction);
903
-			if (mapi_table_getrowcount($hierarchy) == 1) {
904
-				$rows = mapi_table_queryrows($hierarchy, [PR_ENTRYID], 0, 1);
905
-				$this->stateFolder = mapi_msgstore_openentry($store, $rows[0][PR_ENTRYID]);
906
-			}
907
-		}
908
-
909
-		return $this->stateFolder;
910
-	}
911
-
912
-	/**
913
-	 * Returns the restriction for the state folder name.
914
-	 *
915
-	 * @param string $folderName the state folder name
916
-	 *
917
-	 * @return array
918
-	 */
919
-	public function getStateFolderRestriction($folderName) {
920
-		return [RES_AND, [
921
-			[RES_PROPERTY,
922
-				[RELOP => RELOP_EQ,
923
-					ULPROPTAG => PR_DISPLAY_NAME,
924
-					VALUE => $folderName,
925
-				],
926
-			],
927
-			[RES_PROPERTY,
928
-				[RELOP => RELOP_EQ,
929
-					ULPROPTAG => PR_ATTR_HIDDEN,
930
-					VALUE => true,
931
-				],
932
-			],
933
-		]];
934
-	}
935
-
936
-	/**
937
-	 * Returns the restriction for the associated message in the state folder.
938
-	 *
939
-	 * @param string $messageName the message name
940
-	 *
941
-	 * @return array
942
-	 */
943
-	public function getStateMessageRestriction($messageName) {
944
-		return [RES_AND, [
945
-			[RES_PROPERTY,
946
-				[RELOP => RELOP_EQ,
947
-					ULPROPTAG => PR_DISPLAY_NAME,
948
-					VALUE => $messageName,
949
-				],
950
-			],
951
-			[RES_PROPERTY,
952
-				[RELOP => RELOP_EQ,
953
-					ULPROPTAG => PR_MESSAGE_CLASS,
954
-					VALUE => 'IPM.Note.GrommunioState',
955
-				],
956
-			],
957
-		]];
958
-	}
959
-
960
-	/**
961
-	 * Returns the status of the remote wipe policy.
962
-	 *
963
-	 * @param mixed $deviceid
964
-	 *
965
-	 * @return int returns the current status of the device - SYNC_PROVISION_RWSTATUS_*
966
-	 */
967
-	public function getProvisioningWipeStatus($deviceid) {
968
-		// retrieve the WIPE STATUS from the Admin API
969
-		$api_response = file_get_contents(PLUGIN_MDM_ADMIN_API_WIPE_ENDPOINT . $GLOBALS["mapisession"]->getUserName() . "?devices=" . $deviceid);
970
-		if ($api_response) {
971
-			$data = json_decode($api_response, true);
972
-			if (isset($data['data'][$deviceid]["status"])) {
973
-				return $data['data'][$deviceid]["status"];
974
-			}
975
-		}
976
-
977
-		return SYNC_PROVISION_RWSTATUS_NA;
978
-	}
342
+        $syncFoldersProps['synchronizedfolders'] = $synchronizedFolders;
343
+
344
+        return $syncFoldersProps;
345
+    }
346
+
347
+    /**
348
+     * Function which is use to get general type like Mail,Calendar,Contacts,etc. from folder type.
349
+     *
350
+     * @param int    $type foldertype for a folder already known to the mobile
351
+     * @param string $name folder name
352
+     *
353
+     * @return string general folder type
354
+     */
355
+    public function getSyncFolderType($type) {
356
+        switch ($type) {
357
+            case SYNC_FOLDER_TYPE_APPOINTMENT:
358
+            case SYNC_FOLDER_TYPE_USER_APPOINTMENT:
359
+                    $folderType = "Calendars";
360
+                break;
361
+
362
+            case SYNC_FOLDER_TYPE_CONTACT:
363
+            case SYNC_FOLDER_TYPE_USER_CONTACT:
364
+                $folderType = "Contacts";
365
+                break;
366
+
367
+            case SYNC_FOLDER_TYPE_TASK:
368
+            case SYNC_FOLDER_TYPE_USER_TASK:
369
+                $folderType = "Tasks";
370
+                break;
371
+
372
+            case SYNC_FOLDER_TYPE_NOTE:
373
+            case SYNC_FOLDER_TYPE_USER_NOTE:
374
+                $folderType = "Notes";
375
+                break;
376
+
377
+            default:
378
+                $folderType = "Emails";
379
+                break;
380
+        }
381
+
382
+        return $folderType;
383
+    }
384
+
385
+    /**
386
+     * Function which is use to get list of additional folders which was shared with given device.
387
+     *
388
+     * @param string $devid device id
389
+     *
390
+     * @return array has list of properties related to shared folders
391
+     */
392
+    public function getAdditionalFolderList($devid) {
393
+        return [];
394
+        // TODO implement
395
+        $stores = $GLOBALS["mapisession"]->getAllMessageStores();
396
+        $client = $this->getSoapClient();
397
+        $items = $client->AdditionalFolderList($devid);
398
+        $data = [];
399
+        foreach ($items as $item) {
400
+            foreach ($stores as $store) {
401
+                try {
402
+                    $entryid = mapi_msgstore_entryidfromsourcekey($store, hex2bin($item->folderid));
403
+                }
404
+                catch (MAPIException $me) {
405
+                    continue;
406
+                }
407
+            }
408
+            if (isset($entryid)) {
409
+                $item->entryid = bin2hex($entryid);
410
+            }
411
+            array_push($data, ["props" => $item]);
412
+        }
413
+
414
+        return $data;
415
+    }
416
+
417
+    /**
418
+     * Function which is use to remove additional folder which was shared with given device.
419
+     *
420
+     * @param string $entryId  id of device
421
+     * @param string $folderid id of folder which will remove from device
422
+     */
423
+    public function additionalFolderRemove($entryId, $folderid) {
424
+        $client = $this->getSoapClient();
425
+        $client->AdditionalFolderRemove($entryId, $folderid);
426
+    }
427
+
428
+    /**
429
+     * Function which is use to add additional folder which will share with given device.
430
+     *
431
+     * @param string $entryId id of device
432
+     * @param array  $folder  folder which will share with device
433
+     */
434
+    public function additionalFolderAdd($entryId, $folder) {
435
+        $client = $this->getSoapClient();
436
+        $containerClass = isset($folder[PR_CONTAINER_CLASS]) ? $folder[PR_CONTAINER_CLASS] : "IPF.Note";
437
+        $folderId = bin2hex($folder[PR_SOURCE_KEY]);
438
+        $userName = $folder["user"];
439
+        $folderName = $userName === "SYSTEM" ? $folder[PR_DISPLAY_NAME] : $folder[PR_DISPLAY_NAME] . " - " . $userName;
440
+        $folderType = $this->getFolderTypeFromContainerClass($containerClass);
441
+        $client->AdditionalFolderAdd($entryId, $userName, $folderId, $folderName, $folderType, FLD_FLAGS_REPLYASUSER);
442
+    }
443
+
444
+    /**
445
+     * Function which use to save the device.
446
+     * It will use to add or remove folders in the device.
447
+     *
448
+     * @param array $data array of added and removed folders
449
+     */
450
+    public function saveDevice($data) {
451
+        $entryid = $data["entryid"];
452
+        if (isset($data['sharedfolders'])) {
453
+            if (isset($data['sharedfolders']['remove'])) {
454
+                $deletedFolders = $data['sharedfolders']['remove'];
455
+                foreach ($deletedFolders as $folder) {
456
+                    $this->additionalFolderRemove($entryid, $folder["folderid"]);
457
+                }
458
+            }
459
+            if (isset($data['sharedfolders']['add'])) {
460
+                $addFolders = $data['sharedfolders']['add'];
461
+                $hierarchyFolders = $this->getHierarchyList();
462
+                foreach ($addFolders as $folder) {
463
+                    foreach ($hierarchyFolders as $hierarchyFolder) {
464
+                        $folderEntryid = bin2hex($hierarchyFolder[PR_ENTRYID]);
465
+                        if ($folderEntryid === $folder["entryid"]) {
466
+                            $this->additionalFolderAdd($entryid, $hierarchyFolder);
467
+
468
+                            continue 2;
469
+                        }
470
+                    }
471
+                }
472
+            }
473
+        }
474
+    }
475
+
476
+    /**
477
+     * Gets the hierarchy list of all required stores.
478
+     * Function which is use to get the hierarchy list with PR_SOURCE_KEY.
479
+     *
480
+     * @return array the array of all hierarchy folders
481
+     */
482
+    public function getHierarchyList() {
483
+        $storeList = $GLOBALS["mapisession"]->getAllMessageStores();
484
+        $properties = $GLOBALS["properties"]->getFolderListProperties();
485
+        $otherUsers = $GLOBALS["mapisession"]->retrieveOtherUsersFromSettings();
486
+        $properties["source_key"] = PR_SOURCE_KEY;
487
+        $openWholeStore = true;
488
+        $storeData = [];
489
+
490
+        foreach ($storeList as $store) {
491
+            $msgstore_props = mapi_getprops($store, [PR_MDB_PROVIDER, PR_ENTRYID, PR_IPM_SUBTREE_ENTRYID, PR_USER_NAME]);
492
+            $storeType = $msgstore_props[PR_MDB_PROVIDER];
493
+
494
+            if ($storeType == ZARAFA_SERVICE_GUID) {
495
+                continue;
496
+            }
497
+            if ($storeType == ZARAFA_STORE_DELEGATE_GUID) {
498
+                $storeUserName = $GLOBALS["mapisession"]->getUserNameOfStore($msgstore_props[PR_ENTRYID]);
499
+            }
500
+            elseif ($storeType == ZARAFA_STORE_PUBLIC_GUID) {
501
+                $storeUserName = "SYSTEM";
502
+            }
503
+            else {
504
+                $storeUserName = $msgstore_props[PR_USER_NAME];
505
+            }
506
+
507
+            if (is_array($otherUsers)) {
508
+                if (isset($otherUsers[$storeUserName])) {
509
+                    $sharedFolders = $otherUsers[$storeUserName];
510
+                    if (!isset($otherUsers[$storeUserName]['all'])) {
511
+                        $openWholeStore = false;
512
+                        $a = $this->getSharedFolderList($store, $sharedFolders, $properties, $storeUserName);
513
+                        $storeData = array_merge($storeData, $a);
514
+                    }
515
+                }
516
+            }
517
+
518
+            if ($openWholeStore) {
519
+                if (isset($msgstore_props[PR_IPM_SUBTREE_ENTRYID])) {
520
+                    $subtreeFolderEntryID = $msgstore_props[PR_IPM_SUBTREE_ENTRYID];
521
+
522
+                    try {
523
+                        $subtreeFolder = mapi_msgstore_openentry($store, $subtreeFolderEntryID);
524
+                    }
525
+                    catch (MAPIException $e) {
526
+                        // We've handled the event
527
+                        $e->setHandled();
528
+                    }
529
+
530
+                    if ($storeType != ZARAFA_STORE_PUBLIC_GUID) {
531
+                        $this->getSubFolders($subtreeFolder, $store, $properties, $storeData, $storeUserName);
532
+                    }
533
+                    else {
534
+                        $this->getSubFoldersPublic($subtreeFolder, $store, $properties, $storeData, $storeUserName);
535
+                    }
536
+                }
537
+            }
538
+        }
539
+
540
+        return $storeData;
541
+    }
542
+
543
+    /**
544
+     * Helper function to get the shared folder list.
545
+     *
546
+     * @param object $store         message Store Object
547
+     * @param object $sharedFolders mapi Folder Object
548
+     * @param array  $properties    MAPI property mappings for folders
549
+     * @param string $storeUserName owner name of store
550
+     *
551
+     * @return array shared folders list
552
+     */
553
+    public function getSharedFolderList($store, $sharedFolders, $properties, $storeUserName) {
554
+        $msgstore_props = mapi_getprops($store, [PR_ENTRYID, PR_DISPLAY_NAME, PR_IPM_SUBTREE_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID, PR_MDB_PROVIDER, PR_IPM_PUBLIC_FOLDERS_ENTRYID, PR_IPM_FAVORITES_ENTRYID, PR_OBJECT_TYPE, PR_STORE_SUPPORT_MASK, PR_MAILBOX_OWNER_ENTRYID, PR_MAILBOX_OWNER_NAME, PR_USER_ENTRYID, PR_USER_NAME, PR_QUOTA_WARNING_THRESHOLD, PR_QUOTA_SEND_THRESHOLD, PR_QUOTA_RECEIVE_THRESHOLD, PR_MESSAGE_SIZE_EXTENDED, PR_MAPPING_SIGNATURE, PR_COMMON_VIEWS_ENTRYID, PR_FINDER_ENTRYID]);
555
+        $storeData = [];
556
+        $folders = [];
557
+
558
+        try {
559
+            $inbox = mapi_msgstore_getreceivefolder($store);
560
+            $inboxProps = mapi_getprops($inbox, [PR_ENTRYID]);
561
+        }
562
+        catch (MAPIException $e) {
563
+            // don't propagate this error to parent handlers, if store doesn't support it
564
+            if ($e->getCode() === MAPI_E_NO_SUPPORT) {
565
+                $e->setHandled();
566
+            }
567
+        }
568
+
569
+        $root = mapi_msgstore_openentry($store, null);
570
+        $rootProps = mapi_getprops($root, [PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_JOURNAL_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_TASK_ENTRYID, PR_ADDITIONAL_REN_ENTRYIDS]);
571
+
572
+        $additional_ren_entryids = [];
573
+        if (isset($rootProps[PR_ADDITIONAL_REN_ENTRYIDS])) {
574
+            $additional_ren_entryids = $rootProps[PR_ADDITIONAL_REN_ENTRYIDS];
575
+        }
576
+
577
+        $defaultfolders = [
578
+            "default_folder_inbox" => ["inbox" => PR_ENTRYID],
579
+            "default_folder_outbox" => ["store" => PR_IPM_OUTBOX_ENTRYID],
580
+            "default_folder_sent" => ["store" => PR_IPM_SENTMAIL_ENTRYID],
581
+            "default_folder_wastebasket" => ["store" => PR_IPM_WASTEBASKET_ENTRYID],
582
+            "default_folder_favorites" => ["store" => PR_IPM_FAVORITES_ENTRYID],
583
+            "default_folder_publicfolders" => ["store" => PR_IPM_PUBLIC_FOLDERS_ENTRYID],
584
+            "default_folder_calendar" => ["root" => PR_IPM_APPOINTMENT_ENTRYID],
585
+            "default_folder_contact" => ["root" => PR_IPM_CONTACT_ENTRYID],
586
+            "default_folder_drafts" => ["root" => PR_IPM_DRAFTS_ENTRYID],
587
+            "default_folder_journal" => ["root" => PR_IPM_JOURNAL_ENTRYID],
588
+            "default_folder_note" => ["root" => PR_IPM_NOTE_ENTRYID],
589
+            "default_folder_task" => ["root" => PR_IPM_TASK_ENTRYID],
590
+            "default_folder_junk" => ["additional" => 4],
591
+            "default_folder_syncissues" => ["additional" => 1],
592
+            "default_folder_conflicts" => ["additional" => 0],
593
+            "default_folder_localfailures" => ["additional" => 2],
594
+            "default_folder_serverfailures" => ["additional" => 3],
595
+        ];
596
+
597
+        foreach ($defaultfolders as $key => $prop) {
598
+            $tag = reset($prop);
599
+            $from = key($prop);
600
+
601
+            switch ($from) {
602
+                case "inbox":
603
+                    if (isset($inboxProps[$tag])) {
604
+                        $storeData["props"][$key] = bin2hex($inboxProps[$tag]);
605
+                    }
606
+                    break;
607
+
608
+                case "store":
609
+                    if (isset($msgstore_props[$tag])) {
610
+                        $storeData["props"][$key] = bin2hex($msgstore_props[$tag]);
611
+                    }
612
+                    break;
613
+
614
+                case "root":
615
+                    if (isset($rootProps[$tag])) {
616
+                        $storeData["props"][$key] = bin2hex($rootProps[$tag]);
617
+                    }
618
+                    break;
619
+
620
+                case "additional":
621
+                    if (isset($additional_ren_entryids[$tag])) {
622
+                        $storeData["props"][$key] = bin2hex($additional_ren_entryids[$tag]);
623
+                    }
624
+                    break;
625
+            }
626
+        }
627
+
628
+        $store_access = true;
629
+        $openSubFolders = false;
630
+        foreach ($sharedFolders as $type => $sharedFolder) {
631
+            $openSubFolders = ($sharedFolder["show_subfolders"] == true);
632
+            $folderEntryID = hex2bin($storeData["props"]["default_folder_" . $sharedFolder["folder_type"]]);
633
+
634
+            try {
635
+                // load folder props
636
+                $folder = mapi_msgstore_openentry($store, $folderEntryID);
637
+            }
638
+            catch (MAPIException $e) {
639
+                // Indicate that we don't have access to the store,
640
+                // so no more attempts to read properties or open entries.
641
+                $store_access = false;
642
+
643
+                // We've handled the event
644
+                $e->setHandled();
645
+            }
646
+        }
647
+
648
+        if ($store_access === true) {
649
+            $folderProps = mapi_getprops($folder, $properties);
650
+            $folderProps["user"] = $storeUserName;
651
+            array_push($folders, $folderProps);
652
+
653
+            // If folder has sub folders then add its.
654
+            if ($openSubFolders === true) {
655
+                if ($folderProps[PR_SUBFOLDERS] != false) {
656
+                    $subFoldersData = [];
657
+                    $this->getSubFolders($folder, $store, $properties, $subFoldersData, $storeUserName);
658
+                    $folders = array_merge($folders, $subFoldersData);
659
+                }
660
+            }
661
+        }
662
+
663
+        return $folders;
664
+    }
665
+
666
+    /**
667
+     * Helper function to get the sub folders of a given folder.
668
+     *
669
+     * @param object $folder        mapi Folder Object
670
+     * @param object $store         Message Store Object
671
+     * @param array  $properties    MAPI property mappings for folders
672
+     * @param array  $storeData     Reference to an array. The folder properties are added to this array.
673
+     * @param string $storeUserName owner name of store
674
+     */
675
+    public function getSubFolders($folder, $store, $properties, &$storeData, $storeUserName) {
676
+        /**
677
+         * remove hidden folders, folders with PR_ATTR_HIDDEN property set
678
+         * should not be shown to the client.
679
+         */
680
+        $restriction = [RES_OR, [
681
+            [RES_PROPERTY,
682
+                [
683
+                    RELOP => RELOP_EQ,
684
+                    ULPROPTAG => PR_ATTR_HIDDEN,
685
+                    VALUE => [PR_ATTR_HIDDEN => false],
686
+                ],
687
+            ],
688
+            [RES_NOT,
689
+                [
690
+                    [RES_EXIST,
691
+                        [
692
+                            ULPROPTAG => PR_ATTR_HIDDEN,
693
+                        ],
694
+                    ],
695
+                ],
696
+            ],
697
+        ]];
698
+
699
+        $expand = [
700
+            [
701
+                'folder' => $folder,
702
+                'props' => mapi_getprops($folder, [PR_ENTRYID, PR_SUBFOLDERS]),
703
+            ],
704
+        ];
705
+
706
+        // Start looping through the $expand array, during each loop we grab the first item in
707
+        // the array and obtain the hierarchy table for that particular folder. If one of those
708
+        // subfolders has subfolders of its own, it will be appended to $expand again to ensure
709
+        // it will be expanded later.
710
+        while (!empty($expand)) {
711
+            $item = array_shift($expand);
712
+            $columns = $properties;
713
+
714
+            $hierarchyTable = mapi_folder_gethierarchytable($item['folder'], MAPI_DEFERRED_ERRORS);
715
+            mapi_table_restrict($hierarchyTable, $restriction, TBL_BATCH);
716
+
717
+            mapi_table_setcolumns($hierarchyTable, $columns);
718
+            $columns = null;
719
+
720
+            // Load the hierarchy in small batches
721
+            $batchcount = 100;
722
+            do {
723
+                $rows = mapi_table_queryrows($hierarchyTable, $columns, 0, $batchcount);
724
+
725
+                foreach ($rows as $subfolder) {
726
+                    // If the subfolders has subfolders of its own, append the folder
727
+                    // to the $expand array, so it can be expanded in the next loop.
728
+                    if ($subfolder[PR_SUBFOLDERS]) {
729
+                        $folderObject = mapi_msgstore_openentry($store, $subfolder[PR_ENTRYID]);
730
+                        array_push($expand, ['folder' => $folderObject, 'props' => $subfolder]);
731
+                    }
732
+                    $subfolder["user"] = $storeUserName;
733
+                    // Add the folder to the return list.
734
+                    array_push($storeData, $subfolder);
735
+                }
736
+
737
+                // When the server returned a different number of rows then was requested,
738
+                // we have reached the end of the table and we should exit the loop.
739
+            }
740
+            while (count($rows) === $batchcount);
741
+        }
742
+    }
743
+
744
+    /**
745
+     * Helper function to get the subfolders of a Public Store.
746
+     *
747
+     * @param object $folder        mapi Folder Object
748
+     * @param object $store         Message Store Object
749
+     * @param array  $properties    MAPI property mappings for folders
750
+     * @param array  $storeData     Reference to an array. The folder properties are added to this array.
751
+     * @param string $storeUserName owner name of store
752
+     */
753
+    public function getSubFoldersPublic($folder, $store, $properties, &$storeData, $storeUserName) {
754
+        $expand = [
755
+            [
756
+                'folder' => $folder,
757
+                'props' => mapi_getprops($folder, [PR_ENTRYID, PR_SUBFOLDERS]),
758
+            ],
759
+        ];
760
+
761
+        /**
762
+         * remove hidden folders, folders with PR_ATTR_HIDDEN property set
763
+         * should not be shown to the client.
764
+         */
765
+        $restriction = [RES_OR, [
766
+            [RES_PROPERTY,
767
+                [
768
+                    RELOP => RELOP_EQ,
769
+                    ULPROPTAG => PR_ATTR_HIDDEN,
770
+                    VALUE => [PR_ATTR_HIDDEN => false],
771
+                ],
772
+            ],
773
+            [RES_NOT,
774
+                [
775
+                    [RES_EXIST,
776
+                        [
777
+                            ULPROPTAG => PR_ATTR_HIDDEN,
778
+                        ],
779
+                    ],
780
+                ],
781
+            ],
782
+        ]];
783
+
784
+        // CONVENIENT_DEPTH doesn't work on the IPM_SUBTREE, hence we will be recursively
785
+        // walking through the hierarchy. However, we have some special folders like the
786
+        // "Favorites" and "Public Folders" from where we can switch to using
787
+        // CONVENIENT_DEPTH. Obtain these special cases here.
788
+        $specialEntryids = mapi_getprops($store, [
789
+            PR_IPM_FAVORITES_ENTRYID,
790
+            PR_IPM_PUBLIC_FOLDERS_ENTRYID,
791
+        ]);
792
+
793
+        // Start looping through the $expand array, during each loop we grab the first item in
794
+        // the array and obtain the hierarchy table for that particular folder. If one of those
795
+        // subfolders has subfolders of its own, it will be appended to $expand again to ensure
796
+        // it will be expanded later.
797
+        while (!empty($expand)) {
798
+            $item = array_shift($expand);
799
+            $columns = $properties;
800
+            $hierarchyTable = mapi_folder_gethierarchytable($item['folder'], MAPI_DEFERRED_ERRORS);
801
+
802
+            mapi_table_restrict($hierarchyTable, $restriction, TBL_BATCH);
803
+
804
+            mapi_table_setcolumns($hierarchyTable, $columns);
805
+            $columns = null;
806
+
807
+            // Load the hierarchy in small batches
808
+            $batchcount = 100;
809
+            do {
810
+                $rows = mapi_table_queryrows($hierarchyTable, $columns, 0, $batchcount);
811
+
812
+                foreach ($rows as $subfolder) {
813
+                    $specialFolder = false;
814
+
815
+                    // Check if this folder is special...
816
+                    if (!empty($specialEntryids)) {
817
+                        foreach ($specialEntryids as $key => $value) {
818
+                            // No need to do compareEntryId(), the special folders have static
819
+                            // entryids, and can be compared using ===.
820
+                            if (bin2hex($subfolder[PR_ENTRYID]) === bin2hex($value)) {
821
+                                $specialFolder = mapi_msgstore_openentry($store, $subfolder[PR_ENTRYID]);
822
+                                $subfolder = mapi_getprops($specialFolder, $properties);
823
+
824
+                                // We found the folder, no need to loop over it next time.
825
+                                unset($specialEntryids[$key]);
826
+                                break;
827
+                            }
828
+                        }
829
+                    }
830
+
831
+                    // If the subfolders has subfolders of its own, append the folder
832
+                    // to the $expand array, so it can be expanded in the next loop.
833
+                    if ($subfolder[PR_SUBFOLDERS]) {
834
+                        if ($specialFolder) {
835
+                            // Special folders can be redirected again to getSubFolders(),
836
+                            $this->getSubFolders($specialFolder, $store, $properties, $storeData, $storeUserName);
837
+                        }
838
+                        else {
839
+                            $folderObject = mapi_msgstore_openentry($store, $subfolder[PR_ENTRYID]);
840
+                            array_push($expand, ['folder' => $folderObject, 'props' => $subfolder]);
841
+                        }
842
+                    }
843
+
844
+                    $subfolder["user"] = $storeUserName;
845
+                    // Add the folder to the return list.
846
+                    array_push($storeData, $subfolder);
847
+                }
848
+
849
+                // When the server returned a different number of rows then was requested,
850
+                // we have reached the end of the table and we should exit the loop.
851
+            }
852
+            while (count($rows) === $batchcount);
853
+        }
854
+    }
855
+
856
+    /**
857
+     * Function which is use get folder types from the container class.
858
+     *
859
+     * @param string $containerClass container class of folder
860
+     *
861
+     * @return int folder type
862
+     */
863
+    public function getFolderTypeFromContainerClass($containerClass) {
864
+        switch ($containerClass) {
865
+            case "IPF.Note":
866
+                return SYNC_FOLDER_TYPE_USER_MAIL;
867
+
868
+            case "IPF.Appointment":
869
+                return SYNC_FOLDER_TYPE_USER_APPOINTMENT;
870
+
871
+            case "IPF.Contact":
872
+                return SYNC_FOLDER_TYPE_USER_CONTACT;
873
+
874
+            case "IPF.StickyNote":
875
+                return SYNC_FOLDER_TYPE_USER_NOTE;
876
+
877
+            case "IPF.Task":
878
+                return SYNC_FOLDER_TYPE_USER_TASK;
879
+
880
+            case "IPF.Journal":
881
+                return SYNC_FOLDER_TYPE_USER_JOURNAL;
882
+
883
+            default:
884
+                return SYNC_FOLDER_TYPE_UNKNOWN;
885
+        }
886
+    }
887
+
888
+    /**
889
+     * Returns MAPIFolder object which contains the state information.
890
+     * Creates this folder if it is not available yet.
891
+     *
892
+     * @param string $devid the device id
893
+     *
894
+     * @return MAPIFolder
895
+     */
896
+    public function getStoreStateFolder() {
897
+        if (!$this->stateFolder) {
898
+            $store = $GLOBALS["mapisession"]->getDefaultMessageStore();
899
+            $rootFolder = mapi_msgstore_openentry($store);
900
+            $hierarchy = mapi_folder_gethierarchytable($rootFolder, CONVENIENT_DEPTH | MAPI_DEFERRED_ERRORS);
901
+            $restriction = $this->getStateFolderRestriction(PLUGIN_MDM_STORE_STATE_FOLDER);
902
+            mapi_table_restrict($hierarchy, $restriction);
903
+            if (mapi_table_getrowcount($hierarchy) == 1) {
904
+                $rows = mapi_table_queryrows($hierarchy, [PR_ENTRYID], 0, 1);
905
+                $this->stateFolder = mapi_msgstore_openentry($store, $rows[0][PR_ENTRYID]);
906
+            }
907
+        }
908
+
909
+        return $this->stateFolder;
910
+    }
911
+
912
+    /**
913
+     * Returns the restriction for the state folder name.
914
+     *
915
+     * @param string $folderName the state folder name
916
+     *
917
+     * @return array
918
+     */
919
+    public function getStateFolderRestriction($folderName) {
920
+        return [RES_AND, [
921
+            [RES_PROPERTY,
922
+                [RELOP => RELOP_EQ,
923
+                    ULPROPTAG => PR_DISPLAY_NAME,
924
+                    VALUE => $folderName,
925
+                ],
926
+            ],
927
+            [RES_PROPERTY,
928
+                [RELOP => RELOP_EQ,
929
+                    ULPROPTAG => PR_ATTR_HIDDEN,
930
+                    VALUE => true,
931
+                ],
932
+            ],
933
+        ]];
934
+    }
935
+
936
+    /**
937
+     * Returns the restriction for the associated message in the state folder.
938
+     *
939
+     * @param string $messageName the message name
940
+     *
941
+     * @return array
942
+     */
943
+    public function getStateMessageRestriction($messageName) {
944
+        return [RES_AND, [
945
+            [RES_PROPERTY,
946
+                [RELOP => RELOP_EQ,
947
+                    ULPROPTAG => PR_DISPLAY_NAME,
948
+                    VALUE => $messageName,
949
+                ],
950
+            ],
951
+            [RES_PROPERTY,
952
+                [RELOP => RELOP_EQ,
953
+                    ULPROPTAG => PR_MESSAGE_CLASS,
954
+                    VALUE => 'IPM.Note.GrommunioState',
955
+                ],
956
+            ],
957
+        ]];
958
+    }
959
+
960
+    /**
961
+     * Returns the status of the remote wipe policy.
962
+     *
963
+     * @param mixed $deviceid
964
+     *
965
+     * @return int returns the current status of the device - SYNC_PROVISION_RWSTATUS_*
966
+     */
967
+    public function getProvisioningWipeStatus($deviceid) {
968
+        // retrieve the WIPE STATUS from the Admin API
969
+        $api_response = file_get_contents(PLUGIN_MDM_ADMIN_API_WIPE_ENDPOINT . $GLOBALS["mapisession"]->getUserName() . "?devices=" . $deviceid);
970
+        if ($api_response) {
971
+            $data = json_decode($api_response, true);
972
+            if (isset($data['data'][$deviceid]["status"])) {
973
+                return $data['data'][$deviceid]["status"];
974
+            }
975
+        }
976
+
977
+        return SYNC_PROVISION_RWSTATUS_NA;
978
+    }
979 979
 }
Please login to merge, or discard this patch.
Braces   +13 added lines, -26 removed lines patch added patch discarded remove patch
@@ -53,8 +53,7 @@  discard block
 block discarded – undo
53 53
 						// fallback for "old-style" states
54 54
 						if (isset($unserializedState->data->devices)) {
55 55
 							$devices[$unserializedState->data->devices->{$username}->data->deviceid] = $unserializedState->data->devices->{$username}->data;
56
-						}
57
-						else {
56
+						} else {
58 57
 							$devices[$unserializedState->data->deviceid] = $unserializedState->data;
59 58
 						}
60 59
 					}
@@ -94,8 +93,7 @@  discard block
 block discarded – undo
94 93
 
95 94
 					return true;
96 95
 				}
97
-			}
98
-			catch (Exception $e) {
96
+			} catch (Exception $e) {
99 97
 				error_log(sprintf("mdm plugin resyncDevice Exception: %s", $e));
100 98
 
101 99
 				return false;
@@ -168,8 +166,7 @@  discard block
 block discarded – undo
168 166
 				$ret = file_get_contents(PLUGIN_MDM_ADMIN_API_WIPE_ENDPOINT . $GLOBALS["mapisession"]->getUserName() . "?devices=" . $deviceid, false, stream_context_create($opts));
169 167
 
170 168
 				return $ret;
171
-			}
172
-			catch (Exception $e) {
169
+			} catch (Exception $e) {
173 170
 				error_log(sprintf("mdm plugin removeDevice Exception: %s", $e));
174 171
 
175 172
 				return false;
@@ -268,8 +265,7 @@  discard block
 block discarded – undo
268 265
 						default:
269 266
 							$this->handleUnknownActionType($actionType);
270 267
 					}
271
-				}
272
-				catch (Exception $e) {
268
+				} catch (Exception $e) {
273 269
 					$title = _('Mobile device management plugin');
274 270
 					$display_message = sprintf(_('Unexpected error occurred. Please contact your system administrator. Error code: %s'), $e->getMessage());
275 271
 					$this->sendFeedback(true, ["type" => ERROR_GENERAL, "info" => ['title' => $title, 'display_message' => $display_message]]);
@@ -320,8 +316,7 @@  discard block
 block discarded – undo
320 316
 				$folderType = $this->getSyncFolderType($type);
321 317
 				if (isset($synchedFolderTypes[$folderType])) {
322 318
 					++$synchedFolderTypes[$folderType];
323
-				}
324
-				else {
319
+				} else {
325 320
 					$synchedFolderTypes[$folderType] = 1;
326 321
 				}
327 322
 			}
@@ -400,8 +395,7 @@  discard block
 block discarded – undo
400 395
 			foreach ($stores as $store) {
401 396
 				try {
402 397
 					$entryid = mapi_msgstore_entryidfromsourcekey($store, hex2bin($item->folderid));
403
-				}
404
-				catch (MAPIException $me) {
398
+				} catch (MAPIException $me) {
405 399
 					continue;
406 400
 				}
407 401
 			}
@@ -496,11 +490,9 @@  discard block
 block discarded – undo
496 490
 			}
497 491
 			if ($storeType == ZARAFA_STORE_DELEGATE_GUID) {
498 492
 				$storeUserName = $GLOBALS["mapisession"]->getUserNameOfStore($msgstore_props[PR_ENTRYID]);
499
-			}
500
-			elseif ($storeType == ZARAFA_STORE_PUBLIC_GUID) {
493
+			} elseif ($storeType == ZARAFA_STORE_PUBLIC_GUID) {
501 494
 				$storeUserName = "SYSTEM";
502
-			}
503
-			else {
495
+			} else {
504 496
 				$storeUserName = $msgstore_props[PR_USER_NAME];
505 497
 			}
506 498
 
@@ -521,16 +513,14 @@  discard block
 block discarded – undo
521 513
 
522 514
 					try {
523 515
 						$subtreeFolder = mapi_msgstore_openentry($store, $subtreeFolderEntryID);
524
-					}
525
-					catch (MAPIException $e) {
516
+					} catch (MAPIException $e) {
526 517
 						// We've handled the event
527 518
 						$e->setHandled();
528 519
 					}
529 520
 
530 521
 					if ($storeType != ZARAFA_STORE_PUBLIC_GUID) {
531 522
 						$this->getSubFolders($subtreeFolder, $store, $properties, $storeData, $storeUserName);
532
-					}
533
-					else {
523
+					} else {
534 524
 						$this->getSubFoldersPublic($subtreeFolder, $store, $properties, $storeData, $storeUserName);
535 525
 					}
536 526
 				}
@@ -558,8 +548,7 @@  discard block
 block discarded – undo
558 548
 		try {
559 549
 			$inbox = mapi_msgstore_getreceivefolder($store);
560 550
 			$inboxProps = mapi_getprops($inbox, [PR_ENTRYID]);
561
-		}
562
-		catch (MAPIException $e) {
551
+		} catch (MAPIException $e) {
563 552
 			// don't propagate this error to parent handlers, if store doesn't support it
564 553
 			if ($e->getCode() === MAPI_E_NO_SUPPORT) {
565 554
 				$e->setHandled();
@@ -634,8 +623,7 @@  discard block
 block discarded – undo
634 623
 			try {
635 624
 				// load folder props
636 625
 				$folder = mapi_msgstore_openentry($store, $folderEntryID);
637
-			}
638
-			catch (MAPIException $e) {
626
+			} catch (MAPIException $e) {
639 627
 				// Indicate that we don't have access to the store,
640 628
 				// so no more attempts to read properties or open entries.
641 629
 				$store_access = false;
@@ -834,8 +822,7 @@  discard block
 block discarded – undo
834 822
 						if ($specialFolder) {
835 823
 							// Special folders can be redirected again to getSubFolders(),
836 824
 							$this->getSubFolders($specialFolder, $store, $properties, $storeData, $storeUserName);
837
-						}
838
-						else {
825
+						} else {
839 826
 							$folderObject = mapi_msgstore_openentry($store, $subfolder[PR_ENTRYID]);
840 827
 							array_push($expand, ['folder' => $folderObject, 'props' => $subfolder]);
841 828
 						}
Please login to merge, or discard this patch.
plugins/mdm/php/plugin.mdm.php 1 patch
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -3,44 +3,44 @@
 block discarded – undo
3 3
  * Handles plugin registration.
4 4
  */
5 5
 class PluginMDM extends Plugin {
6
-	/**
7
-	 * Called to initialize the plugin and register for hooks.
8
-	 */
9
-	public function init() {
10
-		$this->registerHook('server.core.settings.init.before');
11
-	}
6
+    /**
7
+     * Called to initialize the plugin and register for hooks.
8
+     */
9
+    public function init() {
10
+        $this->registerHook('server.core.settings.init.before');
11
+    }
12 12
 
13
-	/**
14
-	 * Function is executed when a hook is triggered by the PluginManager.
15
-	 *
16
-	 * @param string $eventID Identifier of the hook
17
-	 * @param array  $data    Reference to the data of the triggered hook
18
-	 */
19
-	public function execute($eventID, &$data) {
20
-		switch ($eventID) {
21
-			case 'server.core.settings.init.before':
22
-				$this->onBeforeSettingsInit($data);
23
-				break;
24
-		}
25
-	}
13
+    /**
14
+     * Function is executed when a hook is triggered by the PluginManager.
15
+     *
16
+     * @param string $eventID Identifier of the hook
17
+     * @param array  $data    Reference to the data of the triggered hook
18
+     */
19
+    public function execute($eventID, &$data) {
20
+        switch ($eventID) {
21
+            case 'server.core.settings.init.before':
22
+                $this->onBeforeSettingsInit($data);
23
+                break;
24
+        }
25
+    }
26 26
 
27
-	/**
28
-	 * Called when the core Settings class is initialized and ready to accept sysadmin default
29
-	 * settings. Registers the sysadmin defaults for the StatsLogging plugin.
30
-	 *
31
-	 * @param array $data Reference to the data of the triggered hook
32
-	 */
33
-	public function onBeforeSettingsInit(&$data) {
34
-		$data['settingsObj']->addSysAdminDefaults([
35
-			'zarafa' => [
36
-				'v1' => [
37
-					'plugins' => [
38
-						'mdm' => [
39
-							'enable' => PLUGIN_MDM_USER_DEFAULT_ENABLE_MDM,
40
-						],
41
-					],
42
-				],
43
-			],
44
-		]);
45
-	}
27
+    /**
28
+     * Called when the core Settings class is initialized and ready to accept sysadmin default
29
+     * settings. Registers the sysadmin defaults for the StatsLogging plugin.
30
+     *
31
+     * @param array $data Reference to the data of the triggered hook
32
+     */
33
+    public function onBeforeSettingsInit(&$data) {
34
+        $data['settingsObj']->addSysAdminDefaults([
35
+            'zarafa' => [
36
+                'v1' => [
37
+                    'plugins' => [
38
+                        'mdm' => [
39
+                            'enable' => PLUGIN_MDM_USER_DEFAULT_ENABLE_MDM,
40
+                        ],
41
+                    ],
42
+                ],
43
+            ],
44
+        ]);
45
+    }
46 46
 }
Please login to merge, or discard this patch.
plugins/chat/php/plugin.chat.php 1 patch
Indentation   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -4,48 +4,48 @@
 block discarded – undo
4 4
  * Plugin that adds Chat to grommunio Web.
5 5
  */
6 6
 class PluginChat extends Plugin {
7
-	/**
8
-	 * Function initializes the Plugin and registers all hooks.
9
-	 */
10
-	public function init() {
11
-		$this->registerHook('server.core.settings.init.before');
12
-	}
7
+    /**
8
+     * Function initializes the Plugin and registers all hooks.
9
+     */
10
+    public function init() {
11
+        $this->registerHook('server.core.settings.init.before');
12
+    }
13 13
 
14
-	/**
15
-	 * Function is executed when a hook is triggered by the PluginManager.
16
-	 *
17
-	 * @param string $eventID the id of the triggered hook
18
-	 * @param mixed  $data    object(s) related to the hook
19
-	 */
20
-	public function execute($eventID, &$data) {
21
-		switch ($eventID) {
22
-			case 'server.core.settings.init.before':
23
-				$this->injectPluginSettings($data);
24
-				break;
25
-		}
26
-	}
14
+    /**
15
+     * Function is executed when a hook is triggered by the PluginManager.
16
+     *
17
+     * @param string $eventID the id of the triggered hook
18
+     * @param mixed  $data    object(s) related to the hook
19
+     */
20
+    public function execute($eventID, &$data) {
21
+        switch ($eventID) {
22
+            case 'server.core.settings.init.before':
23
+                $this->injectPluginSettings($data);
24
+                break;
25
+        }
26
+    }
27 27
 
28
-	/**
29
-	 * Called when the core Settings class is initialized and ready to accept sysadmin default
30
-	 * settings. Registers the sysadmin defaults for the desktopnotifications plugin.
31
-	 *
32
-	 * @param array $data Reference to the data of the triggered hook
33
-	 */
34
-	public function injectPluginSettings(&$data) {
35
-		$pluginData = [
36
-			'enable' => PLUGIN_CHAT_USER_DEFAULT_ENABLE,
37
-			'url' => PLUGIN_CHAT_URL,
38
-			'autostart' => PLUGIN_CHAT_AUTOSTART,
39
-		];
28
+    /**
29
+     * Called when the core Settings class is initialized and ready to accept sysadmin default
30
+     * settings. Registers the sysadmin defaults for the desktopnotifications plugin.
31
+     *
32
+     * @param array $data Reference to the data of the triggered hook
33
+     */
34
+    public function injectPluginSettings(&$data) {
35
+        $pluginData = [
36
+            'enable' => PLUGIN_CHAT_USER_DEFAULT_ENABLE,
37
+            'url' => PLUGIN_CHAT_URL,
38
+            'autostart' => PLUGIN_CHAT_AUTOSTART,
39
+        ];
40 40
 
41
-		$data['settingsObj']->addSysAdminDefaults([
42
-			'zarafa' => [
43
-				'v1' => [
44
-					'plugins' => [
45
-						'chat' => $pluginData,
46
-					],
47
-				],
48
-			],
49
-		]);
50
-	}
41
+        $data['settingsObj']->addSysAdminDefaults([
42
+            'zarafa' => [
43
+                'v1' => [
44
+                    'plugins' => [
45
+                        'chat' => $pluginData,
46
+                    ],
47
+                ],
48
+            ],
49
+        ]);
50
+    }
51 51
 }
Please login to merge, or discard this patch.
plugins/filesbackendOwncloud/php/class.backend.php 3 patches
Indentation   +577 added lines, -577 removed lines patch added patch discarded remove patch
@@ -29,586 +29,586 @@
 block discarded – undo
29 29
  * @extends AbstractBackend
30 30
  */
31 31
 class Backend extends \Files\Backend\Webdav\Backend implements iFeatureSharing {
32
-	/**
33
-	 * @var ocsclient the OCS Api client
34
-	 */
35
-	public $ocs_client;
36
-
37
-	/**
38
-	 * @constructor
39
-	 */
40
-	public function __construct() {
41
-		// initialization
42
-		$this->debug = PLUGIN_FILESBROWSER_LOGLEVEL === "DEBUG" ? true : false;
43
-
44
-		$this->init_form();
45
-
46
-		// set backend description
47
-		$this->backendDescription = _("With this backend, you can connect to any ownCloud server.");
48
-
49
-		// set backend display name
50
-		$this->backendDisplayName = "ownCloud";
51
-
52
-		// set backend version
53
-		// TODO: this should be changed on every release
54
-		$this->backendVersion = "3.0";
55
-
56
-		// Backend name used in translations
57
-		$this->backendTransName = _('Files ownCloud Backend: ');
58
-	}
59
-
60
-	/**
61
-	 * Initialise form fields.
62
-	 */
63
-	private function init_form() {
64
-		$this->formConfig = [
65
-			"labelAlign" => "left",
66
-			"columnCount" => 1,
67
-			"labelWidth" => 80,
68
-			"defaults" => [
69
-				"width" => 292,
70
-			],
71
-		];
72
-
73
-		$this->formFields = [
74
-			[
75
-				"name" => "server_address",
76
-				"fieldLabel" => _('Server address'),
77
-				"editor" => [
78
-					"allowBlank" => false,
79
-				],
80
-			],
81
-			[
82
-				"name" => "server_port",
83
-				"fieldLabel" => _('Server port'),
84
-				"editor" => [
85
-					"ref" => "../../portField",
86
-					"allowBlank" => false,
87
-				],
88
-			],
89
-			[
90
-				"name" => "server_ssl",
91
-				"fieldLabel" => _('Use SSL'),
92
-				"editor" => [
93
-					"xtype" => "checkbox",
94
-					"listeners" => [
95
-						"check" => "Zarafa.plugins.files.data.Actions.onCheckSSL", // this javascript function will be called!
96
-					],
97
-				],
98
-			],
99
-			[
100
-				"name" => "server_path",
101
-				"fieldLabel" => _('Webdav base path'),
102
-				"editor" => [
103
-					"allowBlank" => false,
104
-				],
105
-			],
106
-			[
107
-				"name" => "user",
108
-				"fieldLabel" => _('Username'),
109
-				"editor" => [
110
-					"ref" => "../../usernameField",
111
-				],
112
-			],
113
-			[
114
-				"name" => "password",
115
-				"fieldLabel" => _('Password'),
116
-				"editor" => [
117
-					"ref" => "../../passwordField",
118
-					"inputType" => "password",
119
-				],
120
-			],
121
-			[
122
-				"name" => "use_grommunio_credentials",
123
-				"fieldLabel" => _('Use grommunio credentials'),
124
-				"editor" => [
125
-					"xtype" => "checkbox",
126
-					"listeners" => [
127
-						"check" => "Zarafa.plugins.files.data.Actions.onCheckCredentials", // this javascript function will be called!
128
-					],
129
-				],
130
-			],
131
-		];
132
-
133
-		$this->metaConfig = [
134
-			"success" => true,
135
-			"metaData" => [
136
-				"fields" => $this->formFields,
137
-				"formConfig" => $this->formConfig,
138
-			],
139
-			"data" => [ // here we can specify the default values.
140
-				"server_address" => $_SERVER['HTTP_HOST'],
141
-				"server_ssl" => true,
142
-				"server_port" => "443",
143
-				"server_path" => "/files/remote.php/webdav",
144
-				"use_grommunio_credentials" => true,
145
-			],
146
-		];
147
-	}
148
-
149
-	/**
150
-	 * Opens the connection to the webdav server.
151
-	 *
152
-	 * @throws BackendException if connection is not successful
153
-	 *
154
-	 * @return bool true if action succeeded
155
-	 */
156
-	public function open() {
157
-		// check if curl is available
158
-		$serverHasCurl = function_exists('curl_version');
159
-		if (!$serverHasCurl) {
160
-			$e = new BackendException($this->parseErrorCodeToMessage(self::WD_ERR_NO_CURL), 500);
161
-			$e->setTitle($this->backendTransName . _('php-curl is not available'));
162
-
163
-			throw $e;
164
-		}
165
-
166
-		$davsettings = [
167
-			'baseUri' => $this->webdavUrl(),
168
-			'userName' => $this->user,
169
-			'password' => $this->pass,
170
-			'authType' => \Sabre\DAV\Client::AUTH_BASIC,
171
-		];
172
-
173
-		try {
174
-			$this->sabre_client = new FilesWebDavClient($davsettings);
175
-			$this->sabre_client->addCurlSetting(CURLOPT_SSL_VERIFYPEER, !$this->allowselfsigned);
176
-
177
-			$this->ocs_client = new ocsclient($this->getOwncloudBaseURL(), $this->user, $this->pass, $this->allowselfsigned);
178
-
179
-			return true;
180
-		}
181
-		catch (\Exception $e) {
182
-			$this->log('Failed to open: ' . $e->getMessage());
183
-			if (intval($e->getHTTPCode()) == 401) {
184
-				$e = new BackendException($this->parseErrorCodeToMessage(self::WD_ERR_UNAUTHORIZED), $e->getHTTPCode());
185
-				$e->setTitle($this->backendTransName . _('Access denied'));
186
-
187
-				throw $e;
188
-			}
189
-			$e = new BackendException($this->parseErrorCodeToMessage(self::WD_ERR_UNREACHABLE), $e->getHTTPCode());
190
-			$e->setTitle($this->backendTransName . _('Connection failed'));
191
-
192
-			throw $e;
193
-		}
194
-	}
195
-
196
-	/**
197
-	 * /**
198
-	 * Copy a collection on webdav server
199
-	 * Duplicates a collection on the webdav server (serverside).
200
-	 * All work is done on the webdav server. If you set param overwrite as true,
201
-	 * the target will be overwritten.
202
-	 *
203
-	 * @param string $src_path  Source path
204
-	 * @param string $dst_path  Destination path
205
-	 * @param bool   $overwrite Overwrite if collection exists in $dst_path
206
-	 * @param bool   $coll      set this to true if you want to copy a folder
207
-	 *
208
-	 * @throws BackendException if request is not successful
209
-	 *
210
-	 * @return bool true if action succeeded
211
-	 */
212
-	private function copy($src_path, $dst_path, $overwrite, $coll) {
213
-		$time_start = microtime(true);
214
-		$src_path = $this->removeSlash($src_path);
215
-		$dst_path = $this->webdavUrl() . $this->removeSlash($dst_path);
216
-		$this->log("[COPY] start for dir: {$src_path} -> {$dst_path}");
217
-		if ($overwrite) {
218
-			$overwrite = 'T';
219
-		}
220
-		else {
221
-			$overwrite = 'F';
222
-		}
223
-
224
-		$settings = ["Destination" => $dst_path, 'Overwrite' => $overwrite];
225
-		if ($coll) {
226
-			$settings = ["Destination" => $dst_path, 'Depth' => 'Infinity'];
227
-		}
228
-
229
-		try {
230
-			$response = $this->sabre_client->request("COPY", $src_path, null, $settings);
231
-			$time_end = microtime(true);
232
-			$time = $time_end - $time_start;
233
-			$this->log("[COPY] done in {$time} seconds: " . $response['statusCode']);
234
-
235
-			return true;
236
-		}
237
-		catch (ClientException $e) {
238
-			$e = new BackendException($this->parseErrorCodeToMessage($e->getCode()), $e->getCode());
239
-			$e->setTitle($this->backendTransName . _('Sabre error'));
240
-
241
-			throw $e;
242
-		}
243
-		catch (Exception $e) {
244
-			$this->log('[COPY] fatal: ' . $e->getMessage());
245
-			$e = new BackendException($this->parseErrorCodeToMessage($e->getHTTPCode()), $e->getHTTPCode());
246
-			$e->setTitle($this->backendTransName . _('Copying failed'));
247
-
248
-			throw $e;
249
-		}
250
-	}
251
-
252
-	/**
253
-	 * This function will return a user friendly error string.
254
-	 *
255
-	 * @param number $error_code A error code
256
-	 *
257
-	 * @return string userfriendly error message
258
-	 */
259
-	private function parseErrorCodeToMessage($error_code) {
260
-		$error = intval($error_code);
261
-
262
-		$msg = _('Unknown error');
263
-		$contactAdmin = _('Please contact your system administrator.');
264
-
265
-		switch ($error) {
266
-			case CURLE_BAD_PASSWORD_ENTERED:
267
-			case self::WD_ERR_UNAUTHORIZED:
268
-				$msg = _('Unauthorized. Wrong username or password.');
269
-				break;
270
-
271
-			case CURLE_SSL_CONNECT_ERROR:
272
-			case CURLE_COULDNT_RESOLVE_HOST:
273
-			case CURLE_COULDNT_CONNECT:
274
-			case CURLE_OPERATION_TIMEOUTED:
275
-			case self::WD_ERR_UNREACHABLE:
276
-				$msg = _('File server is not reachable. Please verify the file server URL.');
277
-				break;
278
-
279
-			case self::WD_ERR_FORBIDDEN:
280
-				$msg = _('You don\'t have enough permissions to view this file or folder.');
281
-				break;
282
-
283
-			case self::WD_ERR_NOTFOUND:
284
-				$msg = _('The file or folder is not available anymore.');
285
-				break;
286
-
287
-			case self::WD_ERR_TIMEOUT:
288
-				$msg = _('Connection to the file server timed out. Please check again later.');
289
-				break;
290
-
291
-			case self::WD_ERR_LOCKED:
292
-				$msg = _('This file is locked by another user. Please try again later.');
293
-				break;
294
-
295
-			case self::WD_ERR_FAILED_DEPENDENCY:
296
-				$msg = _('The request failed.') . ' ' . $contactAdmin;
297
-				break;
298
-
299
-			case self::WD_ERR_INTERNAL:
300
-				// This is a general error, might be thrown due to a wrong IP, but we don't know.
301
-				$msg = _('The file server encountered an internal problem.') . ' ' . $contactAdmin;
302
-				break;
303
-
304
-			case self::WD_ERR_TMP:
305
-				$msg = _('We could not write to temporary directory.') . ' ' . $contactAdmin;
306
-				break;
307
-
308
-			case self::WD_ERR_FEATURES:
309
-				$msg = _('We could not retrieve list of server features.') . ' ' . $contactAdmin;
310
-				break;
311
-
312
-			case self::WD_ERR_NO_CURL:
313
-				$msg = _('PHP-Curl is not available.') . ' ' . $contactAdmin;
314
-				break;
315
-		}
316
-
317
-		return $msg;
318
-	}
319
-
320
-	/**
321
-	 * a simple php error_log wrapper.
322
-	 *
323
-	 * @param string $err_string error message
324
-	 */
325
-	private function log($err_string) {
326
-		if ($this->debug) {
327
-			error_log("[BACKEND_OWNCLOUD]: " . $err_string);
328
-		}
329
-	}
330
-
331
-	/**
332
-	 * Get the base URL of Owncloud.
333
-	 * For example: http://demo.owncloud.com/owncloud.
334
-	 *
335
-	 * @return string
336
-	 */
337
-	private function getOwncloudBaseURL() {
338
-		$webdavurl = $this->webdavUrl();
339
-
340
-		return substr($webdavurl, 0, strlen($webdavurl) - strlen("/remote.php/webdav/"));
341
-	}
342
-
343
-	/**
344
-	 * ============================ FEATURE FUNCTIONS ========================.
345
-	 */
346
-
347
-	/**
348
-	 * Return the version string of the server backend.
349
-	 *
350
-	 * @return string
351
-	 */
352
-	public function getServerVersion() {
353
-		// check if curl is available
354
-		$serverHasCurl = function_exists('curl_version');
355
-		if (!$serverHasCurl) {
356
-			throw new BackendException($this->parseErrorCodeToMessage(self::WD_ERR_NO_CURL), 500);
357
-		}
358
-
359
-		$url = $this->getOwncloudBaseURL() . "/status.php";
360
-
361
-		// try to get the contents of the owncloud status page
362
-		$ch = curl_init();
363
-		curl_setopt($ch, CURLOPT_AUTOREFERER, true);
364
-		curl_setopt($ch, CURLOPT_TIMEOUT, 3); // timeout of 3 seconds
365
-		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
366
-		curl_setopt($ch, CURLOPT_URL, $url);
367
-		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
368
-		if ($this->allowselfsigned) {
369
-			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
370
-			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
371
-		}
372
-		$versiondata = curl_exec($ch);
373
-		$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
374
-		curl_close($ch);
375
-
376
-		if ($httpcode && $httpcode == "200" && $versiondata) {
377
-			$versions = json_decode($versiondata);
378
-			$version = $versions->versionstring;
379
-		}
380
-		else {
381
-			$version = "Undetected (no ownCloud?)";
382
-		}
383
-
384
-		return $version;
385
-	}
386
-
387
-	/**
388
-	 * Get all shares in the specified folder.
389
-	 *
390
-	 * The response array will look like:
391
-	 *
392
-	 * array(
393
-	 *  path1 => array(
394
-	 *      id1 => details1,
395
-	 *      id2 => details2
396
-	 *  ),
397
-	 *  path2 => array(
398
-	 *      id1 => ....
399
-	 *  )
400
-	 * )
401
-	 *
402
-	 * @param $path
403
-	 *
404
-	 * @return array
405
-	 */
406
-	public function getShares($path) {
407
-		$result = [];
408
-
409
-		$this->log('[GETSHARES]: loading shares for folder: ' . $path);
410
-
411
-		try {
412
-			$this->ocs_client->loadShares($path);
413
-		}
414
-		catch (ConnectionException $e) {
415
-			$this->log('[GETSHARES]: connection exception while loading shares: ' . $e->getMessage() . " " . $e->getCode());
416
-		}
417
-		$shares = $this->ocs_client->getAllShares();
418
-
419
-		$this->log('[GETSHARES]: found ' . count($shares) . ' shares for folder: ' . $path);
420
-
421
-		$result[$path] = [];
422
-		if ($shares !== false) {
423
-			foreach ($shares as $id => $options) {
424
-				$result[$path][$id] = [
425
-					"shared" => true,
426
-					"id" => $options->getId(),
427
-					"path" => $options->getPath(),
428
-					"shareType" => $options->getShareType(),
429
-					"permissions" => $options->getPermissions(),
430
-					"expiration" => $options->getExpiration(),
431
-					"token" => $options->getToken(),
432
-					"url" => $options->getUrl(),
433
-					"shareWith" => $options->getShareWith(),
434
-					"shareWithDisplayname" => $options->getShareWithDisplayname(),
435
-				];
436
-			}
437
-		}
438
-
439
-		return $result;
440
-	}
441
-
442
-	/**
443
-	 * Get details about the shared files/folders.
444
-	 *
445
-	 * The response array will look like:
446
-	 *
447
-	 * array(
448
-	 *  path1 => array(
449
-	 *      id1 => details1,
450
-	 *      id2 => details2
451
-	 *  ),
452
-	 *  path2 => array(
453
-	 *      id1 => ....
454
-	 *  )
455
-	 * )
456
-	 *
457
-	 * @param $patharray Simple array with path's to files or folders
458
-	 *
459
-	 * @return array
460
-	 */
461
-	public function sharingDetails($patharray) {
462
-		$result = [];
463
-
464
-		// performance optimization
465
-		// fetch all shares - so we only need one request
466
-		if (count($patharray) > 1) {
467
-			try {
468
-				$this->ocs_client->loadShares();
469
-			}
470
-			catch (ConnectionException $e) {
471
-				$this->log('[SHARINGDETAILS]: connection exception while loading shares: ' . $e->getMessage() . " " . $e->getCode());
472
-			}
473
-
474
-			/** @var ocsshare[] $shares */
475
-			$shares = $this->ocs_client->getAllShares();
476
-			foreach ($patharray as $path) {
477
-				$result[$path] = [];
478
-				foreach ($shares as $id => $details) {
479
-					if ($details->getPath() == $path) {
480
-						$result[$path][$id] = [
481
-							"shared" => true,
482
-							"id" => $details->getId(),
483
-							"shareType" => $details->getShareType(),
484
-							"permissions" => $details->getPermissions(),
485
-							"expiration" => $details->getExpiration(),
486
-							"token" => $details->getToken(),
487
-							"url" => $details->getUrl(),
488
-							"shareWith" => $details->getShareWith(),
489
-							"shareWithDisplayname" => $details->getShareWithDisplayname(),
490
-						];
491
-					}
492
-				}
493
-			}
494
-		}
495
-		else {
496
-			if (count($patharray) == 1) {
497
-				try {
498
-					$shares = $this->ocs_client->loadShareByPath($patharray[0]);
499
-				}
500
-				catch (FileNotFoundException $e) {
501
-					$shares = false;
502
-				}
503
-
504
-				$result[$patharray[0]] = [];
505
-
506
-				if ($shares !== false) {
507
-					foreach ($shares as $id => $share) {
508
-						$result[$patharray[0]][$id] = [
509
-							"shared" => true,
510
-							"id" => $share->getId(),
511
-							"shareType" => $share->getShareType(),
512
-							"permissions" => $share->getPermissions(),
513
-							"expiration" => $share->getExpiration(),
514
-							"token" => $share->getToken(),
515
-							"url" => $share->getUrl(),
516
-							"shareWith" => $share->getShareWith(),
517
-							"shareWithDisplayname" => $share->getShareWithDisplayName(),
518
-						];
519
-					}
520
-				}
521
-			}
522
-			else {
523
-				return false; // $patharray was empty...
524
-			}
525
-		}
526
-
527
-		return $result;
528
-	}
529
-
530
-	/**
531
-	 * Share one or multiple files.
532
-	 * As the sharing dialog might differ for different backends, it is implemented as
533
-	 * MetaForm - meaning that the argumentnames/count might differ.
534
-	 * That's the cause why this function uses an array as parameter.
535
-	 *
536
-	 * $shareparams should look somehow like this:
537
-	 *
538
-	 * array(
539
-	 *      "path1" => options1,
540
-	 *      "path2" => options2
541
-	 *
542
-	 *      or
543
-	 *
544
-	 *      "id1" => options1 (ONLY if $update = true)
545
-	 * )
546
-	 *
547
-	 * @param $shareparams
548
-	 * @param bool $update
549
-	 *
550
-	 * @return bool
551
-	 */
552
-	public function share($shareparams, $update = false) {
553
-		$result = [];
554
-		if (count($shareparams) > 0) {
555
-			/** @var string $path */
556
-			foreach ($shareparams as $path => $options) {
557
-				$path = rtrim($path, "/");
558
-				$this->log('path: ' . $path);
559
-				if (!$update) {
560
-					$share = $this->ocs_client->createShare($path, $options);
561
-					$result[$path] = [
562
-						"shared" => true,
563
-						"id" => $share->getId(),
564
-						"token" => $share->getToken(),
565
-						"url" => $share->getUrl(),
566
-					];
567
-				}
568
-				else {
569
-					foreach ($options as $key => $value) {
570
-						$this->ocs_client->updateShare($path, $key, $value);
571
-					}
572
-					$result[$path] = [
573
-						"shared" => true,
574
-						"id" => $path,
575
-					];
576
-				}
577
-			}
578
-		}
579
-		else {
580
-			$this->log('No share params given');
581
-
582
-			return false; // no shareparams...
583
-		}
584
-
585
-		return $result;
586
-	}
587
-
588
-	/**
589
-	 * Disable sharing for the given files/folders.
590
-	 *
591
-	 * @param $idarray
592
-	 *
593
-	 * @throws \OCSAPI\Exception\ConnectionException
594
-	 *
595
-	 * @return bool
596
-	 */
597
-	public function unshare($idarray) {
598
-		foreach ($idarray as $id) {
599
-			$this->ocs_client->deleteShare($id);
600
-		}
601
-
602
-		return true;
603
-	}
604
-
605
-	/*
32
+    /**
33
+     * @var ocsclient the OCS Api client
34
+     */
35
+    public $ocs_client;
36
+
37
+    /**
38
+     * @constructor
39
+     */
40
+    public function __construct() {
41
+        // initialization
42
+        $this->debug = PLUGIN_FILESBROWSER_LOGLEVEL === "DEBUG" ? true : false;
43
+
44
+        $this->init_form();
45
+
46
+        // set backend description
47
+        $this->backendDescription = _("With this backend, you can connect to any ownCloud server.");
48
+
49
+        // set backend display name
50
+        $this->backendDisplayName = "ownCloud";
51
+
52
+        // set backend version
53
+        // TODO: this should be changed on every release
54
+        $this->backendVersion = "3.0";
55
+
56
+        // Backend name used in translations
57
+        $this->backendTransName = _('Files ownCloud Backend: ');
58
+    }
59
+
60
+    /**
61
+     * Initialise form fields.
62
+     */
63
+    private function init_form() {
64
+        $this->formConfig = [
65
+            "labelAlign" => "left",
66
+            "columnCount" => 1,
67
+            "labelWidth" => 80,
68
+            "defaults" => [
69
+                "width" => 292,
70
+            ],
71
+        ];
72
+
73
+        $this->formFields = [
74
+            [
75
+                "name" => "server_address",
76
+                "fieldLabel" => _('Server address'),
77
+                "editor" => [
78
+                    "allowBlank" => false,
79
+                ],
80
+            ],
81
+            [
82
+                "name" => "server_port",
83
+                "fieldLabel" => _('Server port'),
84
+                "editor" => [
85
+                    "ref" => "../../portField",
86
+                    "allowBlank" => false,
87
+                ],
88
+            ],
89
+            [
90
+                "name" => "server_ssl",
91
+                "fieldLabel" => _('Use SSL'),
92
+                "editor" => [
93
+                    "xtype" => "checkbox",
94
+                    "listeners" => [
95
+                        "check" => "Zarafa.plugins.files.data.Actions.onCheckSSL", // this javascript function will be called!
96
+                    ],
97
+                ],
98
+            ],
99
+            [
100
+                "name" => "server_path",
101
+                "fieldLabel" => _('Webdav base path'),
102
+                "editor" => [
103
+                    "allowBlank" => false,
104
+                ],
105
+            ],
106
+            [
107
+                "name" => "user",
108
+                "fieldLabel" => _('Username'),
109
+                "editor" => [
110
+                    "ref" => "../../usernameField",
111
+                ],
112
+            ],
113
+            [
114
+                "name" => "password",
115
+                "fieldLabel" => _('Password'),
116
+                "editor" => [
117
+                    "ref" => "../../passwordField",
118
+                    "inputType" => "password",
119
+                ],
120
+            ],
121
+            [
122
+                "name" => "use_grommunio_credentials",
123
+                "fieldLabel" => _('Use grommunio credentials'),
124
+                "editor" => [
125
+                    "xtype" => "checkbox",
126
+                    "listeners" => [
127
+                        "check" => "Zarafa.plugins.files.data.Actions.onCheckCredentials", // this javascript function will be called!
128
+                    ],
129
+                ],
130
+            ],
131
+        ];
132
+
133
+        $this->metaConfig = [
134
+            "success" => true,
135
+            "metaData" => [
136
+                "fields" => $this->formFields,
137
+                "formConfig" => $this->formConfig,
138
+            ],
139
+            "data" => [ // here we can specify the default values.
140
+                "server_address" => $_SERVER['HTTP_HOST'],
141
+                "server_ssl" => true,
142
+                "server_port" => "443",
143
+                "server_path" => "/files/remote.php/webdav",
144
+                "use_grommunio_credentials" => true,
145
+            ],
146
+        ];
147
+    }
148
+
149
+    /**
150
+     * Opens the connection to the webdav server.
151
+     *
152
+     * @throws BackendException if connection is not successful
153
+     *
154
+     * @return bool true if action succeeded
155
+     */
156
+    public function open() {
157
+        // check if curl is available
158
+        $serverHasCurl = function_exists('curl_version');
159
+        if (!$serverHasCurl) {
160
+            $e = new BackendException($this->parseErrorCodeToMessage(self::WD_ERR_NO_CURL), 500);
161
+            $e->setTitle($this->backendTransName . _('php-curl is not available'));
162
+
163
+            throw $e;
164
+        }
165
+
166
+        $davsettings = [
167
+            'baseUri' => $this->webdavUrl(),
168
+            'userName' => $this->user,
169
+            'password' => $this->pass,
170
+            'authType' => \Sabre\DAV\Client::AUTH_BASIC,
171
+        ];
172
+
173
+        try {
174
+            $this->sabre_client = new FilesWebDavClient($davsettings);
175
+            $this->sabre_client->addCurlSetting(CURLOPT_SSL_VERIFYPEER, !$this->allowselfsigned);
176
+
177
+            $this->ocs_client = new ocsclient($this->getOwncloudBaseURL(), $this->user, $this->pass, $this->allowselfsigned);
178
+
179
+            return true;
180
+        }
181
+        catch (\Exception $e) {
182
+            $this->log('Failed to open: ' . $e->getMessage());
183
+            if (intval($e->getHTTPCode()) == 401) {
184
+                $e = new BackendException($this->parseErrorCodeToMessage(self::WD_ERR_UNAUTHORIZED), $e->getHTTPCode());
185
+                $e->setTitle($this->backendTransName . _('Access denied'));
186
+
187
+                throw $e;
188
+            }
189
+            $e = new BackendException($this->parseErrorCodeToMessage(self::WD_ERR_UNREACHABLE), $e->getHTTPCode());
190
+            $e->setTitle($this->backendTransName . _('Connection failed'));
191
+
192
+            throw $e;
193
+        }
194
+    }
195
+
196
+    /**
197
+     * /**
198
+     * Copy a collection on webdav server
199
+     * Duplicates a collection on the webdav server (serverside).
200
+     * All work is done on the webdav server. If you set param overwrite as true,
201
+     * the target will be overwritten.
202
+     *
203
+     * @param string $src_path  Source path
204
+     * @param string $dst_path  Destination path
205
+     * @param bool   $overwrite Overwrite if collection exists in $dst_path
206
+     * @param bool   $coll      set this to true if you want to copy a folder
207
+     *
208
+     * @throws BackendException if request is not successful
209
+     *
210
+     * @return bool true if action succeeded
211
+     */
212
+    private function copy($src_path, $dst_path, $overwrite, $coll) {
213
+        $time_start = microtime(true);
214
+        $src_path = $this->removeSlash($src_path);
215
+        $dst_path = $this->webdavUrl() . $this->removeSlash($dst_path);
216
+        $this->log("[COPY] start for dir: {$src_path} -> {$dst_path}");
217
+        if ($overwrite) {
218
+            $overwrite = 'T';
219
+        }
220
+        else {
221
+            $overwrite = 'F';
222
+        }
223
+
224
+        $settings = ["Destination" => $dst_path, 'Overwrite' => $overwrite];
225
+        if ($coll) {
226
+            $settings = ["Destination" => $dst_path, 'Depth' => 'Infinity'];
227
+        }
228
+
229
+        try {
230
+            $response = $this->sabre_client->request("COPY", $src_path, null, $settings);
231
+            $time_end = microtime(true);
232
+            $time = $time_end - $time_start;
233
+            $this->log("[COPY] done in {$time} seconds: " . $response['statusCode']);
234
+
235
+            return true;
236
+        }
237
+        catch (ClientException $e) {
238
+            $e = new BackendException($this->parseErrorCodeToMessage($e->getCode()), $e->getCode());
239
+            $e->setTitle($this->backendTransName . _('Sabre error'));
240
+
241
+            throw $e;
242
+        }
243
+        catch (Exception $e) {
244
+            $this->log('[COPY] fatal: ' . $e->getMessage());
245
+            $e = new BackendException($this->parseErrorCodeToMessage($e->getHTTPCode()), $e->getHTTPCode());
246
+            $e->setTitle($this->backendTransName . _('Copying failed'));
247
+
248
+            throw $e;
249
+        }
250
+    }
251
+
252
+    /**
253
+     * This function will return a user friendly error string.
254
+     *
255
+     * @param number $error_code A error code
256
+     *
257
+     * @return string userfriendly error message
258
+     */
259
+    private function parseErrorCodeToMessage($error_code) {
260
+        $error = intval($error_code);
261
+
262
+        $msg = _('Unknown error');
263
+        $contactAdmin = _('Please contact your system administrator.');
264
+
265
+        switch ($error) {
266
+            case CURLE_BAD_PASSWORD_ENTERED:
267
+            case self::WD_ERR_UNAUTHORIZED:
268
+                $msg = _('Unauthorized. Wrong username or password.');
269
+                break;
270
+
271
+            case CURLE_SSL_CONNECT_ERROR:
272
+            case CURLE_COULDNT_RESOLVE_HOST:
273
+            case CURLE_COULDNT_CONNECT:
274
+            case CURLE_OPERATION_TIMEOUTED:
275
+            case self::WD_ERR_UNREACHABLE:
276
+                $msg = _('File server is not reachable. Please verify the file server URL.');
277
+                break;
278
+
279
+            case self::WD_ERR_FORBIDDEN:
280
+                $msg = _('You don\'t have enough permissions to view this file or folder.');
281
+                break;
282
+
283
+            case self::WD_ERR_NOTFOUND:
284
+                $msg = _('The file or folder is not available anymore.');
285
+                break;
286
+
287
+            case self::WD_ERR_TIMEOUT:
288
+                $msg = _('Connection to the file server timed out. Please check again later.');
289
+                break;
290
+
291
+            case self::WD_ERR_LOCKED:
292
+                $msg = _('This file is locked by another user. Please try again later.');
293
+                break;
294
+
295
+            case self::WD_ERR_FAILED_DEPENDENCY:
296
+                $msg = _('The request failed.') . ' ' . $contactAdmin;
297
+                break;
298
+
299
+            case self::WD_ERR_INTERNAL:
300
+                // This is a general error, might be thrown due to a wrong IP, but we don't know.
301
+                $msg = _('The file server encountered an internal problem.') . ' ' . $contactAdmin;
302
+                break;
303
+
304
+            case self::WD_ERR_TMP:
305
+                $msg = _('We could not write to temporary directory.') . ' ' . $contactAdmin;
306
+                break;
307
+
308
+            case self::WD_ERR_FEATURES:
309
+                $msg = _('We could not retrieve list of server features.') . ' ' . $contactAdmin;
310
+                break;
311
+
312
+            case self::WD_ERR_NO_CURL:
313
+                $msg = _('PHP-Curl is not available.') . ' ' . $contactAdmin;
314
+                break;
315
+        }
316
+
317
+        return $msg;
318
+    }
319
+
320
+    /**
321
+     * a simple php error_log wrapper.
322
+     *
323
+     * @param string $err_string error message
324
+     */
325
+    private function log($err_string) {
326
+        if ($this->debug) {
327
+            error_log("[BACKEND_OWNCLOUD]: " . $err_string);
328
+        }
329
+    }
330
+
331
+    /**
332
+     * Get the base URL of Owncloud.
333
+     * For example: http://demo.owncloud.com/owncloud.
334
+     *
335
+     * @return string
336
+     */
337
+    private function getOwncloudBaseURL() {
338
+        $webdavurl = $this->webdavUrl();
339
+
340
+        return substr($webdavurl, 0, strlen($webdavurl) - strlen("/remote.php/webdav/"));
341
+    }
342
+
343
+    /**
344
+     * ============================ FEATURE FUNCTIONS ========================.
345
+     */
346
+
347
+    /**
348
+     * Return the version string of the server backend.
349
+     *
350
+     * @return string
351
+     */
352
+    public function getServerVersion() {
353
+        // check if curl is available
354
+        $serverHasCurl = function_exists('curl_version');
355
+        if (!$serverHasCurl) {
356
+            throw new BackendException($this->parseErrorCodeToMessage(self::WD_ERR_NO_CURL), 500);
357
+        }
358
+
359
+        $url = $this->getOwncloudBaseURL() . "/status.php";
360
+
361
+        // try to get the contents of the owncloud status page
362
+        $ch = curl_init();
363
+        curl_setopt($ch, CURLOPT_AUTOREFERER, true);
364
+        curl_setopt($ch, CURLOPT_TIMEOUT, 3); // timeout of 3 seconds
365
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
366
+        curl_setopt($ch, CURLOPT_URL, $url);
367
+        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
368
+        if ($this->allowselfsigned) {
369
+            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
370
+            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
371
+        }
372
+        $versiondata = curl_exec($ch);
373
+        $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
374
+        curl_close($ch);
375
+
376
+        if ($httpcode && $httpcode == "200" && $versiondata) {
377
+            $versions = json_decode($versiondata);
378
+            $version = $versions->versionstring;
379
+        }
380
+        else {
381
+            $version = "Undetected (no ownCloud?)";
382
+        }
383
+
384
+        return $version;
385
+    }
386
+
387
+    /**
388
+     * Get all shares in the specified folder.
389
+     *
390
+     * The response array will look like:
391
+     *
392
+     * array(
393
+     *  path1 => array(
394
+     *      id1 => details1,
395
+     *      id2 => details2
396
+     *  ),
397
+     *  path2 => array(
398
+     *      id1 => ....
399
+     *  )
400
+     * )
401
+     *
402
+     * @param $path
403
+     *
404
+     * @return array
405
+     */
406
+    public function getShares($path) {
407
+        $result = [];
408
+
409
+        $this->log('[GETSHARES]: loading shares for folder: ' . $path);
410
+
411
+        try {
412
+            $this->ocs_client->loadShares($path);
413
+        }
414
+        catch (ConnectionException $e) {
415
+            $this->log('[GETSHARES]: connection exception while loading shares: ' . $e->getMessage() . " " . $e->getCode());
416
+        }
417
+        $shares = $this->ocs_client->getAllShares();
418
+
419
+        $this->log('[GETSHARES]: found ' . count($shares) . ' shares for folder: ' . $path);
420
+
421
+        $result[$path] = [];
422
+        if ($shares !== false) {
423
+            foreach ($shares as $id => $options) {
424
+                $result[$path][$id] = [
425
+                    "shared" => true,
426
+                    "id" => $options->getId(),
427
+                    "path" => $options->getPath(),
428
+                    "shareType" => $options->getShareType(),
429
+                    "permissions" => $options->getPermissions(),
430
+                    "expiration" => $options->getExpiration(),
431
+                    "token" => $options->getToken(),
432
+                    "url" => $options->getUrl(),
433
+                    "shareWith" => $options->getShareWith(),
434
+                    "shareWithDisplayname" => $options->getShareWithDisplayname(),
435
+                ];
436
+            }
437
+        }
438
+
439
+        return $result;
440
+    }
441
+
442
+    /**
443
+     * Get details about the shared files/folders.
444
+     *
445
+     * The response array will look like:
446
+     *
447
+     * array(
448
+     *  path1 => array(
449
+     *      id1 => details1,
450
+     *      id2 => details2
451
+     *  ),
452
+     *  path2 => array(
453
+     *      id1 => ....
454
+     *  )
455
+     * )
456
+     *
457
+     * @param $patharray Simple array with path's to files or folders
458
+     *
459
+     * @return array
460
+     */
461
+    public function sharingDetails($patharray) {
462
+        $result = [];
463
+
464
+        // performance optimization
465
+        // fetch all shares - so we only need one request
466
+        if (count($patharray) > 1) {
467
+            try {
468
+                $this->ocs_client->loadShares();
469
+            }
470
+            catch (ConnectionException $e) {
471
+                $this->log('[SHARINGDETAILS]: connection exception while loading shares: ' . $e->getMessage() . " " . $e->getCode());
472
+            }
473
+
474
+            /** @var ocsshare[] $shares */
475
+            $shares = $this->ocs_client->getAllShares();
476
+            foreach ($patharray as $path) {
477
+                $result[$path] = [];
478
+                foreach ($shares as $id => $details) {
479
+                    if ($details->getPath() == $path) {
480
+                        $result[$path][$id] = [
481
+                            "shared" => true,
482
+                            "id" => $details->getId(),
483
+                            "shareType" => $details->getShareType(),
484
+                            "permissions" => $details->getPermissions(),
485
+                            "expiration" => $details->getExpiration(),
486
+                            "token" => $details->getToken(),
487
+                            "url" => $details->getUrl(),
488
+                            "shareWith" => $details->getShareWith(),
489
+                            "shareWithDisplayname" => $details->getShareWithDisplayname(),
490
+                        ];
491
+                    }
492
+                }
493
+            }
494
+        }
495
+        else {
496
+            if (count($patharray) == 1) {
497
+                try {
498
+                    $shares = $this->ocs_client->loadShareByPath($patharray[0]);
499
+                }
500
+                catch (FileNotFoundException $e) {
501
+                    $shares = false;
502
+                }
503
+
504
+                $result[$patharray[0]] = [];
505
+
506
+                if ($shares !== false) {
507
+                    foreach ($shares as $id => $share) {
508
+                        $result[$patharray[0]][$id] = [
509
+                            "shared" => true,
510
+                            "id" => $share->getId(),
511
+                            "shareType" => $share->getShareType(),
512
+                            "permissions" => $share->getPermissions(),
513
+                            "expiration" => $share->getExpiration(),
514
+                            "token" => $share->getToken(),
515
+                            "url" => $share->getUrl(),
516
+                            "shareWith" => $share->getShareWith(),
517
+                            "shareWithDisplayname" => $share->getShareWithDisplayName(),
518
+                        ];
519
+                    }
520
+                }
521
+            }
522
+            else {
523
+                return false; // $patharray was empty...
524
+            }
525
+        }
526
+
527
+        return $result;
528
+    }
529
+
530
+    /**
531
+     * Share one or multiple files.
532
+     * As the sharing dialog might differ for different backends, it is implemented as
533
+     * MetaForm - meaning that the argumentnames/count might differ.
534
+     * That's the cause why this function uses an array as parameter.
535
+     *
536
+     * $shareparams should look somehow like this:
537
+     *
538
+     * array(
539
+     *      "path1" => options1,
540
+     *      "path2" => options2
541
+     *
542
+     *      or
543
+     *
544
+     *      "id1" => options1 (ONLY if $update = true)
545
+     * )
546
+     *
547
+     * @param $shareparams
548
+     * @param bool $update
549
+     *
550
+     * @return bool
551
+     */
552
+    public function share($shareparams, $update = false) {
553
+        $result = [];
554
+        if (count($shareparams) > 0) {
555
+            /** @var string $path */
556
+            foreach ($shareparams as $path => $options) {
557
+                $path = rtrim($path, "/");
558
+                $this->log('path: ' . $path);
559
+                if (!$update) {
560
+                    $share = $this->ocs_client->createShare($path, $options);
561
+                    $result[$path] = [
562
+                        "shared" => true,
563
+                        "id" => $share->getId(),
564
+                        "token" => $share->getToken(),
565
+                        "url" => $share->getUrl(),
566
+                    ];
567
+                }
568
+                else {
569
+                    foreach ($options as $key => $value) {
570
+                        $this->ocs_client->updateShare($path, $key, $value);
571
+                    }
572
+                    $result[$path] = [
573
+                        "shared" => true,
574
+                        "id" => $path,
575
+                    ];
576
+                }
577
+            }
578
+        }
579
+        else {
580
+            $this->log('No share params given');
581
+
582
+            return false; // no shareparams...
583
+        }
584
+
585
+        return $result;
586
+    }
587
+
588
+    /**
589
+     * Disable sharing for the given files/folders.
590
+     *
591
+     * @param $idarray
592
+     *
593
+     * @throws \OCSAPI\Exception\ConnectionException
594
+     *
595
+     * @return bool
596
+     */
597
+    public function unshare($idarray) {
598
+        foreach ($idarray as $id) {
599
+            $this->ocs_client->deleteShare($id);
600
+        }
601
+
602
+        return true;
603
+    }
604
+
605
+    /*
606 606
 	 * Get Recipients that could be shared with, matching the search string
607 607
 	 *
608 608
 	 * @param $search Searchstring to use
609 609
 	 * @return The response from the osc client API
610 610
 	 */
611
-	public function getRecipients($search) {
612
-		return $this->ocs_client->getRecipients($search);
613
-	}
611
+    public function getRecipients($search) {
612
+        return $this->ocs_client->getRecipients($search);
613
+    }
614 614
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -136,7 +136,7 @@
 block discarded – undo
136 136
 				"fields" => $this->formFields,
137 137
 				"formConfig" => $this->formConfig,
138 138
 			],
139
-			"data" => [ // here we can specify the default values.
139
+			"data" => [// here we can specify the default values.
140 140
 				"server_address" => $_SERVER['HTTP_HOST'],
141 141
 				"server_ssl" => true,
142 142
 				"server_port" => "443",
Please login to merge, or discard this patch.
Braces   +12 added lines, -24 removed lines patch added patch discarded remove patch
@@ -177,8 +177,7 @@  discard block
 block discarded – undo
177 177
 			$this->ocs_client = new ocsclient($this->getOwncloudBaseURL(), $this->user, $this->pass, $this->allowselfsigned);
178 178
 
179 179
 			return true;
180
-		}
181
-		catch (\Exception $e) {
180
+		} catch (\Exception $e) {
182 181
 			$this->log('Failed to open: ' . $e->getMessage());
183 182
 			if (intval($e->getHTTPCode()) == 401) {
184 183
 				$e = new BackendException($this->parseErrorCodeToMessage(self::WD_ERR_UNAUTHORIZED), $e->getHTTPCode());
@@ -216,8 +215,7 @@  discard block
 block discarded – undo
216 215
 		$this->log("[COPY] start for dir: {$src_path} -> {$dst_path}");
217 216
 		if ($overwrite) {
218 217
 			$overwrite = 'T';
219
-		}
220
-		else {
218
+		} else {
221 219
 			$overwrite = 'F';
222 220
 		}
223 221
 
@@ -233,14 +231,12 @@  discard block
 block discarded – undo
233 231
 			$this->log("[COPY] done in {$time} seconds: " . $response['statusCode']);
234 232
 
235 233
 			return true;
236
-		}
237
-		catch (ClientException $e) {
234
+		} catch (ClientException $e) {
238 235
 			$e = new BackendException($this->parseErrorCodeToMessage($e->getCode()), $e->getCode());
239 236
 			$e->setTitle($this->backendTransName . _('Sabre error'));
240 237
 
241 238
 			throw $e;
242
-		}
243
-		catch (Exception $e) {
239
+		} catch (Exception $e) {
244 240
 			$this->log('[COPY] fatal: ' . $e->getMessage());
245 241
 			$e = new BackendException($this->parseErrorCodeToMessage($e->getHTTPCode()), $e->getHTTPCode());
246 242
 			$e->setTitle($this->backendTransName . _('Copying failed'));
@@ -376,8 +372,7 @@  discard block
 block discarded – undo
376 372
 		if ($httpcode && $httpcode == "200" && $versiondata) {
377 373
 			$versions = json_decode($versiondata);
378 374
 			$version = $versions->versionstring;
379
-		}
380
-		else {
375
+		} else {
381 376
 			$version = "Undetected (no ownCloud?)";
382 377
 		}
383 378
 
@@ -410,8 +405,7 @@  discard block
 block discarded – undo
410 405
 
411 406
 		try {
412 407
 			$this->ocs_client->loadShares($path);
413
-		}
414
-		catch (ConnectionException $e) {
408
+		} catch (ConnectionException $e) {
415 409
 			$this->log('[GETSHARES]: connection exception while loading shares: ' . $e->getMessage() . " " . $e->getCode());
416 410
 		}
417 411
 		$shares = $this->ocs_client->getAllShares();
@@ -466,8 +460,7 @@  discard block
 block discarded – undo
466 460
 		if (count($patharray) > 1) {
467 461
 			try {
468 462
 				$this->ocs_client->loadShares();
469
-			}
470
-			catch (ConnectionException $e) {
463
+			} catch (ConnectionException $e) {
471 464
 				$this->log('[SHARINGDETAILS]: connection exception while loading shares: ' . $e->getMessage() . " " . $e->getCode());
472 465
 			}
473 466
 
@@ -491,13 +484,11 @@  discard block
 block discarded – undo
491 484
 					}
492 485
 				}
493 486
 			}
494
-		}
495
-		else {
487
+		} else {
496 488
 			if (count($patharray) == 1) {
497 489
 				try {
498 490
 					$shares = $this->ocs_client->loadShareByPath($patharray[0]);
499
-				}
500
-				catch (FileNotFoundException $e) {
491
+				} catch (FileNotFoundException $e) {
501 492
 					$shares = false;
502 493
 				}
503 494
 
@@ -518,8 +509,7 @@  discard block
 block discarded – undo
518 509
 						];
519 510
 					}
520 511
 				}
521
-			}
522
-			else {
512
+			} else {
523 513
 				return false; // $patharray was empty...
524 514
 			}
525 515
 		}
@@ -564,8 +554,7 @@  discard block
 block discarded – undo
564 554
 						"token" => $share->getToken(),
565 555
 						"url" => $share->getUrl(),
566 556
 					];
567
-				}
568
-				else {
557
+				} else {
569 558
 					foreach ($options as $key => $value) {
570 559
 						$this->ocs_client->updateShare($path, $key, $value);
571 560
 					}
@@ -575,8 +564,7 @@  discard block
 block discarded – undo
575 564
 					];
576 565
 				}
577 566
 			}
578
-		}
579
-		else {
567
+		} else {
580 568
 			$this->log('No share params given');
581 569
 
582 570
 			return false; // no shareparams...
Please login to merge, or discard this patch.
plugins/filesbackendOwncloud/php/lib/ocsapi/class.ocsclient.php 2 patches
Indentation   +611 added lines, -611 removed lines patch added patch discarded remove patch
@@ -30,615 +30,615 @@
 block discarded – undo
30 30
  * @class   ocsclient
31 31
  */
32 32
 class ocsclient {
33
-	/**
34
-	 * OCS Sharing API.
35
-	 */
36
-	public const OCS_PATH = "/ocs/v1.php/apps/files_sharing/api/v1";
37
-	public const OCS_TIMEOUT = 10;
38
-
39
-	/**
40
-	 * @var string Server base URL
41
-	 */
42
-	private $baseurl = "";
43
-
44
-	/**
45
-	 * @var string Username
46
-	 */
47
-	private $user = "";
48
-
49
-	/**
50
-	 * @var string Password
51
-	 */
52
-	private $pass = "";
53
-
54
-	/**
55
-	 * @var bool Allow self signed certs
56
-	 */
57
-	private $allowSelfSignedCerts = false;
58
-
59
-	/**
60
-	 * @var bool Defines if the store has been loaded
61
-	 */
62
-	private $loaded = false;
63
-
64
-	/**
65
-	 * @var ocsshare[] this will hold an array of ocsshares - index is the share ID
66
-	 */
67
-	private $shares;
68
-
69
-	/**
70
-	 * @var array default curl options used for all requests
71
-	 */
72
-	private $curlDefaultOptions = [
73
-		CURLOPT_AUTOREFERER => true,
74
-		CURLOPT_TIMEOUT => self::OCS_TIMEOUT,
75
-		CURLOPT_RETURNTRANSFER => 1,
76
-		CURLOPT_FOLLOWLOCATION => true,
77
-		CURLOPT_HTTPHEADER => ['OCS-APIREQUEST: true'],
78
-	];
79
-
80
-	/**
81
-	 * @var array curl SSL verify options for self signed certificates
82
-	 */
83
-	private $curlSSLVerifyOptions = [
84
-		CURLOPT_SSL_VERIFYHOST => 0,
85
-		CURLOPT_SSL_VERIFYPEER => 0,
86
-	];
87
-
88
-	/**
89
-	 * Constructor.
90
-	 *
91
-	 * @param $baseurl
92
-	 * @param $user
93
-	 * @param $pass
94
-	 * @param $allowSelfSignedCerts
95
-	 *
96
-	 * @throws ConnectionException
97
-	 */
98
-	public function __construct($baseurl, $user, $pass, $allowSelfSignedCerts = false) {
99
-		// check if curl is available
100
-		$serverHasCurl = function_exists('curl_version');
101
-		if (!$serverHasCurl) {
102
-			throw new ConnectionException("Curl not found!");
103
-		}
104
-
105
-		$this->baseurl = $baseurl;
106
-		$this->user = $user;
107
-		$this->pass = $pass;
108
-		$this->allowSelfSignedCerts = $allowSelfSignedCerts;
109
-
110
-		$this->shares = [];
111
-		$this->sharesByPath = [];
112
-	}
113
-
114
-	/**
115
-	 * Get the base URL for OCS.
116
-	 *
117
-	 * @return string
118
-	 */
119
-	private function getOCSUrl() {
120
-		return $this->baseurl . self::OCS_PATH . "/shares";
121
-	}
122
-
123
-	/**
124
-	 * Shortcut for curl get requests.
125
-	 *
126
-	 * @param $url string URL for the request
127
-	 *
128
-	 * @return curl response data
129
-	 */
130
-	private function doCurlGetRequest($url) {
131
-		return $this->doCurlRequest($url, []);
132
-	}
133
-
134
-	/**
135
-	 * Execute curl request with parameters.
136
-	 *
137
-	 * @param $url string URL for the request
138
-	 * @param mixed $curlOptions
139
-	 *
140
-	 * @throws ConnectionException
141
-	 * @throws InvalidResponseException
142
-	 *
143
-	 * @return curl responsedata
144
-	 */
145
-	private function doCurlRequest($url, $curlOptions) {
146
-		$ch = curl_init();
147
-
148
-		curl_setopt($ch, CURLOPT_URL, $url);
149
-		curl_setopt_array($ch, $this->curlDefaultOptions);
150
-		if ($this->allowSelfSignedCerts) {
151
-			curl_setopt_array($ch, $this->curlSSLVerifyOptions);
152
-		}
153
-		curl_setopt($ch, CURLOPT_USERPWD, $this->user . ":" . $this->pass);
154
-		if (!empty($curlOptions)) {
155
-			curl_setopt_array($ch, $curlOptions);
156
-		}
157
-
158
-		$responsedata = curl_exec($ch);
159
-		$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
160
-
161
-		if ($httpcode == 0) {
162
-			$message = curl_errno($ch);
163
-		}
164
-		else {
165
-			$message = $httpcode;
166
-		}
167
-		curl_close($ch);
168
-
169
-		if ($httpcode && $httpcode == "200") {
170
-			$this->loaded = true;
171
-
172
-			return $responsedata;
173
-		}
174
-		$this->loaded = false;
175
-		if ($httpcode == "0") {
176
-			throw new ConnectionException($message, $httpcode);
177
-		}
178
-
179
-		throw new ConnectionException($httpcode);
180
-	}
181
-
182
-	/**
183
-	 * Loads the shares for a specific folder.
184
-	 * If $path is empty all shares will loaded.
185
-	 *
186
-	 * @param string $path
187
-	 *
188
-	 * @throws ConnectionException
189
-	 * @throws InvalidResponseException
190
-	 */
191
-	public function loadShares($path = "") {
192
-		// reset all loaded shares first
193
-		$this->reset();
194
-
195
-		if (empty($path)) {
196
-			$url = $this->getOCSUrl();
197
-		}
198
-		else {
199
-			$url = $this->getOCSUrl() . "?path=" . urlencode($path) . "&subfiles=true";
200
-		}
201
-		$this->parseListingResponse($this->doCurlGetRequest($url));
202
-		$this->loaded = true;
203
-	}
204
-
205
-	/**
206
-	 * Loads only one specific share defined by ID.
207
-	 *
208
-	 * @param $id
209
-	 *
210
-	 * @throws ConnectionException
211
-	 * @throws InvalidResponseException
212
-	 *
213
-	 * @return ocsshare or FALSE
214
-	 */
215
-	public function loadShareByID($id) {
216
-		$url = $this->getOCSUrl() . "/" . $id;
217
-		$this->parseListingResponse($this->doCurlGetRequest($url));
218
-		$this->loaded = true;
219
-		if (isset($this->shares[$id])) {
220
-			return $this->shares[$id];
221
-		}
222
-
223
-		return false;
224
-	}
225
-
226
-	/**
227
-	 * Loads one or more shares defined by path.
228
-	 *
229
-	 * @param $path
230
-	 *
231
-	 * @throws ConnectionException
232
-	 * @throws InvalidResponseException
233
-	 *
234
-	 * @return ocsshare[] or FALSE
235
-	 */
236
-	public function loadShareByPath($path) {
237
-		$path = rtrim($path, "/");
238
-		$url = $this->getOCSUrl() . "?path=" . urlencode($path);
239
-		$this->parseListingResponse($this->doCurlGetRequest($url));
240
-		$this->loaded = true;
241
-		$shares = [];
242
-		foreach ($this->shares as $id => $details) {
243
-			if ($details->getPath() == $path) {
244
-				$shares[$id] = $details;
245
-			}
246
-		}
247
-		if (count($shares) > 0) {
248
-			return $shares;
249
-		}
250
-
251
-		return false;
252
-	}
253
-
254
-	/**
255
-	 * Gets all groups and users we can share with.
256
-	 *
257
-	 * @param mixed $search
258
-	 *
259
-	 * @throws ConnectionException
260
-	 * @throws InvalidResponseException
261
-	 *
262
-	 * @return [] or FALSE
263
-	 */
264
-	public function getRecipients($search) {
265
-		$url = $this->baseurl . self::OCS_PATH . "/sharees?itemType=file&search=" . urlencode($search);
266
-
267
-		$ch = curl_init();
268
-		curl_setopt($ch, CURLOPT_URL, $url);
269
-		curl_setopt_array($ch, $this->curlDefaultOptions);
270
-		if ($this->allowSelfSignedCerts) {
271
-			curl_setopt_array($ch, $this->curlSSLVerifyOptions);
272
-		}
273
-		curl_setopt($ch, CURLOPT_USERPWD, $this->user . ":" . $this->pass);
274
-		$responsedata = curl_exec($ch);
275
-		$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
276
-		curl_close($ch);
277
-
278
-		if ($httpcode === 200) {
279
-			try {
280
-				$xmldata = new \SimpleXMLElement($responsedata);
281
-			}
282
-			catch (\Exception $e) {
283
-				throw new InvalidResponseException($responsedata);
284
-			}
285
-
286
-			if (!$xmldata || !isset($xmldata->meta) || !$this->parseResponseMeta($xmldata->meta) || !isset($xmldata->data)) {
287
-				return false;
288
-			}
289
-
290
-			return $this->parseRecipientData($xmldata->data);
291
-		}
292
-
293
-		throw new ConnectionException($httpcode);
294
-	}
295
-
296
-	/**
297
-	 * Get all loaded shares. Will return FALSE if the store is not loaded yet.
298
-	 *
299
-	 * @return ocsshare or FALSE
300
-	 */
301
-	public function getAllShares() {
302
-		if (!$this->loaded) {
303
-			return false;
304
-		}
305
-
306
-		return $this->shares;
307
-	}
308
-
309
-	/**
310
-	 * Returns one ocsshare specified by ID. Or FALSE if the ID was not found or store is not loaded yet.
311
-	 *
312
-	 * @param $id
313
-	 *
314
-	 * @return ocsshare or bool
315
-	 */
316
-	public function getShareByID($id) {
317
-		if (!$this->loaded) {
318
-			return false;
319
-		}
320
-
321
-		if (isset($this->shares[$id])) {
322
-			return $this->shares[$id];
323
-		}
324
-
325
-		return false;
326
-	}
327
-
328
-	/**
329
-	 * Returns one or many ocsshare specified by Path. Or FALSE if path was not found or store is not loaded yet.
330
-	 *
331
-	 * @param $path
332
-	 *
333
-	 * @return ocsshare[] or bool
334
-	 */
335
-	public function getShareByPath($path) {
336
-		if (!$this->loaded) {
337
-			return false;
338
-		}
339
-
340
-		$shares = [];
341
-
342
-		foreach ($this->shares as $id => $details) {
343
-			if ($details->getPath() == $path) {
344
-				$shares[$id] = $details;
345
-			}
346
-		}
347
-
348
-		if (count($shares) > 0) {
349
-			return $shares;
350
-		}
351
-
352
-		return false;
353
-	}
354
-
355
-	/**
356
-	 * Create a new share on the server.
357
-	 * Optionnames in $options should match Owncloud option names.
358
-	 * See: https://doc.owncloud.org/server/8.0/developer_manual/core/ocs-share-api.html.
359
-	 *
360
-	 * Options has to include shareType (int),  ‘0’ = user; ‘1’ = group; ‘3’ = public link;
361
-	 * and shareWith for shareType 0 or 1.
362
-	 *
363
-	 * @param $path
364
-	 * @param $options
365
-	 *
366
-	 * @throws ConnectionException
367
-	 * @throws InvalidResponseException
368
-	 *
369
-	 * @return ocsshare
370
-	 */
371
-	public function createShare($path, $options) {
372
-		$url = $this->getOCSUrl();
373
-
374
-		// post variables
375
-		$fields = [
376
-			'path' => urlencode($path),
377
-		];
378
-
379
-		foreach ($options as $key => $value) {
380
-			$fields[$key] = urlencode($value);
381
-		}
382
-		// url-ify the data for the POST
383
-		$fields_string = "";
384
-		foreach ($fields as $key => $value) {
385
-			$fields_string .= $key . '=' . $value . '&';
386
-		}
387
-		rtrim($fields_string, '&');
388
-		$curlExtraOptions = [
389
-			CURLOPT_POST => 1,
390
-			CURLOPT_POSTFIELDS => $fields_string,
391
-		];
392
-
393
-		return $this->parseModificationResponse($this->doCurlRequest($url, $curlExtraOptions));
394
-	}
395
-
396
-	/**
397
-	 * Update one value of the given share. ATTENTION: updating the password will change the share id.
398
-	 *
399
-	 * @param $id
400
-	 * @param $key
401
-	 * @param $value
402
-	 *
403
-	 * @throws ConnectionException
404
-	 * @throws InvalidResponseException
405
-	 *
406
-	 * @return ocsshare Returns a empty share
407
-	 */
408
-	public function updateShare($id, $key, $value) {
409
-		$url = $this->getOCSUrl() . "/" . $id;
410
-
411
-		// post variables
412
-		$fields_string = $key . '=' . urlencode($value);
413
-		$curlExtraOptions = [
414
-			CURLOPT_CUSTOMREQUEST => "PUT",
415
-			CURLOPT_POSTFIELDS => $fields_string,
416
-		];
417
-
418
-		return $this->parseModificationResponse($this->doCurlRequest($url, $curlExtraOptions));
419
-	}
420
-
421
-	/**
422
-	 * Clear all loaded shares.
423
-	 */
424
-	public function reset() {
425
-		unset($this->sharesByPath);
426
-		$this->sharesByPath = [];
427
-
428
-		unset($this->shares);
429
-		$this->shares = [];
430
-	}
431
-
432
-	/**
433
-	 * Delete the given share.
434
-	 *
435
-	 * @param $id
436
-	 *
437
-	 * @throws ConnectionException
438
-	 * @throws InvalidResponseException
439
-	 *
440
-	 * @return ocsshare Returns a empty share
441
-	 */
442
-	public function deleteShare($id) {
443
-		$url = $this->getOCSUrl() . "/" . $id;
444
-
445
-		$curlExtraOptions = [
446
-			CURLOPT_CUSTOMREQUEST => "DELETE",
447
-		];
448
-
449
-		return $this->parseModificationResponse($this->doCurlRequest($url, $curlExtraOptions));
450
-	}
451
-
452
-	/**
453
-	 * Parse the response of a create or modify request.
454
-	 *
455
-	 * @param $response
456
-	 *
457
-	 * @throws FileNotFoundException
458
-	 * @throws InvalidArgumentException
459
-	 * @throws InvalidRequestException
460
-	 * @throws InvalidResponseException
461
-	 * @throws PermissionDeniedException
462
-	 *
463
-	 * @return ocsshare
464
-	 */
465
-	private function parseModificationResponse($response) {
466
-		if ($response) {
467
-			try {
468
-				$xmldata = new \SimpleXMLElement($response);
469
-			}
470
-			catch (\Exception $e) {
471
-				throw new InvalidResponseException($response);
472
-			}
473
-
474
-			if ($xmldata) {
475
-				$ok = false;
476
-				if (isset($xmldata->meta)) {
477
-					$ok = $this->parseResponseMeta($xmldata->meta);
478
-				}
479
-
480
-				if ($ok) {
481
-					// create a new ocsshare
482
-					if (isset($xmldata->data)) {
483
-						return new ocsshare($xmldata->data);
484
-					}
485
-
486
-					return false;
487
-				}
488
-			}
489
-		}
490
-		else {
491
-			throw new InvalidResponseException($response);
492
-		}
493
-
494
-		return false;
495
-	}
496
-
497
-	/**
498
-	 * Parse the request response.
499
-	 *
500
-	 * @param $response
501
-	 *
502
-	 * @throws FileNotFoundException
503
-	 * @throws InvalidArgumentException
504
-	 * @throws InvalidRequestException
505
-	 * @throws InvalidResponseException
506
-	 * @throws PermissionDeniedException
507
-	 */
508
-	private function parseListingResponse($response) {
509
-		if (!$response) {
510
-			throw new InvalidResponseException($response);
511
-		}
512
-
513
-		try {
514
-			$xmldata = new \SimpleXMLElement($response);
515
-		}
516
-		catch (\Exception $e) {
517
-			throw new InvalidResponseException($response);
518
-		}
519
-
520
-		if ($xmldata) {
521
-			$ok = false;
522
-			if (isset($xmldata->meta)) {
523
-				$ok = $this->parseResponseMeta($xmldata->meta);
524
-			}
525
-
526
-			if ($ok) {
527
-				if (isset($xmldata->data)) {
528
-					$this->parseResponseData($xmldata->data);
529
-				}
530
-			}
531
-		}
532
-	}
533
-
534
-	/**
535
-	 * Parse the response meta block and its error codes.
536
-	 *
537
-	 * @param $response
538
-	 *
539
-	 * @throws FileNotFoundException
540
-	 * @throws InvalidArgumentException
541
-	 * @throws InvalidRequestException
542
-	 * @throws InvalidResponseException
543
-	 * @throws PermissionDeniedException
544
-	 *
545
-	 * @return bool
546
-	 */
547
-	private function parseResponseMeta($response) {
548
-		if ($response) {
549
-			$statuscode = intval($response->statuscode);
550
-			$message = $response->message;
551
-
552
-			// check status code - it must be 100, otherwise it failed
553
-			if ($statuscode == 100) {
554
-				return true;
555
-			}
556
-
557
-			switch ($statuscode) {
558
-				case 400:
559
-					throw new InvalidArgumentException($message);
560
-					break;
561
-
562
-				case 403:
563
-					throw new PermissionDeniedException($message);
564
-					break;
565
-
566
-				case 404:
567
-					throw new FileNotFoundException($message);
568
-					break;
569
-
570
-				case 999:
571
-					throw new InvalidRequestException($message);
572
-					break;
573
-
574
-				default:
575
-					throw new InvalidResponseException($message);
576
-			}
577
-		}
578
-		else {
579
-			throw new InvalidResponseException("Response contains no meta block.");
580
-		}
581
-	}
582
-
583
-	/**
584
-	 * Parse the response data block.
585
-	 *
586
-	 * @param SimpleXMLElement $response from owncloud server
587
-	 */
588
-	private function parseResponseData($response) {
589
-		// parse each element in the data section
590
-		foreach ($response->element as $element) {
591
-			$parsedShare = new ocsshare($element);
592
-			$parsedShare->generateShareURL($this->baseurl);
593
-
594
-			$this->shares[$parsedShare->getId()] = $parsedShare;
595
-		}
596
-	}
597
-
598
-	/**
599
-	 * Parse the response data for recipients.
600
-	 * Converts the xml response data to an array,
601
-	 *  [[label, shareWith, shareType], ...]
602
-	 * where
603
-	 *  - label is the display name
604
-	 *  - shareWith is the user or group id
605
-	 *  - shareType is type of the recipient: user or group.
606
-	 *
607
-	 * @param SimpleXMLElement $response the response data from the request
608
-	 *
609
-	 * @return array array of recipients
610
-	 */
611
-	private function parseRecipientData($response) {
612
-		$result = [];
613
-		foreach ($response->exact->users->element as $user) {
614
-			$result[] = [
615
-				$user->label->__toString(),
616
-				$user->value->shareWith->__toString(),
617
-				$user->value->shareType->__toString(),
618
-			];
619
-		}
620
-		foreach ($response->users->element as $user) {
621
-			$result[] = [
622
-				$user->label->__toString(),
623
-				$user->value->shareWith->__toString(),
624
-				$user->value->shareType->__toString(),
625
-			];
626
-		}
627
-		foreach ($response->exact->groups->element as $group) {
628
-			$result[] = [
629
-				$group->label->__toString(),
630
-				$group->value->shareWith->__toString(),
631
-				$group->value->shareType->__toString(),
632
-			];
633
-		}
634
-		foreach ($response->groups->element as $group) {
635
-			$result[] = [
636
-				$group->label->__toString(),
637
-				$group->value->shareWith->__toString(),
638
-				$group->value->shareType->__toString(),
639
-			];
640
-		}
641
-
642
-		return $result;
643
-	}
33
+    /**
34
+     * OCS Sharing API.
35
+     */
36
+    public const OCS_PATH = "/ocs/v1.php/apps/files_sharing/api/v1";
37
+    public const OCS_TIMEOUT = 10;
38
+
39
+    /**
40
+     * @var string Server base URL
41
+     */
42
+    private $baseurl = "";
43
+
44
+    /**
45
+     * @var string Username
46
+     */
47
+    private $user = "";
48
+
49
+    /**
50
+     * @var string Password
51
+     */
52
+    private $pass = "";
53
+
54
+    /**
55
+     * @var bool Allow self signed certs
56
+     */
57
+    private $allowSelfSignedCerts = false;
58
+
59
+    /**
60
+     * @var bool Defines if the store has been loaded
61
+     */
62
+    private $loaded = false;
63
+
64
+    /**
65
+     * @var ocsshare[] this will hold an array of ocsshares - index is the share ID
66
+     */
67
+    private $shares;
68
+
69
+    /**
70
+     * @var array default curl options used for all requests
71
+     */
72
+    private $curlDefaultOptions = [
73
+        CURLOPT_AUTOREFERER => true,
74
+        CURLOPT_TIMEOUT => self::OCS_TIMEOUT,
75
+        CURLOPT_RETURNTRANSFER => 1,
76
+        CURLOPT_FOLLOWLOCATION => true,
77
+        CURLOPT_HTTPHEADER => ['OCS-APIREQUEST: true'],
78
+    ];
79
+
80
+    /**
81
+     * @var array curl SSL verify options for self signed certificates
82
+     */
83
+    private $curlSSLVerifyOptions = [
84
+        CURLOPT_SSL_VERIFYHOST => 0,
85
+        CURLOPT_SSL_VERIFYPEER => 0,
86
+    ];
87
+
88
+    /**
89
+     * Constructor.
90
+     *
91
+     * @param $baseurl
92
+     * @param $user
93
+     * @param $pass
94
+     * @param $allowSelfSignedCerts
95
+     *
96
+     * @throws ConnectionException
97
+     */
98
+    public function __construct($baseurl, $user, $pass, $allowSelfSignedCerts = false) {
99
+        // check if curl is available
100
+        $serverHasCurl = function_exists('curl_version');
101
+        if (!$serverHasCurl) {
102
+            throw new ConnectionException("Curl not found!");
103
+        }
104
+
105
+        $this->baseurl = $baseurl;
106
+        $this->user = $user;
107
+        $this->pass = $pass;
108
+        $this->allowSelfSignedCerts = $allowSelfSignedCerts;
109
+
110
+        $this->shares = [];
111
+        $this->sharesByPath = [];
112
+    }
113
+
114
+    /**
115
+     * Get the base URL for OCS.
116
+     *
117
+     * @return string
118
+     */
119
+    private function getOCSUrl() {
120
+        return $this->baseurl . self::OCS_PATH . "/shares";
121
+    }
122
+
123
+    /**
124
+     * Shortcut for curl get requests.
125
+     *
126
+     * @param $url string URL for the request
127
+     *
128
+     * @return curl response data
129
+     */
130
+    private function doCurlGetRequest($url) {
131
+        return $this->doCurlRequest($url, []);
132
+    }
133
+
134
+    /**
135
+     * Execute curl request with parameters.
136
+     *
137
+     * @param $url string URL for the request
138
+     * @param mixed $curlOptions
139
+     *
140
+     * @throws ConnectionException
141
+     * @throws InvalidResponseException
142
+     *
143
+     * @return curl responsedata
144
+     */
145
+    private function doCurlRequest($url, $curlOptions) {
146
+        $ch = curl_init();
147
+
148
+        curl_setopt($ch, CURLOPT_URL, $url);
149
+        curl_setopt_array($ch, $this->curlDefaultOptions);
150
+        if ($this->allowSelfSignedCerts) {
151
+            curl_setopt_array($ch, $this->curlSSLVerifyOptions);
152
+        }
153
+        curl_setopt($ch, CURLOPT_USERPWD, $this->user . ":" . $this->pass);
154
+        if (!empty($curlOptions)) {
155
+            curl_setopt_array($ch, $curlOptions);
156
+        }
157
+
158
+        $responsedata = curl_exec($ch);
159
+        $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
160
+
161
+        if ($httpcode == 0) {
162
+            $message = curl_errno($ch);
163
+        }
164
+        else {
165
+            $message = $httpcode;
166
+        }
167
+        curl_close($ch);
168
+
169
+        if ($httpcode && $httpcode == "200") {
170
+            $this->loaded = true;
171
+
172
+            return $responsedata;
173
+        }
174
+        $this->loaded = false;
175
+        if ($httpcode == "0") {
176
+            throw new ConnectionException($message, $httpcode);
177
+        }
178
+
179
+        throw new ConnectionException($httpcode);
180
+    }
181
+
182
+    /**
183
+     * Loads the shares for a specific folder.
184
+     * If $path is empty all shares will loaded.
185
+     *
186
+     * @param string $path
187
+     *
188
+     * @throws ConnectionException
189
+     * @throws InvalidResponseException
190
+     */
191
+    public function loadShares($path = "") {
192
+        // reset all loaded shares first
193
+        $this->reset();
194
+
195
+        if (empty($path)) {
196
+            $url = $this->getOCSUrl();
197
+        }
198
+        else {
199
+            $url = $this->getOCSUrl() . "?path=" . urlencode($path) . "&subfiles=true";
200
+        }
201
+        $this->parseListingResponse($this->doCurlGetRequest($url));
202
+        $this->loaded = true;
203
+    }
204
+
205
+    /**
206
+     * Loads only one specific share defined by ID.
207
+     *
208
+     * @param $id
209
+     *
210
+     * @throws ConnectionException
211
+     * @throws InvalidResponseException
212
+     *
213
+     * @return ocsshare or FALSE
214
+     */
215
+    public function loadShareByID($id) {
216
+        $url = $this->getOCSUrl() . "/" . $id;
217
+        $this->parseListingResponse($this->doCurlGetRequest($url));
218
+        $this->loaded = true;
219
+        if (isset($this->shares[$id])) {
220
+            return $this->shares[$id];
221
+        }
222
+
223
+        return false;
224
+    }
225
+
226
+    /**
227
+     * Loads one or more shares defined by path.
228
+     *
229
+     * @param $path
230
+     *
231
+     * @throws ConnectionException
232
+     * @throws InvalidResponseException
233
+     *
234
+     * @return ocsshare[] or FALSE
235
+     */
236
+    public function loadShareByPath($path) {
237
+        $path = rtrim($path, "/");
238
+        $url = $this->getOCSUrl() . "?path=" . urlencode($path);
239
+        $this->parseListingResponse($this->doCurlGetRequest($url));
240
+        $this->loaded = true;
241
+        $shares = [];
242
+        foreach ($this->shares as $id => $details) {
243
+            if ($details->getPath() == $path) {
244
+                $shares[$id] = $details;
245
+            }
246
+        }
247
+        if (count($shares) > 0) {
248
+            return $shares;
249
+        }
250
+
251
+        return false;
252
+    }
253
+
254
+    /**
255
+     * Gets all groups and users we can share with.
256
+     *
257
+     * @param mixed $search
258
+     *
259
+     * @throws ConnectionException
260
+     * @throws InvalidResponseException
261
+     *
262
+     * @return [] or FALSE
263
+     */
264
+    public function getRecipients($search) {
265
+        $url = $this->baseurl . self::OCS_PATH . "/sharees?itemType=file&search=" . urlencode($search);
266
+
267
+        $ch = curl_init();
268
+        curl_setopt($ch, CURLOPT_URL, $url);
269
+        curl_setopt_array($ch, $this->curlDefaultOptions);
270
+        if ($this->allowSelfSignedCerts) {
271
+            curl_setopt_array($ch, $this->curlSSLVerifyOptions);
272
+        }
273
+        curl_setopt($ch, CURLOPT_USERPWD, $this->user . ":" . $this->pass);
274
+        $responsedata = curl_exec($ch);
275
+        $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
276
+        curl_close($ch);
277
+
278
+        if ($httpcode === 200) {
279
+            try {
280
+                $xmldata = new \SimpleXMLElement($responsedata);
281
+            }
282
+            catch (\Exception $e) {
283
+                throw new InvalidResponseException($responsedata);
284
+            }
285
+
286
+            if (!$xmldata || !isset($xmldata->meta) || !$this->parseResponseMeta($xmldata->meta) || !isset($xmldata->data)) {
287
+                return false;
288
+            }
289
+
290
+            return $this->parseRecipientData($xmldata->data);
291
+        }
292
+
293
+        throw new ConnectionException($httpcode);
294
+    }
295
+
296
+    /**
297
+     * Get all loaded shares. Will return FALSE if the store is not loaded yet.
298
+     *
299
+     * @return ocsshare or FALSE
300
+     */
301
+    public function getAllShares() {
302
+        if (!$this->loaded) {
303
+            return false;
304
+        }
305
+
306
+        return $this->shares;
307
+    }
308
+
309
+    /**
310
+     * Returns one ocsshare specified by ID. Or FALSE if the ID was not found or store is not loaded yet.
311
+     *
312
+     * @param $id
313
+     *
314
+     * @return ocsshare or bool
315
+     */
316
+    public function getShareByID($id) {
317
+        if (!$this->loaded) {
318
+            return false;
319
+        }
320
+
321
+        if (isset($this->shares[$id])) {
322
+            return $this->shares[$id];
323
+        }
324
+
325
+        return false;
326
+    }
327
+
328
+    /**
329
+     * Returns one or many ocsshare specified by Path. Or FALSE if path was not found or store is not loaded yet.
330
+     *
331
+     * @param $path
332
+     *
333
+     * @return ocsshare[] or bool
334
+     */
335
+    public function getShareByPath($path) {
336
+        if (!$this->loaded) {
337
+            return false;
338
+        }
339
+
340
+        $shares = [];
341
+
342
+        foreach ($this->shares as $id => $details) {
343
+            if ($details->getPath() == $path) {
344
+                $shares[$id] = $details;
345
+            }
346
+        }
347
+
348
+        if (count($shares) > 0) {
349
+            return $shares;
350
+        }
351
+
352
+        return false;
353
+    }
354
+
355
+    /**
356
+     * Create a new share on the server.
357
+     * Optionnames in $options should match Owncloud option names.
358
+     * See: https://doc.owncloud.org/server/8.0/developer_manual/core/ocs-share-api.html.
359
+     *
360
+     * Options has to include shareType (int),  ‘0’ = user; ‘1’ = group; ‘3’ = public link;
361
+     * and shareWith for shareType 0 or 1.
362
+     *
363
+     * @param $path
364
+     * @param $options
365
+     *
366
+     * @throws ConnectionException
367
+     * @throws InvalidResponseException
368
+     *
369
+     * @return ocsshare
370
+     */
371
+    public function createShare($path, $options) {
372
+        $url = $this->getOCSUrl();
373
+
374
+        // post variables
375
+        $fields = [
376
+            'path' => urlencode($path),
377
+        ];
378
+
379
+        foreach ($options as $key => $value) {
380
+            $fields[$key] = urlencode($value);
381
+        }
382
+        // url-ify the data for the POST
383
+        $fields_string = "";
384
+        foreach ($fields as $key => $value) {
385
+            $fields_string .= $key . '=' . $value . '&';
386
+        }
387
+        rtrim($fields_string, '&');
388
+        $curlExtraOptions = [
389
+            CURLOPT_POST => 1,
390
+            CURLOPT_POSTFIELDS => $fields_string,
391
+        ];
392
+
393
+        return $this->parseModificationResponse($this->doCurlRequest($url, $curlExtraOptions));
394
+    }
395
+
396
+    /**
397
+     * Update one value of the given share. ATTENTION: updating the password will change the share id.
398
+     *
399
+     * @param $id
400
+     * @param $key
401
+     * @param $value
402
+     *
403
+     * @throws ConnectionException
404
+     * @throws InvalidResponseException
405
+     *
406
+     * @return ocsshare Returns a empty share
407
+     */
408
+    public function updateShare($id, $key, $value) {
409
+        $url = $this->getOCSUrl() . "/" . $id;
410
+
411
+        // post variables
412
+        $fields_string = $key . '=' . urlencode($value);
413
+        $curlExtraOptions = [
414
+            CURLOPT_CUSTOMREQUEST => "PUT",
415
+            CURLOPT_POSTFIELDS => $fields_string,
416
+        ];
417
+
418
+        return $this->parseModificationResponse($this->doCurlRequest($url, $curlExtraOptions));
419
+    }
420
+
421
+    /**
422
+     * Clear all loaded shares.
423
+     */
424
+    public function reset() {
425
+        unset($this->sharesByPath);
426
+        $this->sharesByPath = [];
427
+
428
+        unset($this->shares);
429
+        $this->shares = [];
430
+    }
431
+
432
+    /**
433
+     * Delete the given share.
434
+     *
435
+     * @param $id
436
+     *
437
+     * @throws ConnectionException
438
+     * @throws InvalidResponseException
439
+     *
440
+     * @return ocsshare Returns a empty share
441
+     */
442
+    public function deleteShare($id) {
443
+        $url = $this->getOCSUrl() . "/" . $id;
444
+
445
+        $curlExtraOptions = [
446
+            CURLOPT_CUSTOMREQUEST => "DELETE",
447
+        ];
448
+
449
+        return $this->parseModificationResponse($this->doCurlRequest($url, $curlExtraOptions));
450
+    }
451
+
452
+    /**
453
+     * Parse the response of a create or modify request.
454
+     *
455
+     * @param $response
456
+     *
457
+     * @throws FileNotFoundException
458
+     * @throws InvalidArgumentException
459
+     * @throws InvalidRequestException
460
+     * @throws InvalidResponseException
461
+     * @throws PermissionDeniedException
462
+     *
463
+     * @return ocsshare
464
+     */
465
+    private function parseModificationResponse($response) {
466
+        if ($response) {
467
+            try {
468
+                $xmldata = new \SimpleXMLElement($response);
469
+            }
470
+            catch (\Exception $e) {
471
+                throw new InvalidResponseException($response);
472
+            }
473
+
474
+            if ($xmldata) {
475
+                $ok = false;
476
+                if (isset($xmldata->meta)) {
477
+                    $ok = $this->parseResponseMeta($xmldata->meta);
478
+                }
479
+
480
+                if ($ok) {
481
+                    // create a new ocsshare
482
+                    if (isset($xmldata->data)) {
483
+                        return new ocsshare($xmldata->data);
484
+                    }
485
+
486
+                    return false;
487
+                }
488
+            }
489
+        }
490
+        else {
491
+            throw new InvalidResponseException($response);
492
+        }
493
+
494
+        return false;
495
+    }
496
+
497
+    /**
498
+     * Parse the request response.
499
+     *
500
+     * @param $response
501
+     *
502
+     * @throws FileNotFoundException
503
+     * @throws InvalidArgumentException
504
+     * @throws InvalidRequestException
505
+     * @throws InvalidResponseException
506
+     * @throws PermissionDeniedException
507
+     */
508
+    private function parseListingResponse($response) {
509
+        if (!$response) {
510
+            throw new InvalidResponseException($response);
511
+        }
512
+
513
+        try {
514
+            $xmldata = new \SimpleXMLElement($response);
515
+        }
516
+        catch (\Exception $e) {
517
+            throw new InvalidResponseException($response);
518
+        }
519
+
520
+        if ($xmldata) {
521
+            $ok = false;
522
+            if (isset($xmldata->meta)) {
523
+                $ok = $this->parseResponseMeta($xmldata->meta);
524
+            }
525
+
526
+            if ($ok) {
527
+                if (isset($xmldata->data)) {
528
+                    $this->parseResponseData($xmldata->data);
529
+                }
530
+            }
531
+        }
532
+    }
533
+
534
+    /**
535
+     * Parse the response meta block and its error codes.
536
+     *
537
+     * @param $response
538
+     *
539
+     * @throws FileNotFoundException
540
+     * @throws InvalidArgumentException
541
+     * @throws InvalidRequestException
542
+     * @throws InvalidResponseException
543
+     * @throws PermissionDeniedException
544
+     *
545
+     * @return bool
546
+     */
547
+    private function parseResponseMeta($response) {
548
+        if ($response) {
549
+            $statuscode = intval($response->statuscode);
550
+            $message = $response->message;
551
+
552
+            // check status code - it must be 100, otherwise it failed
553
+            if ($statuscode == 100) {
554
+                return true;
555
+            }
556
+
557
+            switch ($statuscode) {
558
+                case 400:
559
+                    throw new InvalidArgumentException($message);
560
+                    break;
561
+
562
+                case 403:
563
+                    throw new PermissionDeniedException($message);
564
+                    break;
565
+
566
+                case 404:
567
+                    throw new FileNotFoundException($message);
568
+                    break;
569
+
570
+                case 999:
571
+                    throw new InvalidRequestException($message);
572
+                    break;
573
+
574
+                default:
575
+                    throw new InvalidResponseException($message);
576
+            }
577
+        }
578
+        else {
579
+            throw new InvalidResponseException("Response contains no meta block.");
580
+        }
581
+    }
582
+
583
+    /**
584
+     * Parse the response data block.
585
+     *
586
+     * @param SimpleXMLElement $response from owncloud server
587
+     */
588
+    private function parseResponseData($response) {
589
+        // parse each element in the data section
590
+        foreach ($response->element as $element) {
591
+            $parsedShare = new ocsshare($element);
592
+            $parsedShare->generateShareURL($this->baseurl);
593
+
594
+            $this->shares[$parsedShare->getId()] = $parsedShare;
595
+        }
596
+    }
597
+
598
+    /**
599
+     * Parse the response data for recipients.
600
+     * Converts the xml response data to an array,
601
+     *  [[label, shareWith, shareType], ...]
602
+     * where
603
+     *  - label is the display name
604
+     *  - shareWith is the user or group id
605
+     *  - shareType is type of the recipient: user or group.
606
+     *
607
+     * @param SimpleXMLElement $response the response data from the request
608
+     *
609
+     * @return array array of recipients
610
+     */
611
+    private function parseRecipientData($response) {
612
+        $result = [];
613
+        foreach ($response->exact->users->element as $user) {
614
+            $result[] = [
615
+                $user->label->__toString(),
616
+                $user->value->shareWith->__toString(),
617
+                $user->value->shareType->__toString(),
618
+            ];
619
+        }
620
+        foreach ($response->users->element as $user) {
621
+            $result[] = [
622
+                $user->label->__toString(),
623
+                $user->value->shareWith->__toString(),
624
+                $user->value->shareType->__toString(),
625
+            ];
626
+        }
627
+        foreach ($response->exact->groups->element as $group) {
628
+            $result[] = [
629
+                $group->label->__toString(),
630
+                $group->value->shareWith->__toString(),
631
+                $group->value->shareType->__toString(),
632
+            ];
633
+        }
634
+        foreach ($response->groups->element as $group) {
635
+            $result[] = [
636
+                $group->label->__toString(),
637
+                $group->value->shareWith->__toString(),
638
+                $group->value->shareType->__toString(),
639
+            ];
640
+        }
641
+
642
+        return $result;
643
+    }
644 644
 }
Please login to merge, or discard this patch.
Braces   +7 added lines, -14 removed lines patch added patch discarded remove patch
@@ -160,8 +160,7 @@  discard block
 block discarded – undo
160 160
 
161 161
 		if ($httpcode == 0) {
162 162
 			$message = curl_errno($ch);
163
-		}
164
-		else {
163
+		} else {
165 164
 			$message = $httpcode;
166 165
 		}
167 166
 		curl_close($ch);
@@ -194,8 +193,7 @@  discard block
 block discarded – undo
194 193
 
195 194
 		if (empty($path)) {
196 195
 			$url = $this->getOCSUrl();
197
-		}
198
-		else {
196
+		} else {
199 197
 			$url = $this->getOCSUrl() . "?path=" . urlencode($path) . "&subfiles=true";
200 198
 		}
201 199
 		$this->parseListingResponse($this->doCurlGetRequest($url));
@@ -278,8 +276,7 @@  discard block
 block discarded – undo
278 276
 		if ($httpcode === 200) {
279 277
 			try {
280 278
 				$xmldata = new \SimpleXMLElement($responsedata);
281
-			}
282
-			catch (\Exception $e) {
279
+			} catch (\Exception $e) {
283 280
 				throw new InvalidResponseException($responsedata);
284 281
 			}
285 282
 
@@ -466,8 +463,7 @@  discard block
 block discarded – undo
466 463
 		if ($response) {
467 464
 			try {
468 465
 				$xmldata = new \SimpleXMLElement($response);
469
-			}
470
-			catch (\Exception $e) {
466
+			} catch (\Exception $e) {
471 467
 				throw new InvalidResponseException($response);
472 468
 			}
473 469
 
@@ -486,8 +482,7 @@  discard block
 block discarded – undo
486 482
 					return false;
487 483
 				}
488 484
 			}
489
-		}
490
-		else {
485
+		} else {
491 486
 			throw new InvalidResponseException($response);
492 487
 		}
493 488
 
@@ -512,8 +507,7 @@  discard block
 block discarded – undo
512 507
 
513 508
 		try {
514 509
 			$xmldata = new \SimpleXMLElement($response);
515
-		}
516
-		catch (\Exception $e) {
510
+		} catch (\Exception $e) {
517 511
 			throw new InvalidResponseException($response);
518 512
 		}
519 513
 
@@ -574,8 +568,7 @@  discard block
 block discarded – undo
574 568
 				default:
575 569
 					throw new InvalidResponseException($message);
576 570
 			}
577
-		}
578
-		else {
571
+		} else {
579 572
 			throw new InvalidResponseException("Response contains no meta block.");
580 573
 		}
581 574
 	}
Please login to merge, or discard this patch.